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 /* Used by cm_FollowMountPoint */
33 extern void afsi_log(char *pattern, ...);
37 * Case-folding array. This was constructed by inspecting of SMBtrace output.
38 * I do not know anything more about it.
40 unsigned char cm_foldUpper[256] = {
41 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
42 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
43 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
44 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
45 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
46 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
47 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
48 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
49 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
50 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
51 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
52 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
53 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
57 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
58 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
59 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
60 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
61 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
62 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
63 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
64 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
65 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
66 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
67 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
68 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
69 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
70 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
71 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
72 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
76 * Case-insensitive string comparison. We used to use stricmp, but it doesn't
77 * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
78 * upper-case u-umlaut).
80 int cm_stricmp(const char *str1, const char *str2)
92 c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
93 c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
101 /* characters that are legal in an 8.3 name */
103 * We used to have 1's for all characters from 128 to 254. But
104 * the NT client behaves better if we create an 8.3 name for any
105 * name that has a character with the high bit on, and if we
106 * delete those characters from 8.3 names. In particular, see
107 * Sybase defect 10859.
109 char cm_LegalChars[256] = {
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
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, 0, 0, 0, 1, 1,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
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,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
128 /* return true iff component is a valid 8.3 name */
129 int cm_Is8Dot3(char *namep)
136 * can't have a leading dot;
137 * special case for . and ..
139 if (namep[0] == '.') {
142 if (namep[1] == '.' && namep[2] == 0)
146 while (tc = *namep++) {
148 /* saw another dot */
149 if (sawDot) return 0; /* second dot */
154 if (cm_LegalChars[tc] == 0)
157 if (!sawDot && charCount > 8)
158 /* more than 8 chars in name */
160 if (sawDot && charCount > 3)
161 /* more than 3 chars in extension */
168 * Number unparsing map for generating 8.3 names;
169 * The version taken from DFS was on drugs.
170 * You can't include '&' and '@' in a file name.
172 char cm_8Dot3Mapping[42] =
173 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
174 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
175 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
176 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
178 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
180 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
184 int vnode = ntohl(dep->fid.vnode);
186 int validExtension = 0;
187 char tc, *temp, *name;
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(dep->name, '.');
202 temp = lastDot; temp++;
204 if (cm_LegalChars[tc])
210 /* Copy name characters */
212 for (i = 0, name = dep->name;
213 i < (7 - nsize) && name != lastDot; ) {
218 if (!cm_LegalChars[tc])
221 *shortName++ = toupper(tc);
227 /* Copy uniquifier characters */
228 memcpy(shortName, number, nsize);
231 if (validExtension) {
232 /* Copy extension characters */
233 *shortName++ = *lastDot++; /* copy dot */
234 for (i = 0, tc = *lastDot++;
237 if (cm_LegalChars[tc]) {
239 *shortName++ = toupper(tc);
248 *shortNameEndp = shortName;
251 /* return success if we can open this file in this mode */
252 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
259 if (openMode != 1) rights |= PRSFS_READ;
260 if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
262 lock_ObtainMutex(&scp->mx);
264 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
265 CM_SCACHESYNC_GETSTATUS
266 | CM_SCACHESYNC_NEEDCALLBACK);
269 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
270 scp->fileType == CM_SCACHETYPE_FILE) {
273 unsigned int sLockType;
274 LARGE_INTEGER LOffset, LLength;
276 /* Check if there's some sort of lock on the file at the
279 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
281 if (rights & PRSFS_WRITE)
284 sLockType = LOCKING_ANDX_SHARED_LOCK;
286 LOffset.HighPart = 1;
288 LLength.HighPart = 0;
291 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
294 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
296 /* In this case, we allow the file open to go through even
297 though we can't enforce mandatory locking on the
299 if (code == CM_ERROR_NOACCESS &&
300 !(rights & PRSFS_WRITE))
303 code = CM_ERROR_SHARING_VIOLATION;
307 lock_ReleaseMutex(&scp->mx);
312 /* return success if we can open this file in this mode */
313 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
314 unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
319 /* Always allow delete; the RPC will tell us if it's OK */
320 if (desiredAccess == DELETE)
325 if (desiredAccess & AFS_ACCESS_READ)
326 rights |= PRSFS_READ;
328 if ((desiredAccess & AFS_ACCESS_WRITE)
330 rights |= PRSFS_WRITE;
332 lock_ObtainMutex(&scp->mx);
334 code = cm_SyncOp(scp, NULL, userp, reqp, rights,
335 CM_SCACHESYNC_GETSTATUS
336 | CM_SCACHESYNC_NEEDCALLBACK);
339 * If the open will fail because the volume is readonly, then we will
340 * return an access denied error instead. This is to help brain-dead
341 * apps run correctly on replicated volumes.
342 * See defect 10007 for more information.
344 if (code == CM_ERROR_READONLY)
345 code = CM_ERROR_NOACCESS;
346 else if (code == 0 &&
347 ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
348 scp->fileType == CM_SCACHETYPE_FILE) {
350 unsigned int sLockType;
351 LARGE_INTEGER LOffset, LLength;
353 /* Check if there's some sort of lock on the file at the
356 key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
357 if (rights & PRSFS_WRITE)
360 sLockType = LOCKING_ANDX_SHARED_LOCK;
361 LOffset.HighPart = 1;
363 LLength.HighPart = 0;
366 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
369 cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
371 /* In this case, we allow the file open to go through even
372 though we can't enforce mandatory locking on the
374 if (code == CM_ERROR_NOACCESS &&
375 !(rights & PRSFS_WRITE))
378 code = CM_ERROR_SHARING_VIOLATION;
382 lock_ReleaseMutex(&scp->mx);
388 * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
389 * done in three steps:
390 * (1) open for deletion (NT_CREATE_AND_X)
391 * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
393 * We must not do the RPC until step 3. But if we are going to return an error
394 * code (e.g. directory not empty), we must return it by step 2, otherwise most
395 * clients will not notice it. So we do a preliminary check. For deleting
396 * files, this is almost free, since we have already done the RPC to get the
397 * parent directory's status bits. But for deleting directories, we must do an
398 * additional RPC to get the directory's data to check if it is empty. Sigh.
400 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
407 unsigned short *hashTable;
409 int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
411 /* First check permissions */
412 lock_ObtainMutex(&dscp->mx);
413 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
414 CM_SCACHESYNC_GETSTATUS
415 | CM_SCACHESYNC_NEEDCALLBACK);
416 lock_ReleaseMutex(&dscp->mx);
420 /* If deleting directory, must be empty */
422 if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
425 thyper.HighPart = 0; thyper.LowPart = 0;
426 lock_ObtainRead(&scp->bufCreateLock);
427 code = buf_Get(scp, &thyper, &bufferp);
428 lock_ReleaseRead(&scp->bufCreateLock);
432 lock_ObtainMutex(&bufferp->mx);
433 lock_ObtainMutex(&scp->mx);
435 code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
436 CM_SCACHESYNC_NEEDCALLBACK
438 | CM_SCACHESYNC_BUFLOCKED);
442 if (cm_HaveBuffer(scp, bufferp, 1))
445 /* otherwise, load the buffer and try again */
446 lock_ReleaseMutex(&bufferp->mx);
447 code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
448 lock_ReleaseMutex(&scp->mx);
449 lock_ObtainMutex(&bufferp->mx);
450 lock_ObtainMutex(&scp->mx);
455 /* We try to determine emptiness without looking beyond the first page,
456 * and without assuming "." and ".." are present and are on the first
457 * page (though these assumptions might, after all, be reasonable).
459 hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
460 for (i=0; i<128; i++) {
461 idx = ntohs(hashTable[i]);
467 dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
468 if (strcmp(dep->name, ".") == 0)
470 else if (strcmp(dep->name, "..") == 0)
473 code = CM_ERROR_NOTEMPTY;
476 idx = ntohs(dep->next);
479 if (BeyondPage && HaveDot && HaveDotDot)
480 code = CM_ERROR_NOTEMPTY;
484 lock_ReleaseMutex(&bufferp->mx);
485 buf_Release(bufferp);
486 lock_ReleaseMutex(&scp->mx);
491 * Iterate through all entries in a directory.
492 * When the function funcp is called, the buffer is locked but the
493 * directory vnode is not.
495 * If the retscp parameter is not NULL, the parmp must be a
496 * cm_lookupSearch_t object.
498 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
499 osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
500 cm_scache_t **retscp)
507 osi_hyper_t dirLength;
508 osi_hyper_t bufferOffset;
509 osi_hyper_t curOffset;
513 cm_pageHeader_t *pageHeaderp;
515 long nextEntryCookie;
516 int numDirChunks; /* # of 32 byte dir chunks in this entry */
518 /* get the directory size */
519 lock_ObtainMutex(&scp->mx);
520 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
521 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
523 lock_ReleaseMutex(&scp->mx);
527 if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
528 lock_ReleaseMutex(&scp->mx);
529 return CM_ERROR_NOTDIR;
532 if (retscp) /* if this is a lookup call */
534 cm_lookupSearch_t* sp = parmp;
536 #ifdef AFS_FREELANCE_CLIENT
537 /* Freelance entries never end up in the DNLC because they
538 * do not have an associated cm_server_t
540 if ( !(cm_freelanceEnabled &&
541 sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
542 sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
543 #endif /* AFS_FREELANCE_CLIENT */
545 int casefold = sp->caseFold;
546 sp->caseFold = 0; /* we have a strong preference for exact matches */
547 if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
549 sp->caseFold = casefold;
550 lock_ReleaseMutex(&scp->mx);
553 sp->caseFold = casefold;
558 * XXX We only get the length once. It might change when we drop the
561 dirLength = scp->length;
563 lock_ReleaseMutex(&scp->mx);
566 bufferOffset.LowPart = bufferOffset.HighPart = 0;
568 curOffset = *startOffsetp;
570 curOffset.HighPart = 0;
571 curOffset.LowPart = 0;
575 /* make sure that curOffset.LowPart doesn't point to the first
576 * 32 bytes in the 2nd through last dir page, and that it
577 * doesn't point at the first 13 32-byte chunks in the first
578 * dir page, since those are dir and page headers, and don't
579 * contain useful information.
581 temp = curOffset.LowPart & (2048-1);
582 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
583 /* we're in the first page */
584 if (temp < 13*32) temp = 13*32;
587 /* we're in a later dir page */
588 if (temp < 32) temp = 32;
591 /* make sure the low order 5 bits are zero */
594 /* now put temp bits back ito curOffset.LowPart */
595 curOffset.LowPart &= ~(2048-1);
596 curOffset.LowPart |= temp;
598 /* check if we've passed the dir's EOF */
599 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
602 /* see if we can use the bufferp we have now; compute in which
603 * page the current offset would be, and check whether that's
604 * the offset of the buffer we have. If not, get the buffer.
606 thyper.HighPart = curOffset.HighPart;
607 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
608 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
611 lock_ReleaseMutex(&bufferp->mx);
612 buf_Release(bufferp);
616 lock_ObtainRead(&scp->bufCreateLock);
617 code = buf_Get(scp, &thyper, &bufferp);
618 lock_ReleaseRead(&scp->bufCreateLock);
620 /* if buf_Get() fails we do not have a buffer object to lock */
626 /* for the IFS version, we bulkstat the dirents because this
627 routine is used in place of smb_ReceiveCoreSearchDir. our
628 other option is to modify smb_ReceiveCoreSearchDir itself,
629 but this seems to be the proper use for cm_ApplyDir. */
630 lock_ObtainMutex(&scp->mx);
631 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
632 && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
634 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
635 cm_TryBulkStat(scp, &thyper, userp, reqp);
636 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
637 scp->bulkStatProgress = thyper;
639 lock_ReleaseMutex(&scp->mx);
642 lock_ObtainMutex(&bufferp->mx);
643 bufferOffset = thyper;
645 /* now get the data in the cache */
647 lock_ObtainMutex(&scp->mx);
648 code = cm_SyncOp(scp, bufferp, userp, reqp,
650 CM_SCACHESYNC_NEEDCALLBACK
652 | CM_SCACHESYNC_BUFLOCKED);
654 lock_ReleaseMutex(&scp->mx);
658 if (cm_HaveBuffer(scp, bufferp, 1)) {
659 lock_ReleaseMutex(&scp->mx);
663 /* otherwise, load the buffer and try again */
664 lock_ReleaseMutex(&bufferp->mx);
665 code = cm_GetBuffer(scp, bufferp, NULL, userp,
667 lock_ReleaseMutex(&scp->mx);
668 lock_ObtainMutex(&bufferp->mx);
673 lock_ReleaseMutex(&bufferp->mx);
674 buf_Release(bufferp);
678 } /* if (wrong buffer) ... */
680 /* now we have the buffer containing the entry we're interested
681 * in; copy it out if it represents a non-deleted entry.
683 entryInDir = curOffset.LowPart & (2048-1);
684 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
686 /* page header will help tell us which entries are free. Page
687 * header can change more often than once per buffer, since
688 * AFS 3 dir page size may be less than (but not more than) a
689 * buffer package buffer.
691 /* only look intra-buffer */
692 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
693 temp &= ~(2048 - 1); /* turn off intra-page bits */
694 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
696 /* now determine which entry we're looking at in the page. If
697 * it is free (there's a free bitmap at the start of the dir),
698 * we should skip these 32 bytes.
700 slotInPage = (entryInDir & 0x7e0) >> 5;
701 if (!(pageHeaderp->freeBitmap[slotInPage>>3]
702 & (1 << (slotInPage & 0x7)))) {
703 /* this entry is free */
704 numDirChunks = 1; /* only skip this guy */
708 tp = bufferp->datap + entryInBuffer;
709 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
711 /* while we're here, compute the next entry's location, too,
712 * since we'll need it when writing out the cookie into the
713 * dir listing stream.
715 numDirChunks = cm_NameEntries(dep->name, NULL);
717 /* compute the offset of the cookie representing the next entry */
718 nextEntryCookie = curOffset.LowPart
719 + (CM_DIR_CHUNKSIZE * numDirChunks);
721 if (dep->fid.vnode != 0) {
722 /* this is one of the entries to use: it is not deleted */
723 code = (*funcp)(scp, dep, parmp, &curOffset);
726 } /* if we're including this name */
729 /* and adjust curOffset to be where the new cookie is */
731 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
732 curOffset = LargeIntegerAdd(thyper, curOffset);
733 } /* while copying data for dir listing */
735 /* release the mutex */
737 lock_ReleaseMutex(&bufferp->mx);
738 buf_Release(bufferp);
743 int cm_NoneUpper(char *s)
747 if (c >= 'A' && c <= 'Z')
752 int cm_NoneLower(char *s)
756 if (c >= 'a' && c <= 'z')
761 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
764 cm_lookupSearch_t *sp;
769 sp = (cm_lookupSearch_t *) rockp;
771 matchName = dep->name;
773 match = cm_stricmp(matchName, sp->searchNamep);
775 match = strcmp(matchName, sp->searchNamep);
779 && !cm_Is8Dot3(dep->name)) {
780 matchName = shortName;
781 cm_Gen8Dot3Name(dep, shortName, NULL);
783 match = cm_stricmp(matchName, sp->searchNamep);
785 match = strcmp(matchName, sp->searchNamep);
795 if (!sp->caseFold || matchName == shortName) {
796 sp->fid.vnode = ntohl(dep->fid.vnode);
797 sp->fid.unique = ntohl(dep->fid.unique);
798 return CM_ERROR_STOPNOW;
802 * If we get here, we are doing a case-insensitive search, and we
803 * have found a match. Now we determine what kind of match it is:
804 * exact, lower-case, upper-case, or none of the above. This is done
805 * in order to choose among matches, if there are more than one.
808 /* Exact matches are the best. */
809 match = strcmp(matchName, sp->searchNamep);
812 sp->fid.vnode = ntohl(dep->fid.vnode);
813 sp->fid.unique = ntohl(dep->fid.unique);
814 return CM_ERROR_STOPNOW;
817 /* Lower-case matches are next. */
820 if (cm_NoneUpper(matchName)) {
825 /* Upper-case matches are next. */
828 if (cm_NoneLower(matchName)) {
833 /* General matches are last. */
839 sp->fid.vnode = ntohl(dep->fid.vnode);
840 sp->fid.unique = ntohl(dep->fid.unique);
844 /* read the contents of a mount point into the appropriate string.
845 * called with locked scp, and returns with locked scp.
847 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
854 if (scp->mountPointStringp[0])
857 /* otherwise, we have to read it in */
858 lock_ReleaseMutex(&scp->mx);
860 lock_ObtainRead(&scp->bufCreateLock);
861 thyper.LowPart = thyper.HighPart = 0;
862 code = buf_Get(scp, &thyper, &bufp);
863 lock_ReleaseRead(&scp->bufCreateLock);
865 lock_ObtainMutex(&scp->mx);
870 code = cm_SyncOp(scp, bufp, userp, reqp, 0,
871 CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
876 if (cm_HaveBuffer(scp, bufp, 0))
879 /* otherwise load buffer */
880 code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
885 /* locked, has callback, has valid data in buffer */
886 if ((tlen = scp->length.LowPart) > 1000)
887 return CM_ERROR_TOOBIG;
889 code = CM_ERROR_INVAL;
893 /* someone else did the work while we were out */
894 if (scp->mountPointStringp[0]) {
899 /* otherwise, copy out the link */
900 memcpy(scp->mountPointStringp, bufp->datap, tlen);
902 /* now make it null-terminated. Note that the original contents of a
903 * link that is a mount point is "#volname." where "." is there just to
904 * be turned into a null. That is, we can trash the last char of the
905 * link without damaging the vol name. This is a stupid convention,
906 * but that's the protocol.
908 scp->mountPointStringp[tlen-1] = 0;
917 /* called with a locked scp and chases the mount point, yielding outScpp.
918 * scp remains locked, just for simplicity of describing the interface.
920 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
921 cm_req_t *reqp, cm_scache_t **outScpp)
936 if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
937 tfid = scp->mountRootFid;
938 lock_ReleaseMutex(&scp->mx);
939 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
940 lock_ObtainMutex(&scp->mx);
944 /* parse the volume name */
945 mpNamep = scp->mountPointStringp;
946 osi_assert(mpNamep[0]);
947 tlen = strlen(scp->mountPointStringp);
948 mtType = *scp->mountPointStringp;
949 cellNamep = malloc(tlen);
950 volNamep = malloc(tlen);
952 cp = strrchr(mpNamep, ':');
954 /* cellular mount point */
955 memset(cellNamep, 0, tlen);
956 strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
957 strcpy(volNamep, cp+1);
958 /* now look up the cell */
959 cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
963 strcpy(volNamep, mpNamep+1);
965 cellp = cm_FindCellByID(scp->fid.cell);
969 code = CM_ERROR_NOSUCHCELL;
973 vnLength = strlen(volNamep);
974 if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
976 else if (vnLength >= 10
977 && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
982 /* check for backups within backups */
984 && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
985 == CM_SCACHEFLAG_RO) {
986 code = CM_ERROR_NOSUCHVOLUME;
990 /* now we need to get the volume */
991 lock_ReleaseMutex(&scp->mx);
992 code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
993 lock_ObtainMutex(&scp->mx);
996 /* save the parent of the volume root for this is the
997 * place where the volume is mounted and we must remember
998 * this in the volume structure rather than just in the
999 * scache entry lest the scache entry gets recycled
1002 lock_ObtainMutex(&volp->mx);
1003 volp->dotdotFid = dscp->fid;
1004 lock_ReleaseMutex(&volp->mx);
1006 scp->mountRootFid.cell = cellp->cellID;
1007 /* if the mt pt is in a read-only volume (not just a
1008 * backup), and if there is a read-only volume for the
1009 * target, and if this is a type '#' mount point, use
1010 * the read-only, otherwise use the one specified.
1012 if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
1013 && volp->roID != 0 && type == RWVOL)
1016 scp->mountRootFid.volume = volp->roID;
1017 else if (type == BACKVOL)
1018 scp->mountRootFid.volume = volp->bkID;
1020 scp->mountRootFid.volume = volp->rwID;
1022 /* the rest of the fid is a magic number */
1023 scp->mountRootFid.vnode = 1;
1024 scp->mountRootFid.unique = 1;
1025 scp->mountRootGen = cm_data.mountRootGen;
1027 tfid = scp->mountRootFid;
1028 lock_ReleaseMutex(&scp->mx);
1029 code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1030 lock_ObtainMutex(&scp->mx);
1039 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1040 cm_req_t *reqp, cm_scache_t **outpScpp)
1043 int dnlcHit = 1; /* did we hit in the dnlc? yes, we did */
1044 cm_scache_t *tscp = NULL;
1045 cm_scache_t *mountedScp;
1046 cm_lookupSearch_t rock;
1049 if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1050 && strcmp(namep, "..") == 0) {
1051 if (dscp->dotdotFid.volume == 0)
1052 return CM_ERROR_NOSUCHVOLUME;
1053 rock.fid = dscp->dotdotFid;
1057 memset(&rock, 0, sizeof(rock));
1058 rock.fid.cell = dscp->fid.cell;
1059 rock.fid.volume = dscp->fid.volume;
1060 rock.searchNamep = namep;
1061 rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1062 rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1064 /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1065 code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1066 (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1068 /* code == 0 means we fell off the end of the dir, while stopnow means
1069 * that we stopped early, probably because we found the entry we're
1070 * looking for. Any other non-zero code is an error.
1072 if (code && code != CM_ERROR_STOPNOW) {
1073 /* if the cm_scache_t we are searching in is not a directory
1074 * we must return path not found because the error
1075 * is to describe the final component not an intermediary
1077 if (code == CM_ERROR_NOTDIR) {
1078 if (flags & CM_FLAG_CHECKPATH)
1079 return CM_ERROR_NOSUCHPATH;
1081 return CM_ERROR_NOSUCHFILE;
1086 getroot = (dscp==cm_data.rootSCachep) ;
1088 if (!cm_freelanceEnabled || !getroot) {
1089 if (flags & CM_FLAG_CHECKPATH)
1090 return CM_ERROR_NOSUCHPATH;
1092 return CM_ERROR_NOSUCHFILE;
1094 else { /* nonexistent dir on freelance root, so add it */
1095 char fullname[200] = ".";
1098 osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
1099 osi_LogSaveString(afsd_logp,namep));
1100 if (namep[0] == '.') {
1101 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1103 if ( stricmp(&namep[1], &fullname[1]) )
1104 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1106 code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1109 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1111 if ( stricmp(namep, fullname) )
1112 code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1114 code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1117 if (!found || code < 0) { /* add mount point failed, so give up */
1118 if (flags & CM_FLAG_CHECKPATH)
1119 return CM_ERROR_NOSUCHPATH;
1121 return CM_ERROR_NOSUCHFILE;
1123 tscp = NULL; /* to force call of cm_GetSCache */
1128 if ( !tscp ) /* we did not find it in the dnlc */
1131 code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1135 /* tscp is now held */
1137 lock_ObtainMutex(&tscp->mx);
1138 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1139 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1141 lock_ReleaseMutex(&tscp->mx);
1142 cm_ReleaseSCache(tscp);
1145 /* tscp is now locked */
1147 if (!(flags & CM_FLAG_NOMOUNTCHASE)
1148 && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1149 /* mount points are funny: they have a volume name to mount
1152 code = cm_ReadMountPoint(tscp, userp, reqp);
1154 code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1156 lock_ReleaseMutex(&tscp->mx);
1157 cm_ReleaseSCache(tscp);
1164 lock_ReleaseMutex(&tscp->mx);
1167 /* copy back pointer */
1170 /* insert scache in dnlc */
1171 if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1172 /* lock the directory entry to prevent racing callback revokes */
1173 lock_ObtainMutex(&dscp->mx);
1174 if ( dscp->cbServerp && dscp->cbExpires )
1175 cm_dnlcEnter(dscp, namep, tscp);
1176 lock_ReleaseMutex(&dscp->mx);
1183 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1188 tp = strrchr(inp, '@');
1190 return 0; /* no @sys */
1192 if (strcmp(tp, "@sys") != 0)
1193 return 0; /* no @sys */
1195 /* caller just wants to know if this is a valid @sys type of name */
1199 if (index >= MAXNUMSYSNAMES)
1202 /* otherwise generate the properly expanded @sys name */
1203 prefixCount = tp - inp;
1205 strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
1206 outp[prefixCount] = 0; /* null terminate the "a." */
1207 strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1211 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1212 cm_req_t *reqp, cm_scache_t **outpScpp)
1216 int sysNameIndex = 0;
1217 cm_scache_t *scp = 0;
1219 if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1220 if (flags & CM_FLAG_CHECKPATH)
1221 return CM_ERROR_NOSUCHPATH;
1223 return CM_ERROR_NOSUCHFILE;
1226 for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1227 code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1229 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1235 cm_ReleaseSCache(scp);
1239 return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1243 /* None of the possible sysName expansions could be found */
1244 if (flags & CM_FLAG_CHECKPATH)
1245 return CM_ERROR_NOSUCHPATH;
1247 return CM_ERROR_NOSUCHFILE;
1250 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1256 AFSFetchStatus newDirStatus;
1258 struct rx_connection * callp;
1260 #ifdef AFS_FREELANCE_CLIENT
1261 if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1262 /* deleting a mount point from the root dir. */
1263 code = cm_FreelanceRemoveMount(namep);
1268 /* make sure we don't screw up the dir status during the merge */
1269 lock_ObtainMutex(&dscp->mx);
1270 sflags = CM_SCACHESYNC_STOREDATA;
1271 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1272 lock_ReleaseMutex(&dscp->mx);
1277 afsFid.Volume = dscp->fid.volume;
1278 afsFid.Vnode = dscp->fid.vnode;
1279 afsFid.Unique = dscp->fid.unique;
1281 osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%x", (long) dscp);
1283 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1287 callp = cm_GetRxConn(connp);
1288 code = RXAFS_RemoveFile(callp, &afsFid, namep,
1289 &newDirStatus, &volSync);
1290 rx_PutConnection(callp);
1292 } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1293 code = cm_MapRPCError(code, reqp);
1296 osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1298 osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1300 lock_ObtainMutex(&dscp->mx);
1301 cm_dnlcRemove(dscp, namep);
1302 cm_SyncOpDone(dscp, NULL, sflags);
1304 cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1305 lock_ReleaseMutex(&dscp->mx);
1310 /* called with a locked vnode, and fills in the link info.
1311 * returns this the vnode still locked.
1313 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1320 lock_AssertMutex(&linkScp->mx);
1321 if (!linkScp->mountPointStringp[0]) {
1322 /* read the link data */
1323 lock_ReleaseMutex(&linkScp->mx);
1324 thyper.LowPart = thyper.HighPart = 0;
1325 code = buf_Get(linkScp, &thyper, &bufp);
1326 lock_ObtainMutex(&linkScp->mx);
1330 code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1331 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1336 if (cm_HaveBuffer(linkScp, bufp, 0))
1339 code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1344 } /* while loop to get the data */
1346 /* now if we still have no link read in,
1347 * copy the data from the buffer */
1348 if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1350 return CM_ERROR_TOOBIG;
1353 /* otherwise, it fits; make sure it is still null (could have
1354 * lost race with someone else referencing this link above),
1355 * and if so, copy in the data.
1357 if (!linkScp->mountPointStringp[0]) {
1358 strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1359 linkScp->mountPointStringp[temp] = 0; /* null terminate */
1362 } /* don't have sym link contents cached */
1367 /* called with a held vnode and a path suffix, with the held vnode being a
1368 * symbolic link. Our goal is to generate a new path to interpret, and return
1369 * this new path in newSpaceBufferp. If the new vnode is relative to a dir
1370 * other than the directory containing the symbolic link, then the new root is
1371 * returned in *newRootScpp, otherwise a null is returned there.
1373 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1374 cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1375 cm_user_t *userp, cm_req_t *reqp)
1382 lock_ObtainMutex(&linkScp->mx);
1383 code = cm_HandleLink(linkScp, userp, reqp);
1387 /* if we may overflow the buffer, bail out; buffer is signficantly
1388 * bigger than max path length, so we don't really have to worry about
1389 * being a little conservative here.
1391 if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1392 >= CM_UTILS_SPACESIZE)
1393 return CM_ERROR_TOOBIG;
1395 tsp = cm_GetSpace();
1396 linkp = linkScp->mountPointStringp;
1397 if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1398 if (strlen(linkp) > cm_mountRootLen)
1399 strcpy(tsp->data, linkp+cm_mountRootLen+1);
1402 *newRootScpp = cm_data.rootSCachep;
1403 cm_HoldSCache(cm_data.rootSCachep);
1404 } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1405 if (!strnicmp(&linkp[2], cm_NetbiosName, (len = strlen(cm_NetbiosName))))
1407 char * p = &linkp[len + 3];
1408 if (strnicmp(p, "all", 3) == 0)
1411 strcpy(tsp->data, p);
1412 for (p = tsp->data; *p; p++) {
1416 *newRootScpp = cm_data.rootSCachep;
1417 cm_HoldSCache(cm_data.rootSCachep);
1419 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1420 strcpy(tsp->data, linkp);
1421 *newRootScpp = NULL;
1422 code = CM_ERROR_PATH_NOT_COVERED;
1424 } else if ( !strnicmp(linkp, "msdfs:", (len = strlen("msdfs:"))) ) {
1425 linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1426 strcpy(tsp->data, linkp);
1427 *newRootScpp = NULL;
1428 code = CM_ERROR_PATH_NOT_COVERED;
1429 } else if (*linkp == '\\' || *linkp == '/') {
1431 /* formerly, this was considered to be from the AFS root,
1432 * but this seems to create problems. instead, we will just
1433 * reject the link */
1434 strcpy(tsp->data, linkp+1);
1435 *newRootScpp = cm_data.rootSCachep;
1436 cm_HoldSCache(cm_data.rootSCachep);
1438 /* we still copy the link data into the response so that
1439 * the user can see what the link points to
1441 linkScp->fileType = CM_SCACHETYPE_INVALID;
1442 strcpy(tsp->data, linkp);
1443 *newRootScpp = NULL;
1444 code = CM_ERROR_NOSUCHPATH;
1447 /* a relative link */
1448 strcpy(tsp->data, linkp);
1449 *newRootScpp = NULL;
1451 if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
1452 strcat(tsp->data, "\\");
1453 strcat(tsp->data, pathSuffixp);
1455 *newSpaceBufferp = tsp;
1458 lock_ReleaseMutex(&linkScp->mx);
1462 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1463 cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1466 char *tp; /* ptr moving through input buffer */
1467 char tc; /* temp char */
1468 int haveComponent; /* has new component started? */
1469 char component[256]; /* this is the new component */
1470 char *cp; /* component name being assembled */
1471 cm_scache_t *tscp; /* current location in the hierarchy */
1472 cm_scache_t *nscp; /* next dude down */
1473 cm_scache_t *dirScp; /* last dir we searched */
1474 cm_scache_t *linkScp; /* new root for the symlink we just
1476 cm_space_t *psp; /* space for current path, if we've hit
1478 cm_space_t *tempsp; /* temp vbl */
1479 char *restp; /* rest of the pathname to interpret */
1480 int symlinkCount; /* count of # of symlinks traversed */
1481 int extraFlag; /* avoid chasing mt pts for dir cmd */
1482 int phase = 1; /* 1 = tidPathp, 2 = pathp */
1495 cm_HoldSCache(tscp);
1502 /* map Unix slashes into DOS ones so we can interpret Unix
1508 if (!haveComponent) {
1511 } else if (tc == 0) {
1525 /* we have a component here */
1526 if (tc == 0 || tc == '\\') {
1527 /* end of the component; we're at the last
1528 * component if tc == 0. However, if the last
1529 * is a symlink, we have more to do.
1531 *cp++ = 0; /* add null termination */
1533 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1534 extraFlag = CM_FLAG_NOMOUNTCHASE;
1535 code = cm_Lookup(tscp, component,
1537 userp, reqp, &nscp);
1539 cm_ReleaseSCache(tscp);
1541 cm_ReleaseSCache(dirScp);
1544 if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1545 return CM_ERROR_NOSUCHPATH;
1549 haveComponent = 0; /* component done */
1551 cm_ReleaseSCache(dirScp);
1552 dirScp = tscp; /* for some symlinks */
1553 tscp = nscp; /* already held */
1555 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1558 cm_ReleaseSCache(dirScp);
1564 /* now, if tscp is a symlink, we should follow
1565 * it and assemble the path again.
1567 lock_ObtainMutex(&tscp->mx);
1568 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1569 CM_SCACHESYNC_GETSTATUS
1570 | CM_SCACHESYNC_NEEDCALLBACK);
1572 lock_ReleaseMutex(&tscp->mx);
1573 cm_ReleaseSCache(tscp);
1576 cm_ReleaseSCache(dirScp);
1581 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1582 /* this is a symlink; assemble a new buffer */
1583 lock_ReleaseMutex(&tscp->mx);
1584 if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1585 cm_ReleaseSCache(tscp);
1588 cm_ReleaseSCache(dirScp);
1593 return CM_ERROR_TOO_MANY_SYMLINKS;
1599 code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1601 /* something went wrong */
1602 cm_ReleaseSCache(tscp);
1605 cm_ReleaseSCache(dirScp);
1611 /* otherwise, tempsp has the new path,
1612 * and linkScp is the new root from
1613 * which to interpret that path.
1614 * Continue with the namei processing,
1615 * also doing the bookkeeping for the
1616 * space allocation and tracking the
1617 * vnode reference counts.
1623 cm_ReleaseSCache(tscp);
1628 * now, if linkScp is null, that's
1629 * AssembleLink's way of telling us that
1630 * the sym link is relative to the dir
1631 * containing the link. We have a ref
1632 * to it in dirScp, and we hold it now
1633 * and reuse it as the new spot in the
1641 /* not a symlink, we may be done */
1642 lock_ReleaseMutex(&tscp->mx);
1650 cm_ReleaseSCache(dirScp);
1658 cm_ReleaseSCache(dirScp);
1661 } /* end of a component */
1664 } /* we have a component */
1665 } /* big while loop over all components */
1669 cm_ReleaseSCache(dirScp);
1675 cm_ReleaseSCache(tscp);
1679 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1680 * We chase the link, and return a held pointer to the target, if it exists,
1681 * in *outScpp. If we succeed, we return 0, otherwise we return an error code
1682 * and do not hold or return a target vnode.
1684 * This is very similar to calling cm_NameI with the last component of a name,
1685 * which happens to be a symlink, except that we've already passed by the name.
1687 * This function is typically called by the directory listing functions, which
1688 * encounter symlinks but need to return the proper file length so programs
1689 * like "more" work properly when they make use of the attributes retrieved from
1692 * The input vnode should not be locked when this function is called.
1694 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1695 cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1699 cm_scache_t *newRootScp;
1701 osi_Log1(afsd_logp, "Evaluating symlink scp 0x%x", linkScp);
1703 code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1707 /* now, if newRootScp is NULL, we're really being told that the symlink
1708 * is relative to the current directory (dscp).
1710 if (newRootScp == NULL) {
1712 cm_HoldSCache(dscp);
1715 code = cm_NameI(newRootScp, spacep->data,
1716 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1717 userp, NULL, reqp, outScpp);
1719 if (code == CM_ERROR_NOSUCHFILE)
1720 code = CM_ERROR_NOSUCHPATH;
1722 /* this stuff is allocated no matter what happened on the namei call,
1724 cm_FreeSpace(spacep);
1725 cm_ReleaseSCache(newRootScp);
1730 /* make this big enough so that one buffer of dir pages won't overflow. We'll
1731 * check anyway, but we want to minimize the chance that we have to leave stuff
1734 #define CM_BULKMAX 128
1736 /* rock for bulk stat calls */
1737 typedef struct cm_bulkStat {
1738 osi_hyper_t bufOffset; /* only do it for things in this buffer page */
1740 /* info for the actual call */
1741 int counter; /* next free slot */
1742 AFSFid fids[CM_BULKMAX];
1743 AFSFetchStatus stats[CM_BULKMAX];
1744 AFSCallBack callbacks[CM_BULKMAX];
1747 /* for a given entry, make sure that it isn't in the stat cache, and then
1748 * add it to the list of file IDs to be obtained.
1750 * Don't bother adding it if we already have a vnode. Note that the dir
1751 * is locked, so we have to be careful checking the vnode we're thinking of
1752 * processing, to avoid deadlocks.
1754 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1765 /* Don't overflow bsp. */
1766 if (bsp->counter >= CM_BULKMAX)
1767 return CM_ERROR_STOPNOW;
1769 thyper.LowPart = cm_data.buf_blockSize;
1770 thyper.HighPart = 0;
1771 thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1773 /* thyper is now the first byte past the end of the record we're
1774 * interested in, and bsp->bufOffset is the first byte of the record
1775 * we're interested in.
1776 * Skip data in the others.
1779 if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1781 if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1782 return CM_ERROR_STOPNOW;
1783 if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1786 tfid.cell = scp->fid.cell;
1787 tfid.volume = scp->fid.volume;
1788 tfid.vnode = ntohl(dep->fid.vnode);
1789 tfid.unique = ntohl(dep->fid.unique);
1790 tscp = cm_FindSCache(&tfid);
1792 if (lock_TryMutex(&tscp->mx)) {
1793 /* we have an entry that we can look at */
1794 if (cm_HaveCallback(tscp)) {
1795 /* we have a callback on it. Don't bother
1796 * fetching this stat entry, since we're happy
1797 * with the info we have.
1799 lock_ReleaseMutex(&tscp->mx);
1800 cm_ReleaseSCache(tscp);
1803 lock_ReleaseMutex(&tscp->mx);
1805 cm_ReleaseSCache(tscp);
1808 #ifdef AFS_FREELANCE_CLIENT
1809 // yj: if this is a mountpoint under root.afs then we don't want it
1810 // to be bulkstat-ed, instead, we call getSCache directly and under
1811 // getSCache, it is handled specially.
1812 if ( cm_freelanceEnabled &&
1813 tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
1814 tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1815 !(tfid.vnode==0x1 && tfid.unique==0x1) )
1817 osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1818 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1820 #endif /* AFS_FREELANCE_CLIENT */
1823 bsp->fids[i].Volume = scp->fid.volume;
1824 bsp->fids[i].Vnode = tfid.vnode;
1825 bsp->fids[i].Unique = tfid.unique;
1829 /* called with a locked scp and a pointer to a buffer. Make bulk stat
1830 * calls on all undeleted files in the page of the directory specified.
1832 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1836 cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
1837 * watch for stack problems */
1838 AFSCBFids fidStruct;
1839 AFSBulkStats statStruct;
1841 AFSCBs callbackStruct;
1844 cm_callbackRequest_t cbReq;
1850 struct rx_connection * callp;
1852 osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1854 /* should be on a buffer boundary */
1855 osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1858 bb.bufOffset = *offsetp;
1860 lock_ReleaseMutex(&dscp->mx);
1861 /* first, assemble the file IDs we need to stat */
1862 code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1864 /* if we failed, bail out early */
1865 if (code && code != CM_ERROR_STOPNOW) {
1866 lock_ObtainMutex(&dscp->mx);
1870 /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1871 * make the calls to create the entries. Handle AFSCBMAX files at a
1875 while (filex < bb.counter) {
1876 filesThisCall = bb.counter - filex;
1877 if (filesThisCall > AFSCBMAX)
1878 filesThisCall = AFSCBMAX;
1880 fidStruct.AFSCBFids_len = filesThisCall;
1881 fidStruct.AFSCBFids_val = &bb.fids[filex];
1882 statStruct.AFSBulkStats_len = filesThisCall;
1883 statStruct.AFSBulkStats_val = &bb.stats[filex];
1884 callbackStruct.AFSCBs_len = filesThisCall;
1885 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1886 cm_StartCallbackGrantingCall(NULL, &cbReq);
1887 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1889 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1893 callp = cm_GetRxConn(connp);
1894 code = RXAFS_BulkStatus(callp, &fidStruct,
1895 &statStruct, &callbackStruct, &volSync);
1896 rx_PutConnection(callp);
1898 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1899 &volSync, NULL, &cbReq, code));
1900 code = cm_MapRPCError(code, reqp);
1903 osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1905 osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1907 /* may as well quit on an error, since we're not going to do
1908 * much better on the next immediate call, either.
1911 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1915 /* otherwise, we should do the merges */
1916 for (i = 0; i<filesThisCall; i++) {
1918 tfid.cell = dscp->fid.cell;
1919 tfid.volume = bb.fids[j].Volume;
1920 tfid.vnode = bb.fids[j].Vnode;
1921 tfid.unique = bb.fids[j].Unique;
1922 code = cm_GetSCache(&tfid, &scp, userp, reqp);
1926 /* otherwise, if this entry has no callback info,
1929 lock_ObtainMutex(&scp->mx);
1930 /* now, we have to be extra paranoid on merging in this
1931 * information, since we didn't use cm_SyncOp before
1932 * starting the fetch to make sure that no bad races
1933 * were occurring. Specifically, we need to make sure
1934 * we don't obliterate any newer information in the
1935 * vnode than have here.
1937 * Right now, be pretty conservative: if there's a
1938 * callback or a pending call, skip it.
1940 if (scp->cbServerp == NULL
1942 (CM_SCACHEFLAG_FETCHING
1943 | CM_SCACHEFLAG_STORING
1944 | CM_SCACHEFLAG_SIZESTORING))) {
1945 cm_EndCallbackGrantingCall(scp, &cbReq,
1947 CM_CALLBACK_MAINTAINCOUNT);
1948 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1951 lock_ReleaseMutex(&scp->mx);
1952 cm_ReleaseSCache(scp);
1953 } /* all files in the response */
1954 /* now tell it to drop the count,
1955 * after doing the vnode processing above */
1956 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1958 filex += filesThisCall;
1959 } /* while there are still more files to process */
1960 lock_ObtainMutex(&dscp->mx);
1961 osi_Log0(afsd_logp, "END cm_TryBulkStat");
1964 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1968 /* initialize store back mask as inexpensive local variable */
1970 memset(statusp, 0, sizeof(AFSStoreStatus));
1972 /* copy out queued info from scache first, if scp passed in */
1974 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1975 statusp->ClientModTime = scp->clientModTime;
1976 mask |= AFS_SETMODTIME;
1977 scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1982 /* now add in our locally generated request */
1983 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1984 statusp->ClientModTime = attrp->clientModTime;
1985 mask |= AFS_SETMODTIME;
1987 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1988 statusp->UnixModeBits = attrp->unixModeBits;
1989 mask |= AFS_SETMODE;
1991 if (attrp->mask & CM_ATTRMASK_OWNER) {
1992 statusp->Owner = attrp->owner;
1993 mask |= AFS_SETOWNER;
1995 if (attrp->mask & CM_ATTRMASK_GROUP) {
1996 statusp->Group = attrp->group;
1997 mask |= AFS_SETGROUP;
2000 statusp->Mask = mask;
2003 /* set the file size, and make sure that all relevant buffers have been
2004 * truncated. Ensure that any partially truncated buffers have been zeroed
2005 * to the end of the buffer.
2007 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2013 /* start by locking out buffer creation */
2014 lock_ObtainWrite(&scp->bufCreateLock);
2016 /* verify that this is a file, not a dir or a symlink */
2017 lock_ObtainMutex(&scp->mx);
2018 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2019 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2023 if (scp->fileType != CM_SCACHETYPE_FILE) {
2024 code = CM_ERROR_ISDIR;
2029 if (LargeIntegerLessThan(*sizep, scp->length))
2034 lock_ReleaseMutex(&scp->mx);
2036 /* can't hold scp->mx lock here, since we may wait for a storeback to
2037 * finish if the buffer package is cleaning a buffer by storing it to
2041 buf_Truncate(scp, userp, reqp, sizep);
2043 /* now ensure that file length is short enough, and update truncPos */
2044 lock_ObtainMutex(&scp->mx);
2046 /* make sure we have a callback (so we have the right value for the
2047 * length), and wait for it to be safe to do a truncate.
2049 code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2050 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2051 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2055 if (LargeIntegerLessThan(*sizep, scp->length)) {
2056 /* a real truncation. If truncPos is not set yet, or is bigger
2057 * than where we're truncating the file, set truncPos to this
2062 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2063 || LargeIntegerLessThan(*sizep, scp->length)) {
2065 scp->truncPos = *sizep;
2066 scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2068 /* in either case, the new file size has been changed */
2069 scp->length = *sizep;
2070 scp->mask |= CM_SCACHEMASK_LENGTH;
2072 else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2073 /* really extending the file */
2074 scp->length = *sizep;
2075 scp->mask |= CM_SCACHEMASK_LENGTH;
2078 /* done successfully */
2082 lock_ReleaseMutex(&scp->mx);
2083 lock_ReleaseWrite(&scp->bufCreateLock);
2088 /* set the file size or other attributes (but not both at once) */
2089 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2094 AFSFetchStatus afsOutStatus;
2098 AFSStoreStatus afsInStatus;
2099 struct rx_connection * callp;
2101 /* handle file length setting */
2102 if (attrp->mask & CM_ATTRMASK_LENGTH)
2103 return cm_SetLength(scp, &attrp->length, userp, reqp);
2105 flags = CM_SCACHESYNC_STORESTATUS;
2107 lock_ObtainMutex(&scp->mx);
2108 /* otherwise, we have to make an RPC to get the status */
2109 code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2111 /* make the attr structure */
2112 cm_StatusFromAttr(&afsInStatus, scp, attrp);
2114 tfid.Volume = scp->fid.volume;
2115 tfid.Vnode = scp->fid.vnode;
2116 tfid.Unique = scp->fid.unique;
2118 lock_ReleaseMutex(&scp->mx);
2122 /* now make the RPC */
2123 osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%x", (long) scp);
2125 code = cm_Conn(&scp->fid, userp, reqp, &connp);
2129 callp = cm_GetRxConn(connp);
2130 code = RXAFS_StoreStatus(callp, &tfid,
2131 &afsInStatus, &afsOutStatus, &volSync);
2132 rx_PutConnection(callp);
2134 } while (cm_Analyze(connp, userp, reqp,
2135 &scp->fid, &volSync, NULL, NULL, code));
2136 code = cm_MapRPCError(code, reqp);
2139 osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2141 osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2143 lock_ObtainMutex(&scp->mx);
2144 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2146 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2147 CM_MERGEFLAG_FORCE);
2149 /* if we're changing the mode bits, discard the ACL cache,
2150 * since we changed the mode bits.
2152 if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2153 lock_ReleaseMutex(&scp->mx);
2157 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2158 cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2163 cm_callbackRequest_t cbReq;
2168 AFSStoreStatus inStatus;
2169 AFSFetchStatus updatedDirStatus;
2170 AFSFetchStatus newFileStatus;
2171 AFSCallBack newFileCallback;
2173 struct rx_connection * callp;
2175 /* can't create names with @sys in them; must expand it manually first.
2176 * return "invalid request" if they try.
2178 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2179 return CM_ERROR_ATSYS;
2182 /* before starting the RPC, mark that we're changing the file data, so
2183 * that someone who does a chmod will know to wait until our call
2186 lock_ObtainMutex(&dscp->mx);
2187 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2189 cm_StartCallbackGrantingCall(NULL, &cbReq);
2191 lock_ReleaseMutex(&dscp->mx);
2197 cm_StatusFromAttr(&inStatus, NULL, attrp);
2199 /* try the RPC now */
2200 osi_Log1(afsd_logp, "CALL CreateFile scp 0x%x", (long) dscp);
2202 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2206 dirAFSFid.Volume = dscp->fid.volume;
2207 dirAFSFid.Vnode = dscp->fid.vnode;
2208 dirAFSFid.Unique = dscp->fid.unique;
2210 callp = cm_GetRxConn(connp);
2211 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2212 &inStatus, &newAFSFid, &newFileStatus,
2213 &updatedDirStatus, &newFileCallback,
2215 rx_PutConnection(callp);
2217 } while (cm_Analyze(connp, userp, reqp,
2218 &dscp->fid, &volSync, NULL, &cbReq, code));
2219 code = cm_MapRPCError(code, reqp);
2222 osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2224 osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2226 lock_ObtainMutex(&dscp->mx);
2227 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2229 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2231 lock_ReleaseMutex(&dscp->mx);
2233 /* now try to create the file's entry, too, but be careful to
2234 * make sure that we don't merge in old info. Since we weren't locking
2235 * out any requests during the file's creation, we may have pretty old
2239 newFid.cell = dscp->fid.cell;
2240 newFid.volume = dscp->fid.volume;
2241 newFid.vnode = newAFSFid.Vnode;
2242 newFid.unique = newAFSFid.Unique;
2243 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2245 lock_ObtainMutex(&scp->mx);
2246 if (!cm_HaveCallback(scp)) {
2247 cm_MergeStatus(scp, &newFileStatus, &volSync,
2249 cm_EndCallbackGrantingCall(scp, &cbReq,
2250 &newFileCallback, 0);
2253 lock_ReleaseMutex(&scp->mx);
2258 /* make sure we end things properly */
2260 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2265 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2269 lock_ObtainWrite(&scp->bufCreateLock);
2270 code = buf_CleanVnode(scp, userp, reqp);
2271 lock_ReleaseWrite(&scp->bufCreateLock);
2273 lock_ObtainMutex(&scp->mx);
2274 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2275 | CM_SCACHEFLAG_OUTOFSPACE);
2276 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2277 | CM_SCACHEMASK_CLIENTMODTIME
2278 | CM_SCACHEMASK_LENGTH))
2279 code = cm_StoreMini(scp, userp, reqp);
2280 lock_ReleaseMutex(&scp->mx);
2285 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2286 cm_user_t *userp, cm_req_t *reqp)
2291 cm_callbackRequest_t cbReq;
2296 AFSStoreStatus inStatus;
2297 AFSFetchStatus updatedDirStatus;
2298 AFSFetchStatus newDirStatus;
2299 AFSCallBack newDirCallback;
2301 struct rx_connection * callp;
2303 /* can't create names with @sys in them; must expand it manually first.
2304 * return "invalid request" if they try.
2306 if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2307 return CM_ERROR_ATSYS;
2310 /* before starting the RPC, mark that we're changing the directory
2311 * data, so that someone who does a chmod on the dir will wait until
2312 * our call completes.
2314 lock_ObtainMutex(&dscp->mx);
2315 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2317 cm_StartCallbackGrantingCall(NULL, &cbReq);
2319 lock_ReleaseMutex(&dscp->mx);
2325 cm_StatusFromAttr(&inStatus, NULL, attrp);
2327 /* try the RPC now */
2328 osi_Log1(afsd_logp, "CALL MakeDir scp 0x%x", (long) dscp);
2330 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2334 dirAFSFid.Volume = dscp->fid.volume;
2335 dirAFSFid.Vnode = dscp->fid.vnode;
2336 dirAFSFid.Unique = dscp->fid.unique;
2338 callp = cm_GetRxConn(connp);
2339 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2340 &inStatus, &newAFSFid, &newDirStatus,
2341 &updatedDirStatus, &newDirCallback,
2343 rx_PutConnection(callp);
2345 } while (cm_Analyze(connp, userp, reqp,
2346 &dscp->fid, &volSync, NULL, &cbReq, code));
2347 code = cm_MapRPCError(code, reqp);
2350 osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2352 osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2354 lock_ObtainMutex(&dscp->mx);
2355 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2357 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2359 lock_ReleaseMutex(&dscp->mx);
2361 /* now try to create the new dir's entry, too, but be careful to
2362 * make sure that we don't merge in old info. Since we weren't locking
2363 * out any requests during the file's creation, we may have pretty old
2367 newFid.cell = dscp->fid.cell;
2368 newFid.volume = dscp->fid.volume;
2369 newFid.vnode = newAFSFid.Vnode;
2370 newFid.unique = newAFSFid.Unique;
2371 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2373 lock_ObtainMutex(&scp->mx);
2374 if (!cm_HaveCallback(scp)) {
2375 cm_MergeStatus(scp, &newDirStatus, &volSync,
2377 cm_EndCallbackGrantingCall(scp, &cbReq,
2378 &newDirCallback, 0);
2381 lock_ReleaseMutex(&scp->mx);
2382 cm_ReleaseSCache(scp);
2386 /* make sure we end things properly */
2388 cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2390 /* and return error code */
2394 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2395 cm_user_t *userp, cm_req_t *reqp)
2400 AFSFid existingAFSFid;
2401 AFSFetchStatus updatedDirStatus;
2402 AFSFetchStatus newLinkStatus;
2404 struct rx_connection * callp;
2406 if (dscp->fid.cell != sscp->fid.cell ||
2407 dscp->fid.volume != sscp->fid.volume) {
2408 return CM_ERROR_CROSSDEVLINK;
2411 lock_ObtainMutex(&dscp->mx);
2412 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2413 lock_ReleaseMutex(&dscp->mx);
2418 /* try the RPC now */
2419 osi_Log1(afsd_logp, "CALL Link scp 0x%x", (long) dscp);
2421 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2424 dirAFSFid.Volume = dscp->fid.volume;
2425 dirAFSFid.Vnode = dscp->fid.vnode;
2426 dirAFSFid.Unique = dscp->fid.unique;
2428 existingAFSFid.Volume = sscp->fid.volume;
2429 existingAFSFid.Vnode = sscp->fid.vnode;
2430 existingAFSFid.Unique = sscp->fid.unique;
2432 callp = cm_GetRxConn(connp);
2433 code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2434 &newLinkStatus, &updatedDirStatus, &volSync);
2435 rx_PutConnection(callp);
2436 osi_Log1(smb_logp," RXAFS_Link returns 0x%x", code);
2438 } while (cm_Analyze(connp, userp, reqp,
2439 &dscp->fid, &volSync, NULL, NULL, code));
2441 code = cm_MapRPCError(code, reqp);
2444 osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2446 osi_Log0(afsd_logp, "CALL Link SUCCESS");
2448 lock_ObtainMutex(&dscp->mx);
2449 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2451 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2453 lock_ReleaseMutex(&dscp->mx);
2458 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2459 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2467 AFSStoreStatus inStatus;
2468 AFSFetchStatus updatedDirStatus;
2469 AFSFetchStatus newLinkStatus;
2471 struct rx_connection * callp;
2473 /* before starting the RPC, mark that we're changing the directory data,
2474 * so that someone who does a chmod on the dir will wait until our
2477 lock_ObtainMutex(&dscp->mx);
2478 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2479 lock_ReleaseMutex(&dscp->mx);
2484 cm_StatusFromAttr(&inStatus, NULL, attrp);
2486 /* try the RPC now */
2487 osi_Log1(afsd_logp, "CALL Symlink scp 0x%x", (long) dscp);
2489 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2493 dirAFSFid.Volume = dscp->fid.volume;
2494 dirAFSFid.Vnode = dscp->fid.vnode;
2495 dirAFSFid.Unique = dscp->fid.unique;
2497 callp = cm_GetRxConn(connp);
2498 code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2499 &inStatus, &newAFSFid, &newLinkStatus,
2500 &updatedDirStatus, &volSync);
2501 rx_PutConnection(callp);
2503 } while (cm_Analyze(connp, userp, reqp,
2504 &dscp->fid, &volSync, NULL, NULL, code));
2505 code = cm_MapRPCError(code, reqp);
2508 osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2510 osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2512 lock_ObtainMutex(&dscp->mx);
2513 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2515 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2517 lock_ReleaseMutex(&dscp->mx);
2519 /* now try to create the new dir's entry, too, but be careful to
2520 * make sure that we don't merge in old info. Since we weren't locking
2521 * out any requests during the file's creation, we may have pretty old
2525 newFid.cell = dscp->fid.cell;
2526 newFid.volume = dscp->fid.volume;
2527 newFid.vnode = newAFSFid.Vnode;
2528 newFid.unique = newAFSFid.Unique;
2529 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2531 lock_ObtainMutex(&scp->mx);
2532 if (!cm_HaveCallback(scp)) {
2533 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2536 lock_ReleaseMutex(&scp->mx);
2537 cm_ReleaseSCache(scp);
2541 /* and return error code */
2545 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2552 AFSFetchStatus updatedDirStatus;
2554 struct rx_connection * callp;
2556 /* before starting the RPC, mark that we're changing the directory data,
2557 * so that someone who does a chmod on the dir will wait until our
2560 lock_ObtainMutex(&dscp->mx);
2561 code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2562 lock_ReleaseMutex(&dscp->mx);
2568 /* try the RPC now */
2569 osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%x", (long) dscp);
2571 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2575 dirAFSFid.Volume = dscp->fid.volume;
2576 dirAFSFid.Vnode = dscp->fid.vnode;
2577 dirAFSFid.Unique = dscp->fid.unique;
2579 callp = cm_GetRxConn(connp);
2580 code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2581 &updatedDirStatus, &volSync);
2582 rx_PutConnection(callp);
2584 } while (cm_Analyze(connp, userp, reqp,
2585 &dscp->fid, &volSync, NULL, NULL, code));
2586 code = cm_MapRPCErrorRmdir(code, reqp);
2589 osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2591 osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2593 lock_ObtainMutex(&dscp->mx);
2594 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2596 cm_dnlcRemove(dscp, namep);
2597 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2599 lock_ReleaseMutex(&dscp->mx);
2601 /* and return error code */
2605 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2607 /* grab mutex on contents */
2608 lock_ObtainMutex(&scp->mx);
2610 /* reset the prefetch info */
2611 scp->prefetch.base.LowPart = 0; /* base */
2612 scp->prefetch.base.HighPart = 0;
2613 scp->prefetch.end.LowPart = 0; /* and end */
2614 scp->prefetch.end.HighPart = 0;
2616 /* release mutex on contents */
2617 lock_ReleaseMutex(&scp->mx);
2623 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2624 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2628 AFSFid oldDirAFSFid;
2629 AFSFid newDirAFSFid;
2631 AFSFetchStatus updatedOldDirStatus;
2632 AFSFetchStatus updatedNewDirStatus;
2635 struct rx_connection * callp;
2637 /* before starting the RPC, mark that we're changing the directory data,
2638 * so that someone who does a chmod on the dir will wait until our call
2639 * completes. We do this in vnode order so that we don't deadlock,
2640 * which makes the code a little verbose.
2642 if (oldDscp == newDscp) {
2643 /* check for identical names */
2644 if (strcmp(oldNamep, newNamep) == 0)
2645 return CM_ERROR_RENAME_IDENTICAL;
2648 lock_ObtainMutex(&oldDscp->mx);
2649 cm_dnlcRemove(oldDscp, oldNamep);
2650 cm_dnlcRemove(oldDscp, newNamep);
2651 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2652 CM_SCACHESYNC_STOREDATA);
2653 lock_ReleaseMutex(&oldDscp->mx);
2656 /* two distinct dir vnodes */
2658 if (oldDscp->fid.cell != newDscp->fid.cell ||
2659 oldDscp->fid.volume != newDscp->fid.volume)
2660 return CM_ERROR_CROSSDEVLINK;
2662 /* shouldn't happen that we have distinct vnodes for two
2663 * different files, but could due to deliberate attack, or
2664 * stale info. Avoid deadlocks and quit now.
2666 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2667 return CM_ERROR_CROSSDEVLINK;
2669 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2670 lock_ObtainMutex(&oldDscp->mx);
2671 cm_dnlcRemove(oldDscp, oldNamep);
2672 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2673 CM_SCACHESYNC_STOREDATA);
2674 lock_ReleaseMutex(&oldDscp->mx);
2676 lock_ObtainMutex(&newDscp->mx);
2677 cm_dnlcRemove(newDscp, newNamep);
2678 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2679 CM_SCACHESYNC_STOREDATA);
2680 lock_ReleaseMutex(&newDscp->mx);
2682 /* cleanup first one */
2683 lock_ObtainMutex(&newDscp->mx);
2684 cm_SyncOpDone(oldDscp, NULL,
2685 CM_SCACHESYNC_STOREDATA);
2686 lock_ReleaseMutex(&oldDscp->mx);
2691 /* lock the new vnode entry first */
2692 lock_ObtainMutex(&newDscp->mx);
2693 cm_dnlcRemove(newDscp, newNamep);
2694 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2695 CM_SCACHESYNC_STOREDATA);
2696 lock_ReleaseMutex(&newDscp->mx);
2698 lock_ObtainMutex(&oldDscp->mx);
2699 cm_dnlcRemove(oldDscp, oldNamep);
2700 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2701 CM_SCACHESYNC_STOREDATA);
2702 lock_ReleaseMutex(&oldDscp->mx);
2704 /* cleanup first one */
2705 lock_ObtainMutex(&newDscp->mx);
2706 cm_SyncOpDone(newDscp, NULL,
2707 CM_SCACHESYNC_STOREDATA);
2708 lock_ReleaseMutex(&newDscp->mx);
2712 } /* two distinct vnodes */
2719 /* try the RPC now */
2720 osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x",
2721 (long) oldDscp, (long) newDscp);
2723 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2727 oldDirAFSFid.Volume = oldDscp->fid.volume;
2728 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2729 oldDirAFSFid.Unique = oldDscp->fid.unique;
2730 newDirAFSFid.Volume = newDscp->fid.volume;
2731 newDirAFSFid.Vnode = newDscp->fid.vnode;
2732 newDirAFSFid.Unique = newDscp->fid.unique;
2734 callp = cm_GetRxConn(connp);
2735 code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2736 &newDirAFSFid, newNamep,
2737 &updatedOldDirStatus, &updatedNewDirStatus,
2739 rx_PutConnection(callp);
2741 } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2742 &volSync, NULL, NULL, code));
2743 code = cm_MapRPCError(code, reqp);
2746 osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2748 osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2750 /* update the individual stat cache entries for the directories */
2751 lock_ObtainMutex(&oldDscp->mx);
2752 cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2754 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2757 lock_ReleaseMutex(&oldDscp->mx);
2759 /* and update it for the new one, too, if necessary */
2761 lock_ObtainMutex(&newDscp->mx);
2762 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2764 cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2767 lock_ReleaseMutex(&newDscp->mx);
2770 /* and return error code */
2774 /* Byte range locks:
2776 The OpenAFS Windows client has to fake byte range locks given no
2777 server side support for such locks. This is implemented as keyed
2778 byte range locks on the cache manager.
2780 Keyed byte range locks:
2782 Each cm_scache_t structure keeps track of a list of keyed locks.
2783 The key for a lock is essentially a token which identifies an owner
2784 of a set of locks (referred to as a client). The set of keys used
2785 within a specific cm_scache_t structure form a namespace that has a
2786 scope of just that cm_scache_t structure. The same key value can
2787 be used with another cm_scache_t structure and correspond to a
2788 completely different client. However it is advantageous for the
2789 SMB or IFS layer to make sure that there is a 1-1 mapping between
2790 client and keys irrespective of the cm_scache_t.
2792 Assume a client C has key Key(C) (although, since the scope of the
2793 key is a cm_scache_t, the key can be Key(C,S), where S is the
2794 cm_scache_t. But assume a 1-1 relation between keys and clients).
2795 A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2796 inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
2797 through cm_generateKey() function for both SMB and IFS.
2799 The cache manager will set a lock on the AFS file server in order
2800 to assert the locks in S->fileLocks. If only shared locks are in
2801 place for S, then the cache manager will obtain a LockRead lock,
2802 while if there are any exclusive locks, it will obtain a LockWrite
2803 lock. If the exclusive locks are all released while the shared
2804 locks remain, then the cache manager will downgrade the lock from
2805 LockWrite to LockRead.
2809 A lock exists iff it is in S->fileLocks for some cm_scache_t
2810 S. Existing locks are in one of the following states: ACTIVE,
2811 WAITLOCK, WAITUNLOCK, LOST, DELETED.
2813 The following sections describe each lock and the associated
2816 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2817 the lock with the AFS file server. This type of lock can be
2818 exercised by a client to read or write to the locked region (as
2821 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2822 server lock that was required to assert the lock.
2824 1.2 ACTIVE->DELETED: Lock is released.
2826 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2827 grants the lock but the lock is yet to be asserted with the AFS
2828 file server. Once the file server grants the lock, the state
2829 will transition to an ACTIVE lock.
2831 2.1 WAITLOCK->ACTIVE: The server granted the lock.
2833 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2836 2.3 WAITLOCK->LOST: One or more locks from this client were
2837 marked as LOST. No further locks will be granted to this
2838 client until al lost locks are removed.
2840 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2841 receives a request for a lock that conflicts with an existing
2842 ACTIVE or WAITLOCK lock. The lock will be placed in the queue
2843 and will be granted at such time the conflicting locks are
2844 removed, at which point the state will transition to either
2847 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
2848 current serverLock is sufficient to assert this lock, or a
2849 sufficient serverLock is obtained.
2851 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2852 however the required serverLock is yet to be asserted with the
2855 3.3 WAITUNLOCK->DELETED: The lock is abandoned or timed out.
2857 3.5 WAITUNLOCK->LOST: One or more locks from this client were
2858 marked as LOST. No further locks will be granted to this
2859 client until all lost locks are removed.
2861 4. LOST: A lock L is LOST if the server lock that was required to
2862 assert the lock could not be obtained or if it could not be
2863 extended, or if other locks by the same client were LOST.
2864 Effectively, once a lock is LOST, the contract between the cache
2865 manager and that specific client is no longer valid.
2867 The cache manager rechecks the server lock once every minute and
2868 extends it as appropriate. If this is not done for 5 minutes,
2869 the AFS file server will release the lock. Once released, the
2870 lock cannot be re-obtained without verifying that the contents
2871 of the file hasn't been modified since the time the lock was
2872 released. Doing so may cause data corruption.
2874 4.1 LOST->DELETED: The lock is released.
2876 4.2 LOST->ACTIVE: The lock is reassertd. This requires
2877 verifying that the file was not modified in between.
2879 4.3 LOST->WAITLOCK: All LOST ACTIVE locks from this client were
2880 reasserted. The cache manager can reinstate this waiting
2883 4.4 LOST->WAITUNLOCK: All LOST ACTIVE locks from this client
2884 were reasserted. The cache manager can reinstate this waiting
2887 5. DELETED: The lock is no longer relevant. Eventually, it will
2888 get removed from the cm_scache_t. In the meantime, it will be
2889 treated as if it does not exist.
2891 5.1 DELETED->not exist: The lock is removed from the
2894 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.
2895 These locks have been accepted by the cache manager, but may or
2896 may not have been granted back to the client.
2898 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
2900 8* A lock L is EFFECTIVE if it is ACTIVE or LOST.
2902 9* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
2906 A client C can READ range (Offset,+Length) of cm_scache_t S iff:
2908 1. for all _a_ in (Offset,+Length), one of the following is true:
2910 1.1 There does NOT exist an ACTIVE lock L in S->fileLocks such
2911 that _a_ in (L->LOffset,+L->LLength) (IOW: byte _a_ of S is
2916 For each LOST lock M in S->fileLocks such that
2917 _a_ in (M->LOffset,+M->LLength), M->LockType is shared AND
2920 (Note: If this is a different client from one whose shared
2921 lock was LOST, then the contract between this client and the
2922 cache manager is indistinguishable from that where no lock
2923 was lost. If an exclusive lock was lost, then the range is
2924 considered unsafe for consumption.)
2926 1.3 There is an ACTIVE lock L in S->fileLocks such that: L->key
2927 == Key(C) && _a_ in (L->LOffset,+L->LLength) (IOW: byte _a_
2928 of S is owned by C under lock L)
2930 1.4 There is an ACTIVE lock L in S->fileLocks such that _a_ in
2931 (L->LOffset,L->+LLength) && L->LockType is shared (IOW: byte
2932 _a_ of S is shared) AND there is no LOST lock M such that _a_
2933 in (M->LOffset,+M->LLength) and M->key == Key(C)
2935 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff:
2937 2. for all _a_ in (Offset,+Length), one of the following is true:
2939 2.1 Byte _a_ of S is unowned (as above) AND for each LOST lock
2940 L in S->fileLocks _a_ NOT in (L->LOffset,+L->LLength).
2942 2.2 Byte _a_ of S is owned by C under lock L (as above) AND
2943 L->LockType is exclusive.
2945 A client C can OBTAIN a lock L on cm_scache_t S iff:
2947 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
2950 3.1 L->LockType is exclusive IMPLIES there does NOT exist a QUEUED lock
2951 M in S->fileLocks such that _a_ in (M->LOffset,+M->LLength).
2953 (Note: If we count all QUEUED locks then we hit cases such as
2954 cascading waiting locks where the locks later on in the queue
2955 can be granted without compromising file integrity. On the
2956 other hand if only ACCEPTED locks are considered, then locks
2957 that were received earlier may end up waiting for locks that
2958 were received later to be unlocked. The choice of QUEUED
2959 locks were made so that large locks don't consistently get
2960 trumped by smaller locks which were requested later.)
2962 3.2 L->LockType is shared IMPLIES for each QUEUED lock M in
2963 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
2964 M->LockType is shared.
2966 4. For each LOST lock M in S->fileLocks, M->key != Key(C)
2968 (Note: If a client loses a lock, it loses all locks.
2969 Subsequently, it will not be allowed to obtain any more locks
2970 until all existing LOST locks that belong to the client are
2971 released. Once all locks are released by a single client,
2972 there exists no further contract between the client and AFS
2973 about the contents of the file, hence the client can then
2974 proceed to obtain new locks and establish a new contract.)
2976 A client C can only unlock locks L in S->fileLocks which have
2979 The representation and invariants are as follows:
2981 - Each cm_scache_t structure keeps:
2983 - A queue of byte-range locks (cm_scache_t::fileLocks) which
2984 are of type cm_file_lock_t.
2986 - A record of the highest server-side lock that has been
2987 obtained for this object (cm_scache_t::serverLock), which is
2988 one of (-1), LockRead, LockWrite.
2990 - A count of ACCEPTED exclusive and shared locks that are in the
2991 queue (cm_scache_t::sharedLocks and
2992 cm_scache_t::exclusiveLocks)
2994 - Each cm_file_lock_t structure keeps:
2996 - The type of lock (cm_file_lock_t::LockType)
2998 - The key associated with the lock (cm_file_lock_t::key)
3000 - The offset and length of the lock (cm_file_lock_t::LOffset
3001 and cm_file_lock_t::LLength)
3003 - The state of the lock.
3005 - Time of issuance or last successful extension
3007 Semantic invariants:
3009 I1. The number of ACCEPTED locks in S->fileLocks are
3010 (S->sharedLocks + S->exclusiveLocks)
3012 External invariants:
3014 I3. S->serverLock is the lock that we have asserted with the
3015 AFS file server for this cm_scache_t.
3017 I4. S->serverLock == LockRead iff there is at least one ACTIVE
3018 shared lock, but no ACTIVE exclusive locks.
3020 I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3023 I6. If a WAITUNLOCK lock L exists in S->fileLocks, then all
3024 locks that L is waiting on are ahead of L in S->fileLocks.
3026 I7. If L is a LOST lock, then for each lock M in S->fileLocks,
3027 M->key == L->key IMPLIES M is LOST or DELETED.
3032 #define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST))==0)
3034 #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)
3036 #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)
3038 #define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3040 #define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3042 /* the following macros are unsafe */
3043 #define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3045 #define IS_LOCK_QUEUED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp) || IS_LOCK_WAITUNLOCK(lockp))
3047 #define IS_LOCK_EFFECTIVE(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_LOST(lockp))
3049 #define IS_LOCK_CLIENTONLY(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY)
3051 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3053 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3055 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3057 afs_int64 int_begin;
3060 int_begin = MAX(pos->offset, neg->offset);
3061 int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3063 if(int_begin < int_end) {
3064 if(int_begin == pos->offset) {
3065 pos->length = pos->offset + pos->length - int_end;
3066 pos->offset = int_end;
3067 } else if(int_end == pos->offset + pos->length) {
3068 pos->offset = int_begin;
3069 pos->length = int_end - int_begin;
3074 /* Called with scp->mx held. Returns 0 if all is clear to read the
3075 specified range by the client identified by key.
3077 long cm_LockCheckRead(cm_scache_t *scp,
3078 LARGE_INTEGER LOffset,
3079 LARGE_INTEGER LLength,
3082 #ifndef ADVISORY_LOCKS
3084 cm_file_lock_t *fileLock;
3088 int substract_ranges = FALSE;
3090 range.offset = LOffset.QuadPart;
3091 range.length = LLength.QuadPart;
3094 1. for all _a_ in (Offset,+Length), one of the following is true:
3096 1.1 There does NOT exist an ACTIVE lock L in S->fileLocks such
3097 that _a_ in (L->LOffset,+L->LLength) (IOW: byte _a_ of S is
3102 For each LOST lock M in S->fileLocks such that
3103 _a_ in (M->LOffset,+M->LLength), M->LockType is shared AND
3106 1.3 There is an ACTIVE lock L in S->fileLocks such that: L->key
3107 == Key(C) && _a_ in (L->LOffset,+L->LLength) (IOW: byte _a_
3108 of S is owned by C under lock L)
3110 1.4 There is an ACTIVE lock L in S->fileLocks such that _a_ in
3111 (L->LOffset,L->+LLength) && L->LockType is shared (IOW: byte
3112 _a_ of S is shared) AND there is no LOST lock M such that _a_
3113 in (M->LOffset,+M->LLength) and M->key == Key(C)
3116 lock_ObtainRead(&cm_scacheLock);
3118 for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3120 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3123 if(IS_LOCK_DELETED(fileLock) ||
3124 (IS_LOCK_LOST(fileLock) &&
3125 fileLock->lockType == LockRead &&
3126 fileLock->key != key))
3130 if(INTERSECT_RANGE(range, fileLock->range)) {
3131 if(IS_LOCK_ACTIVE(fileLock)) {
3132 if(fileLock->key == key) {
3134 /* if there is an active lock for this client, it
3135 is safe to substract ranges */
3136 cm_LockRangeSubtract(&range, &fileLock->range);
3137 substract_ranges = TRUE;
3139 if(fileLock->lockType != LockRead) {
3140 code = CM_ERROR_LOCK_CONFLICT;
3144 /* even if the entire range is locked for reading,
3145 we still can't grant the lock at this point
3146 because the client may have lost locks. That
3147 is, unless we have already seen an active lock
3148 belonging to the client, in which case there
3149 can't be any lost locks. */
3150 if(substract_ranges)
3151 cm_LockRangeSubtract(&range, &fileLock->range);
3153 } else if(IS_LOCK_LOST(fileLock)
3155 /* Uncomment for less aggressive handling of
3158 (fileLock->key == key || fileLock->lockType == LockWrite)
3161 code = CM_ERROR_BADFD;
3164 } else if (IS_LOCK_LOST(fileLock)) {
3165 code = CM_ERROR_BADFD;
3170 lock_ReleaseRead(&cm_scacheLock);
3181 /* Called with scp->mx held. Returns 0 if all is clear to write the
3182 specified range by the client identified by key.
3184 long cm_LockCheckWrite(cm_scache_t *scp,
3185 LARGE_INTEGER LOffset,
3186 LARGE_INTEGER LLength,
3189 #ifndef ADVISORY_LOCKS
3191 cm_file_lock_t *fileLock;
3196 range.offset = LOffset.QuadPart;
3197 range.length = LLength.QuadPart;
3200 A client C can WRITE range (Offset,+Length) of cm_scache_t S iff:
3202 2. for all _a_ in (Offset,+Length), one of the following is true:
3204 2.1 Byte _a_ of S is unowned (as above) AND for each LOST lock
3205 L in S->fileLocks _a_ NOT in (L->LOffset,+L->LLength).
3207 2.2 Byte _a_ of S is owned by C under lock L (as above) AND
3208 L->LockType is exclusive.
3212 lock_ObtainRead(&cm_scacheLock);
3214 for(q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3216 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3219 if(IS_LOCK_DELETED(fileLock) ||
3220 (IS_LOCK_LOST(fileLock) &&
3221 fileLock->lockType == LockRead &&
3222 fileLock->key != key))
3226 if(INTERSECT_RANGE(range, fileLock->range)) {
3227 if(IS_LOCK_ACTIVE(fileLock)) {
3228 if(fileLock->key == key) {
3229 if(fileLock->lockType == LockWrite) {
3231 /* if there is an active lock for this client, it
3232 is safe to substract ranges */
3233 cm_LockRangeSubtract(&range, &fileLock->range);
3235 code = CM_ERROR_LOCK_CONFLICT;
3239 code = CM_ERROR_LOCK_CONFLICT;
3242 } else if(IS_LOCK_LOST(fileLock)) {
3243 code = CM_ERROR_BADFD;
3246 } else if (IS_LOCK_LOST(fileLock)) {
3247 code = CM_ERROR_BADFD;
3252 lock_ReleaseRead(&cm_scacheLock);
3264 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3266 /* Called with cm_scacheLock write locked */
3267 static cm_file_lock_t * cm_GetFileLock(void) {
3270 l = (cm_file_lock_t *) cm_freeFileLocks;
3272 osi_QRemove(&cm_freeFileLocks, &l->q);
3274 l = malloc(sizeof(cm_file_lock_t));
3278 memset(l, 0, sizeof(cm_file_lock_t));
3283 /* Called with cm_scacheLock write locked */
3284 static void cm_PutFileLock(cm_file_lock_t *l) {
3285 osi_QAdd(&cm_freeFileLocks, &l->q);
3288 /* called with scp->mx held */
3289 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3290 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3292 int allowWait, cm_user_t *userp, cm_req_t *reqp,
3293 cm_file_lock_t **lockpp)
3296 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3300 cm_file_lock_t *fileLock;
3302 struct rx_connection * callp;
3304 int wait_unlock = FALSE;
3306 osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
3307 scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3308 osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
3309 (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3312 A client C can OBTAIN a lock L on cm_scache_t S iff:
3314 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3317 3.1 L->LockType is exclusive IMPLIES there does NOT exist a QUEUED lock
3318 M in S->fileLocks such that _a_ in (M->LOffset,+M->LLength).
3320 3.2 L->LockType is shared IMPLIES for each QUEUED lock M in
3321 S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3322 M->LockType is shared.
3324 4. For each LOST lock M in S->fileLocks, M->key != Key(C)
3327 range.offset = LOffset.QuadPart;
3328 range.length = LLength.QuadPart;
3330 lock_ObtainRead(&cm_scacheLock);
3332 for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
3334 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3336 if(IS_LOCK_LOST(fileLock) && fileLock->key == key) {
3337 code = CM_ERROR_BADFD;
3341 /* we don't need to check for deleted locks here since deleted
3342 locks are dequeued from fileLocks */
3343 if(INTERSECT_RANGE(range, fileLock->range)) {
3345 if((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
3346 fileLock->lockType != LockRead) {
3348 code = CM_ERROR_WOULDBLOCK;
3354 lock_ReleaseRead(&cm_scacheLock);
3356 if(code == 0 && !(scp->flags & CM_SCACHEFLAG_RO)) {
3357 if(Which == scp->serverLock ||
3358 (Which == LockRead && scp->serverLock == LockWrite)) {
3360 /* we already have the lock we need */
3361 osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
3362 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3363 code = 0; /* redundant */
3365 } else if((scp->exclusiveLocks > 0) ||
3366 (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
3368 /* We are already waiting for some other lock. We should
3369 wait for the daemon to catch up instead of generating a
3370 flood of SetLock calls. */
3371 osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
3372 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3373 code = CM_ERROR_WOULDBLOCK;
3379 if (scp->serverLock == LockRead && Which == LockWrite) {
3381 /* We want to escalate the lock to a LockWrite.
3382 Unfortunately that's not really possible without
3383 letting go of the current lock. But for now we do
3386 osi_Log0(afsd_logp, " attempting to UPGRADE from LockRead to LockWrite.");
3388 tfid.Volume = scp->fid.volume;
3389 tfid.Vnode = scp->fid.vnode;
3390 tfid.Unique = scp->fid.unique;
3393 lock_ReleaseMutex(&scp->mx);
3395 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
3398 code = cm_Conn(&cfid, userp, reqp, &connp);
3402 callp = cm_GetRxConn(connp);
3403 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3404 rx_PutConnection(callp);
3406 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3408 code = cm_MapRPCError(code, reqp);
3411 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3413 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3415 lock_ObtainMutex(&scp->mx);
3418 /* We couldn't release the lock */
3421 scp->serverLock = -1;
3425 /* We need to obtain a server lock of type Which in order
3426 to assert this file lock */
3427 tfid.Volume = scp->fid.volume;
3428 tfid.Vnode = scp->fid.vnode;
3429 tfid.Unique = scp->fid.unique;
3432 #ifndef AGGRESSIVE_LOCKS
3435 newLock = LockWrite;
3437 osi_Log3(afsd_logp, "CALL SetLock scp 0x%x from %d to %d", (long) scp, (int) scp->serverLock, newLock);
3439 lock_ReleaseMutex(&scp->mx);
3442 code = cm_Conn(&cfid, userp, reqp, &connp);
3446 callp = cm_GetRxConn(connp);
3447 code = RXAFS_SetLock(callp, &tfid, newLock,
3449 rx_PutConnection(callp);
3451 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3454 code = cm_MapRPCError(code, reqp);
3457 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3459 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3462 if (code == CM_ERROR_WOULDBLOCK && newLock != Which) {
3463 /* we wanted LockRead. We tried LockWrite. Now try LockRead again */
3467 osi_assert(newLock == LockRead);
3469 osi_Log3(afsd_logp, "CALL SetLock AGAIN scp 0x%x from %d to %d",
3470 (long) scp, (int) scp->serverLock, newLock);
3473 code = cm_Conn(&cfid, userp, reqp, &connp);
3477 callp = cm_GetRxConn(connp);
3478 code = RXAFS_SetLock(callp, &tfid, newLock,
3480 rx_PutConnection(callp);
3482 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3485 code = cm_MapRPCError(code, reqp);
3488 osi_Log1(afsd_logp, "CALL SetLock FAILURE AGAIN, code 0x%x", code);
3490 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3494 lock_ObtainMutex(&scp->mx);
3497 scp->serverLock = newLock;
3499 if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3500 scp->serverLock == -1) {
3501 /* Oops. We lost the lock. */
3502 cm_LockMarkSCacheLost(scp);
3506 } else if (scp->flags & CM_SCACHEFLAG_RO) {
3507 osi_Log0(afsd_logp, " Skipping server lock for RO scp");
3513 /* Special case error translations
3515 Applications don't expect certain errors from a
3516 LockFile/UnlockFile call. We need to translate some error
3517 code to codes that apps expect and handle. */
3519 /* We shouldn't actually need to handle this case since we
3520 simulate locks for RO scps anyway. */
3521 if (code == CM_ERROR_READONLY) {
3522 osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
3523 code = CM_ERROR_NOACCESS;
3527 if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait)) {
3529 lock_ObtainWrite(&cm_scacheLock);
3530 fileLock = cm_GetFileLock();
3531 lock_ReleaseWrite(&cm_scacheLock);
3533 fileLock->fid = scp->fid;
3535 fileLock->key = key;
3536 fileLock->lockType = Which;
3538 fileLock->userp = userp;
3539 fileLock->range = range;
3540 fileLock->flags = (code == 0 ? 0 :
3542 CM_FILELOCK_FLAG_WAITUNLOCK :
3543 CM_FILELOCK_FLAG_WAITLOCK));
3544 if (scp->flags & CM_SCACHEFLAG_RO)
3545 fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
3547 fileLock->lastUpdate = (code == 0) ? time(NULL) : 0;
3549 osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
3551 lock_ObtainWrite(&cm_scacheLock);
3552 cm_HoldSCacheNoLock(scp);
3553 fileLock->scp = scp;
3554 osi_QAdd(&cm_allFileLocks, &fileLock->q);
3555 lock_ReleaseWrite(&cm_scacheLock);
3561 if (IS_LOCK_ACCEPTED(fileLock)) {
3562 if(Which == LockRead)
3565 scp->exclusiveLocks++;
3568 osi_Log1(afsd_logp, "cm_Lock Lock added 0x%x", (long) fileLock);
3569 osi_Log4(afsd_logp, " scp[0x%x] exclusives[%d] shared[%d] serverLock[%d]",
3570 scp, scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3576 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
3578 /* Called with scp->mx held */
3579 long cm_UnlockByKey(cm_scache_t * scp,
3589 cm_file_lock_t *fileLock;
3590 osi_queue_t *q, *qn;
3591 struct rx_connection * callp;
3594 osi_Log3(afsd_logp, "cm_UnlockByKey scp 0x%x key 0x%x:%x",
3595 (long) scp, (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
3597 lock_ObtainWrite(&cm_scacheLock);
3599 for(q = scp->fileLocksH; q; q = qn) {
3602 fileLock = (cm_file_lock_t *)
3603 ((char *) q - offsetof(cm_file_lock_t, fileq));
3606 osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
3607 fileLock, (unsigned long) fileLock->range.offset, (unsigned long) fileLock->range.length,
3608 fileLock->lockType);
3609 osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
3610 (unsigned long)(fileLock->key >> 32),
3611 (unsigned long)(fileLock->key & 0xffffffff),
3614 if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3615 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3616 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3618 fileLock->fid.volume,
3619 fileLock->fid.vnode,
3620 fileLock->fid.unique);
3621 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3622 fileLock->scp->fid.cell,
3623 fileLock->scp->fid.volume,
3624 fileLock->scp->fid.vnode,
3625 fileLock->scp->fid.unique);
3630 if (!IS_LOCK_DELETED(fileLock) &&
3631 cm_KeyEquals(fileLock->key, key, flags)) {
3632 osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
3633 fileLock->range.offset,
3634 fileLock->range.length,
3635 fileLock->lockType);
3637 if (scp->fileLocksT == q)
3638 scp->fileLocksT = osi_QPrev(q);
3639 osi_QRemove(&scp->fileLocksH,q);
3641 if(IS_LOCK_ACCEPTED(fileLock)) {
3642 if(fileLock->lockType == LockRead)
3645 scp->exclusiveLocks--;
3648 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3650 cm_ReleaseUser(fileLock->userp);
3651 cm_ReleaseSCacheNoLock(scp);
3653 fileLock->userp = NULL;
3654 fileLock->scp = NULL;
3660 lock_ReleaseWrite(&cm_scacheLock);
3662 if(n_unlocks == 0) {
3663 osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
3664 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3665 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3670 osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
3672 osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
3673 osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
3675 if (scp->flags & CM_SCACHEFLAG_RO) {
3676 osi_Log0(afsd_logp, " Skipping server lock for RO scp");
3680 /* Ideally we would go through the rest of the locks to determine
3681 * if one or more locks that were formerly in WAITUNLOCK can now
3682 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3683 * scp->sharedLocks accordingly. However, the retrying of locks
3684 * in that manner is done cm_RetryLock() manually.
3687 if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
3691 /* The serverLock should be downgraded to LockRead */
3692 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
3694 tfid.Volume = scp->fid.volume;
3695 tfid.Vnode = scp->fid.vnode;
3696 tfid.Unique = scp->fid.unique;
3699 lock_ReleaseMutex(&scp->mx);
3701 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
3704 code = cm_Conn(&cfid, userp, reqp, &connp);
3708 callp = cm_GetRxConn(connp);
3709 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3710 rx_PutConnection(callp);
3712 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3714 code = cm_MapRPCError(code, reqp);
3717 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3719 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3721 lock_ObtainMutex(&scp->mx);
3724 /* so we couldn't release it. Just let the lock be for now */
3728 scp->serverLock = -1;
3731 tfid.Volume = scp->fid.volume;
3732 tfid.Vnode = scp->fid.vnode;
3733 tfid.Unique = scp->fid.unique;
3736 osi_Log3(afsd_logp, "CALL SetLock scp 0x%x from %d to %d", (long) scp, (int) scp->serverLock, LockRead);
3738 lock_ReleaseMutex(&scp->mx);
3742 code = cm_Conn(&cfid, userp, reqp, &connp);
3746 callp = cm_GetRxConn(connp);
3747 code = RXAFS_SetLock(callp, &tfid, LockRead,
3750 rx_PutConnection(callp);
3752 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3756 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3758 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3761 lock_ObtainMutex(&scp->mx);
3764 scp->serverLock = LockRead;
3766 if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
3767 (scp->serverLock == -1)) {
3769 cm_LockMarkSCacheLost(scp);
3773 /* failure here has no bearing on the return value of
3777 } else if(scp->serverLock != (-1) && scp->exclusiveLocks == 0 && scp->sharedLocks == 0) {
3780 /* The serverLock should be released entirely */
3782 tfid.Volume = scp->fid.volume;
3783 tfid.Vnode = scp->fid.vnode;
3784 tfid.Unique = scp->fid.unique;
3787 lock_ReleaseMutex(&scp->mx);
3789 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
3792 code = cm_Conn(&cfid, userp, reqp, &connp);
3796 callp = cm_GetRxConn(connp);
3797 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3798 rx_PutConnection(callp);
3800 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3802 code = cm_MapRPCError(code, reqp);
3805 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3807 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3809 lock_ObtainMutex(&scp->mx);
3812 scp->serverLock = (-1);
3817 osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
3818 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
3819 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
3824 long cm_Unlock(cm_scache_t *scp,
3825 unsigned char sLockType,
3826 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3832 int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
3836 cm_file_lock_t *fileLock;
3838 int release_userp = FALSE;
3839 struct rx_connection * callp;
3841 osi_Log4(afsd_logp, "cm_Unlock scp 0x%x type 0x%x offset %d length %d",
3842 (long) scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
3843 osi_Log2(afsd_logp, "... key 0x%x:%x",
3844 (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
3846 lock_ObtainRead(&cm_scacheLock);
3848 for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
3849 fileLock = (cm_file_lock_t *)
3850 ((char *) q - offsetof(cm_file_lock_t, fileq));
3853 if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
3854 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
3855 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3857 fileLock->fid.volume,
3858 fileLock->fid.vnode,
3859 fileLock->fid.unique);
3860 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
3861 fileLock->scp->fid.cell,
3862 fileLock->scp->fid.volume,
3863 fileLock->scp->fid.vnode,
3864 fileLock->scp->fid.unique);
3868 if (!IS_LOCK_DELETED(fileLock) &&
3869 fileLock->key == key &&
3870 fileLock->range.offset == LOffset.QuadPart &&
3871 fileLock->range.length == LLength.QuadPart) {
3877 osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
3879 lock_ReleaseRead(&cm_scacheLock);
3881 return CM_ERROR_WOULDBLOCK; /* how is this an appropriate error code? */
3884 /* discard lock record */
3885 if (scp->fileLocksT == q)
3886 scp->fileLocksT = osi_QPrev(q);
3887 osi_QRemove(&scp->fileLocksH, q);
3889 if(IS_LOCK_ACCEPTED(fileLock)) {
3890 if(fileLock->lockType == LockRead)
3893 scp->exclusiveLocks--;
3896 lock_ReleaseRead(&cm_scacheLock);
3899 * Don't delete it here; let the daemon delete it, to simplify
3900 * the daemon's traversal of the list.
3903 lock_ObtainWrite(&cm_scacheLock);
3904 fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
3905 if (userp != NULL) {
3906 cm_ReleaseUser(fileLock->userp);
3908 userp = fileLock->userp;
3909 release_userp = TRUE;
3911 fileLock->userp = NULL;
3912 cm_ReleaseSCacheNoLock(scp);
3913 fileLock->scp = NULL;
3914 lock_ReleaseWrite(&cm_scacheLock);
3916 if (scp->flags & CM_SCACHEFLAG_RO) {
3917 osi_Log0(afsd_logp, " Skipping server locks for RO scp");
3921 /* Ideally we would go through the rest of the locks to determine
3922 * if one or more locks that were formerly in WAITUNLOCK can now
3923 * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
3924 * scp->sharedLocks accordingly. However, the retrying of locks
3925 * in that manner is done cm_RetryLock() manually.
3928 if (scp->serverLock == LockWrite && scp->exclusiveLocks == 0 && scp->sharedLocks > 0) {
3932 /* The serverLock should be downgraded to LockRead */
3933 osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
3935 tfid.Volume = scp->fid.volume;
3936 tfid.Vnode = scp->fid.vnode;
3937 tfid.Unique = scp->fid.unique;
3940 lock_ReleaseMutex(&scp->mx);
3942 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
3945 code = cm_Conn(&cfid, userp, reqp, &connp);
3949 callp = cm_GetRxConn(connp);
3950 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3951 rx_PutConnection(callp);
3953 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3956 code = cm_MapRPCError(code, reqp);
3959 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
3961 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
3963 lock_ObtainMutex(&scp->mx);
3966 /* so we couldn't release it. Just let the lock be for now */
3970 scp->serverLock = -1;
3973 tfid.Volume = scp->fid.volume;
3974 tfid.Vnode = scp->fid.vnode;
3975 tfid.Unique = scp->fid.unique;
3978 osi_Log3(afsd_logp, "CALL SetLock scp 0x%x from %d to %d", (long) scp, (int) scp->serverLock, LockRead);
3980 lock_ReleaseMutex(&scp->mx);
3984 code = cm_Conn(&cfid, userp, reqp, &connp);
3988 callp = cm_GetRxConn(connp);
3989 code = RXAFS_SetLock(callp, &tfid, LockRead,
3992 rx_PutConnection(callp);
3994 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3998 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4000 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4003 lock_ObtainMutex(&scp->mx);
4006 scp->serverLock = LockRead;
4008 if ((scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4009 (scp->serverLock == -1)) {
4011 cm_LockMarkSCacheLost(scp);
4015 /* failure here has no bearing on the return value of
4019 } else if(scp->serverLock != (-1) && scp->exclusiveLocks == 0 && scp->sharedLocks == 0) {
4022 /* The serverLock should be released entirely */
4024 tfid.Volume = scp->fid.volume;
4025 tfid.Vnode = scp->fid.vnode;
4026 tfid.Unique = scp->fid.unique;
4029 lock_ReleaseMutex(&scp->mx);
4031 osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
4034 code = cm_Conn(&cfid, userp, reqp, &connp);
4038 callp = cm_GetRxConn(connp);
4039 code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4040 rx_PutConnection(callp);
4042 } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4044 code = cm_MapRPCError(code, reqp);
4047 osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
4049 osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
4051 lock_ObtainMutex(&scp->mx);
4054 scp->serverLock = (-1);
4059 cm_ReleaseUser(userp);
4063 osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
4064 osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
4065 scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4070 /* called with scp->mx held */
4071 static void cm_LockMarkSCacheLost(cm_scache_t * scp)
4073 cm_file_lock_t *fileLock;
4076 /* cm_scacheLock needed because we are modifying
4078 lock_ObtainWrite(&cm_scacheLock);
4080 for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
4082 (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4084 if(IS_LOCK_ACTIVE(fileLock)) {
4085 fileLock->flags |= CM_FILELOCK_FLAG_LOST;
4089 scp->serverLock = -1;
4090 lock_ReleaseWrite(&cm_scacheLock);
4093 /* Called with no relevant locks held */
4094 void cm_CheckLocks()
4096 osi_queue_t *q, *nq;
4097 cm_file_lock_t *fileLock;
4103 struct rx_connection * callp;
4106 lock_ObtainWrite(&cm_scacheLock);
4108 cm_lockRefreshCycle++;
4110 osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
4112 for(q = cm_allFileLocks; q; q = nq) {
4113 fileLock = (cm_file_lock_t *) q;
4117 if (IS_LOCK_DELETED(fileLock)) {
4119 osi_QRemove(&cm_allFileLocks, q);
4120 cm_PutFileLock(fileLock);
4122 } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
4124 scp = fileLock->scp;
4125 osi_assert(scp != NULL);
4126 cm_HoldSCacheNoLock(scp);
4129 if(cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
4130 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
4131 osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4133 fileLock->fid.volume,
4134 fileLock->fid.vnode,
4135 fileLock->fid.unique);
4136 osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
4137 fileLock->scp->fid.cell,
4138 fileLock->scp->fid.volume,
4139 fileLock->scp->fid.vnode,
4140 fileLock->scp->fid.unique);
4144 /* Server locks are extended once per scp per refresh
4146 if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
4148 int scp_done = FALSE;
4150 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
4152 lock_ReleaseWrite(&cm_scacheLock);
4153 lock_ObtainMutex(&scp->mx);
4155 /* did the lock change while we weren't holding the lock? */
4156 if (!IS_LOCK_ACTIVE(fileLock))
4157 goto post_syncopdone;
4159 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
4160 CM_SCACHESYNC_NEEDCALLBACK
4161 | CM_SCACHESYNC_GETSTATUS
4162 | CM_SCACHESYNC_LOCK);
4165 osi_Log1(smb_logp, "cm_CheckLocks SyncOp failure code 0x%x", code);
4166 goto post_syncopdone;
4169 /* cm_SyncOp releases scp->mx during which the lock
4170 may get released. */
4171 if (!IS_LOCK_ACTIVE(fileLock))
4172 goto pre_syncopdone;
4174 if(scp->serverLock != -1) {
4180 tfid.Volume = scp->fid.volume;
4181 tfid.Vnode = scp->fid.vnode;
4182 tfid.Unique = scp->fid.unique;
4184 userp = fileLock->userp;
4186 osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%x for scp=0x%x with lock %d",
4189 (int) scp->serverLock);
4191 lock_ReleaseMutex(&scp->mx);
4194 code = cm_Conn(&cfid, userp,
4199 callp = cm_GetRxConn(connp);
4200 code = RXAFS_ExtendLock(callp, &tfid,
4202 rx_PutConnection(callp);
4204 osi_Log1(afsd_logp, " ExtendLock returns %d", code);
4206 } while (cm_Analyze(connp, userp, &req,
4207 &cfid, &volSync, NULL, NULL,
4210 code = cm_MapRPCError(code, &req);
4212 lock_ObtainMutex(&scp->mx);
4215 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
4216 if (code == EINVAL || code == CM_ERROR_INVAL)
4217 cm_LockMarkSCacheLost(scp);
4219 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
4222 /* interestingly, we have found an active lock
4223 belonging to an scache that has no
4225 cm_LockMarkSCacheLost(scp);
4232 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4235 lock_ReleaseMutex(&scp->mx);
4237 lock_ObtainWrite(&cm_scacheLock);
4240 fileLock->lastUpdate = time(NULL);
4244 scp->lastRefreshCycle = cm_lockRefreshCycle;
4247 /* we have already refreshed the locks on this scp */
4248 fileLock->lastUpdate = time(NULL);
4251 cm_ReleaseSCacheNoLock(scp);
4253 } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
4254 /* TODO: Check callbacks */
4258 lock_ReleaseWrite(&cm_scacheLock);
4261 /* NOT called with scp->mx held. */
4262 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
4269 cm_file_lock_t *fileLock;
4272 struct rx_connection * callp;
4275 if (client_is_dead) {
4276 code = CM_ERROR_TIMEDOUT;
4280 lock_ObtainRead(&cm_scacheLock);
4282 /* if the lock has already been granted, then we have nothing to do */
4283 if(IS_LOCK_ACTIVE(oldFileLock)) {
4284 lock_ReleaseRead(&cm_scacheLock);
4288 /* we can't do anything with lost or deleted locks at the moment. */
4289 if(IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
4290 code = CM_ERROR_BADFD;
4291 lock_ReleaseRead(&cm_scacheLock);
4295 scp = oldFileLock->scp;
4297 osi_assert(scp != NULL);
4299 lock_ReleaseRead(&cm_scacheLock);
4300 lock_ObtainMutex(&scp->mx);
4301 lock_ObtainWrite(&cm_scacheLock);
4303 /* Check if we already have a sufficient server lock to allow this
4304 lock to go through */
4305 if(IS_LOCK_WAITLOCK(oldFileLock) &&
4306 (scp->serverLock == oldFileLock->lockType ||
4307 scp->serverLock == LockWrite)) {
4309 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4311 lock_ReleaseWrite(&cm_scacheLock);
4312 lock_ReleaseMutex(&scp->mx);
4317 if(IS_LOCK_WAITUNLOCK(oldFileLock)) {
4319 /* check if the conflicting locks have dissappeared already */
4321 for(q = scp->fileLocksH; q; q = osi_QNext(q)) {
4323 fileLock = (cm_file_lock_t *)
4324 ((char *) q - offsetof(cm_file_lock_t, fileq));
4326 /* a oldFileLock can only depend on locks that are ahead
4327 of it in the queue. If we came this far, then all
4329 if(fileLock == oldFileLock) {
4333 if(IS_LOCK_LOST(fileLock)
4335 && fileLock->key == oldFileLock->key
4338 code = CM_ERROR_BADFD;
4339 oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
4343 /* we don't need to check for deleted locks here since deleted
4344 locks are dequeued from fileLocks */
4345 if(INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
4347 if(oldFileLock->lockType != LockRead ||
4348 fileLock->lockType != LockRead) {
4349 code = CM_ERROR_WOULDBLOCK;
4357 lock_ReleaseWrite(&cm_scacheLock);
4358 lock_ReleaseMutex(&scp->mx);
4363 /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
4364 If it is WAITUNLOCK, then we didn't find any conflicting lock
4365 but we haven't verfied whether the serverLock is sufficient to
4366 assert it. If it is WAITLOCK, then the serverLock is
4367 insufficient to assert it. Eitherway, we are ready to accept
4368 the lock as either ACTIVE or WAITLOCK depending on the
4371 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
4373 if (scp->serverLock == oldFileLock->lockType ||
4374 (oldFileLock->lockType == LockRead &&
4375 scp->serverLock == LockWrite)) {
4377 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4379 lock_ReleaseWrite(&cm_scacheLock);
4380 lock_ReleaseMutex(&scp->mx);
4388 oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
4392 code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
4393 CM_SCACHESYNC_NEEDCALLBACK
4394 | CM_SCACHESYNC_GETSTATUS
4395 | CM_SCACHESYNC_LOCK);
4397 osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
4398 lock_ReleaseWrite(&cm_scacheLock);
4399 goto post_syncopdone;
4402 if(!IS_LOCK_WAITLOCK(oldFileLock))
4403 goto pre_syncopdone;
4405 tfid.Volume = scp->fid.volume;
4406 tfid.Vnode = scp->fid.vnode;
4407 tfid.Unique = scp->fid.unique;
4409 userp = oldFileLock->userp;
4411 #ifndef AGGRESSIVE_LOCKS
4412 newLock = oldFileLock->lockType;
4414 newLock = LockWrite;
4417 osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
4419 lock_ReleaseWrite(&cm_scacheLock);
4420 lock_ReleaseMutex(&scp->mx);
4423 code = cm_Conn(&cfid, userp, &req, &connp);
4427 callp = cm_GetRxConn(connp);
4428 code = RXAFS_SetLock(callp, &tfid, newLock,
4430 rx_PutConnection(callp);
4432 } while (cm_Analyze(connp, userp, &req,
4435 code = cm_MapRPCError(code, &req);
4438 osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4440 osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4443 lock_ObtainMutex(&scp->mx);
4445 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
4451 if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
4452 if (scp->fileLocksT == &oldFileLock->fileq)
4453 scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
4454 osi_QRemove(&scp->fileLocksH, &oldFileLock->fileq);
4455 } else if (code == 0 && IS_LOCK_WAITLOCK(oldFileLock)) {
4456 scp->serverLock = newLock;
4458 lock_ReleaseMutex(&scp->mx);
4460 lock_ObtainWrite(&cm_scacheLock);
4462 oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
4463 } else if (code != CM_ERROR_WOULDBLOCK) {
4464 oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
4465 cm_ReleaseUser(oldFileLock->userp);
4466 oldFileLock->userp = NULL;
4467 cm_ReleaseSCacheNoLock(scp);
4468 oldFileLock->scp = NULL;
4470 lock_ReleaseWrite(&cm_scacheLock);
4475 cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
4477 return (((cm_key_t) process_id) << 32) |
4478 (((cm_key_t) session_id) << 16) |
4479 (((cm_key_t) file_id));
4482 static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
4484 if (flags & CM_UNLOCK_BY_FID) {
4485 return ((k1 & 0xffffffff) == (k2 & 0xffffffff));