#include <afs/param.h>
#include <afs/stds.h>
-#ifndef DJGPP
#include <windows.h>
#include <winsock2.h>
-#endif /* !DJGPP */
#include <stddef.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
+#include <errno.h>
#include <osi.h>
#include "afsd.h"
-
-/* Used by cm_FollowMountPoint */
-#define RWVOL 0
-#define ROVOL 1
-#define BACKVOL 2
+#include "cm_btree.h"
#ifdef DEBUG
extern void afsi_log(char *pattern, ...);
#endif
+int cm_enableServerLocks = 1;
+
+int cm_followBackupPath = 0;
+
/*
* Case-folding array. This was constructed by inspecting of SMBtrace output.
* I do not know anything more about it.
int cm_Is8Dot3(char *namep)
{
int sawDot = 0;
- int sawUpper = 0, sawLower = 0;
unsigned char tc;
int charCount = 0;
}
if (cm_LegalChars[tc] == 0)
return 0;
- if (tc >= 'A' && tc <= 'Z')
- sawUpper = 1;
- else if (tc >= 'a' && tc <= 'z')
- sawLower = 1;
charCount++;
if (!sawDot && charCount > 8)
/* more than 8 chars in name */
/* more than 3 chars in extension */
return 0;
}
- /*
- * Used to check that all characters were the same case.
- * This doesn't help 16-bit apps, and meanwhile it causes the
- * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
- *
- if (sawUpper && sawLower)
- return 0;
- */
return 1;
}
};
int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
-void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
+void cm_Gen8Dot3NameInt(const char * longname, cm_dirFid_t * pfid,
+ char *shortName, char **shortNameEndp)
{
char number[12];
int i, nsize = 0;
- int vnode = ntohl(dep->fid.vnode);
+ int vnode = ntohl(pfid->vnode);
char *lastDot;
int validExtension = 0;
- char tc, *temp, *name;
+ char tc, *temp;
+ const char *name;
/* Unparse the file's vnode number to get a "uniquifier" */
do {
* Look for valid extension. There has to be a dot, and
* at least one of the characters following has to be legal.
*/
- lastDot = strrchr(dep->name, '.');
+ lastDot = strrchr(longname, '.');
if (lastDot) {
temp = lastDot; temp++;
while (tc = *temp++)
break;
if (tc)
validExtension = 1;
- }
+ }
/* Copy name characters */
- name = dep->name;
- for (i = 0, name = dep->name;
+ for (i = 0, name = longname;
i < (7 - nsize) && name != lastDot; ) {
tc = *name++;
long code;
rights = 0;
- if (openMode != 1) rights |= PRSFS_READ;
- if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
+ if (openMode != 1)
+ rights |= PRSFS_READ;
+ if (openMode == 1 || openMode == 2 || trunc)
+ rights |= PRSFS_WRITE;
lock_ObtainMutex(&scp->mx);
code = cm_SyncOp(scp, NULL, userp, reqp, rights,
CM_SCACHESYNC_GETSTATUS
- | CM_SCACHESYNC_NEEDCALLBACK);
+ | CM_SCACHESYNC_NEEDCALLBACK
+ | CM_SCACHESYNC_LOCK);
+
+ if (code == 0 &&
+ ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
+ scp->fileType == CM_SCACHETYPE_FILE) {
+
+ cm_key_t key;
+ unsigned int sLockType;
+ LARGE_INTEGER LOffset, LLength;
+
+ /* Check if there's some sort of lock on the file at the
+ moment. */
+
+ key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
+
+ if (rights & PRSFS_WRITE)
+ sLockType = 0;
+ else
+ sLockType = LOCKING_ANDX_SHARED_LOCK;
+
+ LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
+ LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
+ LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
+ LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
+
+ code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
+
+ if (code == 0) {
+ cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
+ } else {
+ /* In this case, we allow the file open to go through even
+ though we can't enforce mandatory locking on the
+ file. */
+ if (code == CM_ERROR_NOACCESS &&
+ !(rights & PRSFS_WRITE))
+ code = 0;
+ else {
+ switch (code) {
+ case CM_ERROR_ALLOFFLINE:
+ case CM_ERROR_ALLDOWN:
+ case CM_ERROR_ALLBUSY:
+ case CM_ERROR_TIMEDOUT:
+ case CM_ERROR_RETRY:
+ case CM_ERROR_WOULDBLOCK:
+ break;
+ default:
+ code = CM_ERROR_SHARING_VIOLATION;
+ }
+ }
+ }
+
+ } else if (code != 0) {
+ goto _done;
+ }
+
+ cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
+ _done:
+
lock_ReleaseMutex(&scp->mx);
return code;
/* return success if we can open this file in this mode */
long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
- unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
+ unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
+ cm_lock_data_t **ldpp)
{
long rights;
long code;
+ osi_assertx(ldpp != NULL, "null cm_lock_data_t");
+ *ldpp = NULL;
+
/* Always allow delete; the RPC will tell us if it's OK */
if (desiredAccess == DELETE)
return 0;
rights = 0;
if (desiredAccess & AFS_ACCESS_READ)
- rights |= PRSFS_READ;
+ rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
- if ((desiredAccess & AFS_ACCESS_WRITE)
- || createDisp == 4)
+ /* We used to require PRSFS_WRITE if createDisp was 4
+ (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
+ However, we don't need to do that since the existence of the
+ scp implies that we don't need to create it. */
+ if (desiredAccess & AFS_ACCESS_WRITE)
rights |= PRSFS_WRITE;
lock_ObtainMutex(&scp->mx);
code = cm_SyncOp(scp, NULL, userp, reqp, rights,
CM_SCACHESYNC_GETSTATUS
- | CM_SCACHESYNC_NEEDCALLBACK);
- lock_ReleaseMutex(&scp->mx);
+ | CM_SCACHESYNC_NEEDCALLBACK
+ | CM_SCACHESYNC_LOCK);
/*
* If the open will fail because the volume is readonly, then we will
if (code == CM_ERROR_READONLY)
code = CM_ERROR_NOACCESS;
+ if (code == 0 &&
+ ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
+ scp->fileType == CM_SCACHETYPE_FILE) {
+ cm_key_t key;
+ unsigned int sLockType;
+ LARGE_INTEGER LOffset, LLength;
+
+ /* Check if there's some sort of lock on the file at the
+ moment. */
+
+ key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
+ if (rights & PRSFS_WRITE)
+ sLockType = 0;
+ else
+ sLockType = LOCKING_ANDX_SHARED_LOCK;
+
+ /* single byte lock at offset 0x0100 0000 0000 0000 */
+ LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
+ LOffset.LowPart = CM_FLSHARE_OFFSET_LOW;
+ LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
+ LLength.LowPart = CM_FLSHARE_LENGTH_LOW;
+
+ code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
+
+ if (code == 0) {
+ (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
+ if (!*ldpp) {
+ code = ENOMEM;
+ goto _done;
+ }
+
+ (*ldpp)->key = key;
+ (*ldpp)->sLockType = sLockType;
+ (*ldpp)->LOffset.HighPart = LOffset.HighPart;
+ (*ldpp)->LOffset.LowPart = LOffset.LowPart;
+ (*ldpp)->LLength.HighPart = LLength.HighPart;
+ (*ldpp)->LLength.LowPart = LLength.LowPart;
+ } else {
+ /* In this case, we allow the file open to go through even
+ though we can't enforce mandatory locking on the
+ file. */
+ if (code == CM_ERROR_NOACCESS &&
+ !(rights & PRSFS_WRITE))
+ code = 0;
+ else {
+ switch (code) {
+ case CM_ERROR_ALLOFFLINE:
+ case CM_ERROR_ALLDOWN:
+ case CM_ERROR_ALLBUSY:
+ case CM_ERROR_TIMEDOUT:
+ case CM_ERROR_RETRY:
+ case CM_ERROR_WOULDBLOCK:
+ break;
+ default:
+ code = CM_ERROR_SHARING_VIOLATION;
+ }
+ }
+ }
+ } else if (code != 0) {
+ goto _done;
+ }
+
+ cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
+ _done:
+ lock_ReleaseMutex(&scp->mx);
+
return code;
}
+extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
+ cm_lock_data_t ** ldpp)
+{
+ if (*ldpp) {
+ lock_ObtainMutex(&scp->mx);
+ cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
+ (*ldpp)->key, userp, reqp);
+ lock_ReleaseMutex(&scp->mx);
+ free(*ldpp);
+ *ldpp = NULL;
+ }
+ return 0;
+}
/*
* When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
* done in three steps:
long code;
osi_hyper_t thyper;
cm_buf_t *bufferp;
- cm_dirEntry_t *dep;
+ cm_dirEntry_t *dep = 0;
unsigned short *hashTable;
unsigned int i, idx;
int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
/* First check permissions */
- lock_ObtainMutex(&dscp->mx);
- code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
- CM_SCACHESYNC_GETSTATUS
- | CM_SCACHESYNC_NEEDCALLBACK);
- lock_ReleaseMutex(&dscp->mx);
+ lock_ObtainMutex(&scp->mx);
+ code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
+ CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
+ cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+ lock_ReleaseMutex(&scp->mx);
if (code)
return code;
lock_ReleaseMutex(&scp->mx);
lock_ObtainMutex(&bufferp->mx);
lock_ObtainMutex(&scp->mx);
+ cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
if (code)
break;
}
{
char *tp;
long code;
- cm_dirEntry_t *dep;
+ cm_dirEntry_t *dep = 0;
cm_buf_t *bufferp;
long temp;
osi_hyper_t dirLength;
lock_ObtainMutex(&scp->mx);
code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
- if (code) {
- lock_ReleaseMutex(&scp->mx);
+ lock_ReleaseMutex(&scp->mx);
+ if (code)
return code;
- }
- if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
- lock_ReleaseMutex(&scp->mx);
+ if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
return CM_ERROR_NOTDIR;
- }
if (retscp) /* if this is a lookup call */
{
cm_lookupSearch_t* sp = parmp;
+ if (
#ifdef AFS_FREELANCE_CLIENT
/* Freelance entries never end up in the DNLC because they
* do not have an associated cm_server_t
*/
- if ( !(cm_freelanceEnabled &&
+ !(cm_freelanceEnabled &&
sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
- sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
-#endif /* AFS_FREELANCE_CLIENT */
- {
- int casefold = sp->caseFold;
- sp->caseFold = 0; /* we have a strong preference for exact matches */
- if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
+ sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+#else /* !AFS_FREELANCE_CLIENT */
+ TRUE
+#endif
+ )
{
+ int casefold = sp->caseFold;
+ sp->caseFold = 0; /* we have a strong preference for exact matches */
+ if ( *retscp = cm_dnlcLookup(scp, sp)) /* dnlc hit */
+ {
+ sp->caseFold = casefold;
+ return 0;
+ }
sp->caseFold = casefold;
- lock_ReleaseMutex(&scp->mx);
- return 0;
+
+ /* see if we can find it using the directory hash tables.
+ we can only do exact matches, since the hash is case
+ sensitive. */
+ {
+ cm_dirOp_t dirop;
+#ifdef USE_BPLUS
+ int usedBplus = 0;
+#endif
+
+ code = ENOENT;
+
+ code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
+ if (code == 0) {
+
+#ifdef USE_BPLUS
+ code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
+ if (code != EINVAL)
+ usedBplus = 1;
+ else
+#endif
+ code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
+
+ cm_EndDirOp(&dirop);
+ }
+
+ if (code == 0) {
+ /* found it */
+ sp->found = TRUE;
+ sp->ExactFound = TRUE;
+ *retscp = NULL; /* force caller to call cm_GetSCache() */
+ return 0;
+ }
+#ifdef USE_BPLUS
+ if (usedBplus) {
+ if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
+ /* found it */
+ sp->found = TRUE;
+ sp->ExactFound = FALSE;
+ *retscp = NULL; /* force caller to call cm_GetSCache() */
+ return 0;
+ }
+
+ return CM_ERROR_BPLUS_NOMATCH;
+ }
+#endif
+ }
}
- sp->caseFold = casefold;
- }
}
/*
*/
dirLength = scp->length;
- lock_ReleaseMutex(&scp->mx);
-
bufferp = NULL;
bufferOffset.LowPart = bufferOffset.HighPart = 0;
if (startOffsetp)
break;
}
+#ifdef AFSIFS
+ /* for the IFS version, we bulkstat the dirents because this
+ routine is used in place of smb_ReceiveCoreSearchDir. our
+ other option is to modify smb_ReceiveCoreSearchDir itself,
+ but this seems to be the proper use for cm_ApplyDir. */
+ lock_ObtainMutex(&scp->mx);
+ if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
+ && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
+ {
+ scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
+ code = cm_TryBulkStat(scp, &thyper, userp, reqp);
+ scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
+ scp->bulkStatProgress = thyper;
+ }
+ lock_ReleaseMutex(&scp->mx);
+#endif
+
lock_ObtainMutex(&bufferp->mx);
bufferOffset = thyper;
lock_ReleaseMutex(&scp->mx);
break;
}
+ cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
if (cm_HaveBuffer(scp, bufferp, 1)) {
lock_ReleaseMutex(&scp->mx);
match = cm_stricmp(matchName, sp->searchNamep);
else
match = strcmp(matchName, sp->searchNamep);
- }
+ }
if (match != 0)
return 0;
sp->found = 1;
- if(!sp->caseFold)
+ if (!sp->caseFold)
sp->ExactFound = 1;
if (!sp->caseFold || matchName == shortName) {
lock_ReleaseRead(&scp->bufCreateLock);
lock_ObtainMutex(&scp->mx);
- if (code) {
+ if (code)
return code;
- }
+
while (1) {
code = cm_SyncOp(scp, bufp, userp, reqp, 0,
CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
- if (code) {
+ if (code)
goto done;
- }
+
+ cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
if (cm_HaveBuffer(scp, bufp, 0))
break;
/* otherwise load buffer */
code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
- if (code) {
+ if (code)
goto done;
- }
}
/* locked, has callback, has valid data in buffer */
- if ((tlen = scp->length.LowPart) > 1000)
+ if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1)
return CM_ERROR_TOOBIG;
if (tlen <= 0) {
code = CM_ERROR_INVAL;
return code;
}
+
/* called with a locked scp and chases the mount point, yielding outScpp.
* scp remains locked, just for simplicity of describing the interface.
*/
long code;
char *cp;
char *mpNamep;
- cm_volume_t *volp;
+ cm_volume_t *volp = NULL;
cm_cell_t *cellp;
char mtType;
cm_fid_t tfid;
size_t vnLength;
- int type;
+ int targetType;
if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
tfid = scp->mountRootFid;
/* parse the volume name */
mpNamep = scp->mountPointStringp;
- osi_assert(mpNamep[0]);
- tlen = strlen(scp->mountPointStringp);
+ if (!mpNamep[0])
+ return CM_ERROR_NOSUCHPATH;
+ tlen = (int)strlen(scp->mountPointStringp);
mtType = *scp->mountPointStringp;
cellNamep = malloc(tlen);
volNamep = malloc(tlen);
strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
strcpy(volNamep, cp+1);
/* now look up the cell */
+ lock_ReleaseMutex(&scp->mx);
cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
+ lock_ObtainMutex(&scp->mx);
}
else {
/* normal mt pt */
strcpy(volNamep, mpNamep+1);
- cellp = cm_FindCellByID(scp->fid.cell);
+ cellp = cm_FindCellByID(scp->fid.cell, 0);
}
if (!cellp) {
vnLength = strlen(volNamep);
if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
- type = BACKVOL;
+ targetType = BACKVOL;
else if (vnLength >= 10
&& strcmp(volNamep + vnLength - 9, ".readonly") == 0)
- type = ROVOL;
+ targetType = ROVOL;
else
- type = RWVOL;
+ targetType = RWVOL;
/* check for backups within backups */
- if (type == BACKVOL
+ if (targetType == BACKVOL
&& (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
== CM_SCACHEFLAG_RO) {
code = CM_ERROR_NOSUCHVOLUME;
/* now we need to get the volume */
lock_ReleaseMutex(&scp->mx);
- code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
+ if (cm_VolNameIsID(volNamep)) {
+ code = cm_GetVolumeByID(cellp, atoi(volNamep), userp, reqp,
+ CM_GETVOL_FLAG_CREATE, &volp);
+ } else {
+ code = cm_GetVolumeByName(cellp, volNamep, userp, reqp,
+ CM_GETVOL_FLAG_CREATE, &volp);
+ }
lock_ObtainMutex(&scp->mx);
if (code == 0) {
lock_ReleaseMutex(&volp->mx);
scp->mountRootFid.cell = cellp->cellID;
+
+ /* if the mt pt originates in a .backup volume (not a .readonly)
+ * and FollowBackupPath is active, and if there is a .backup
+ * volume for the target, then use the .backup of the target
+ * instead of the read-write.
+ */
+ if (cm_followBackupPath &&
+ volp->bk.ID != 0 &&
+ (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
+ (targetType == RWVOL || targetType == ROVOL && volp->ro.ID == 0)
+ ) {
+ targetType = BACKVOL;
+ }
/* if the mt pt is in a read-only volume (not just a
* backup), and if there is a read-only volume for the
- * target, and if this is a type '#' mount point, use
+ * target, and if this is a targetType '#' mount point, use
* the read-only, otherwise use the one specified.
*/
- if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
- && volp->roID != 0 && type == RWVOL)
- type = ROVOL;
- if (type == ROVOL)
- scp->mountRootFid.volume = volp->roID;
- else if (type == BACKVOL)
- scp->mountRootFid.volume = volp->bkID;
+ else if (mtType == '#' && targetType == RWVOL &&
+ (scp->flags & CM_SCACHEFLAG_PURERO) &&
+ volp->ro.ID != 0) {
+ targetType = ROVOL;
+ }
+ if (targetType == ROVOL)
+ scp->mountRootFid.volume = volp->ro.ID;
+ else if (targetType == BACKVOL)
+ scp->mountRootFid.volume = volp->bk.ID;
else
- scp->mountRootFid.volume = volp->rwID;
+ scp->mountRootFid.volume = volp->rw.ID;
/* the rest of the fid is a magic number */
scp->mountRootFid.vnode = 1;
}
done:
+ if (volp)
+ cm_PutVolume(volp);
free(cellNamep);
free(volNamep);
return code;
cm_lookupSearch_t rock;
int getroot;
+ memset(&rock, 0, sizeof(rock));
+
if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
&& strcmp(namep, "..") == 0) {
if (dscp->dotdotFid.volume == 0)
return CM_ERROR_NOSUCHVOLUME;
rock.fid = dscp->dotdotFid;
goto haveFid;
+ } else if (strcmp(namep, ".") == 0) {
+ rock.fid = dscp->fid;
+ goto haveFid;
+ }
+
+ if (flags & CM_FLAG_NOMOUNTCHASE) {
+ /* In this case, we should go and call cm_Dir* functions
+ directly since the following cm_ApplyDir() function will
+ not. */
+
+ cm_dirOp_t dirop;
+#ifdef USE_BPLUS
+ int usedBplus = 0;
+#endif
+
+ code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
+ if (code == 0) {
+#ifdef USE_BPLUS
+ code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
+ if (code != EINVAL)
+ usedBplus = 1;
+ else
+#endif
+ code = cm_DirLookup(&dirop, namep, &rock.fid);
+
+ cm_EndDirOp(&dirop);
+ }
+
+ if (code == 0) {
+ /* found it */
+ rock.found = TRUE;
+ goto haveFid;
+ }
+#ifdef USE_BPLUS
+ if (usedBplus) {
+ if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
+ /* found it */
+ code = 0;
+ rock.found = TRUE;
+ goto haveFid;
+ }
+
+ return CM_ERROR_BPLUS_NOMATCH;
+ }
+#endif
}
- memset(&rock, 0, sizeof(rock));
rock.fid.cell = dscp->fid.cell;
rock.fid.volume = dscp->fid.volume;
rock.searchNamep = namep;
else
return CM_ERROR_NOSUCHFILE;
}
- else { /* nonexistent dir on freelance root, so add it */
+ else if (!strchr(namep, '#') && !strchr(namep, '%') &&
+ strcmp(namep, "srvsvc") && strcmp(namep, "wkssvc") &&
+ strcmp(namep, "ipc$")) {
+ /* nonexistent dir on freelance root, so add it */
char fullname[200] = ".";
int found = 0;
osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s",
osi_LogSaveString(afsd_logp,namep));
+
+ /*
+ * There is an ugly behavior where a share name "foo" will be searched
+ * for as "fo". If the searched for name differs by an already existing
+ * symlink or mount point in the Freelance directory, do not add the
+ * new value automatically.
+ */
+
+ code = -1;
if (namep[0] == '.') {
if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
found = 1;
- if ( stricmp(&namep[1], &fullname[1]) )
+ if (!cm_FreelanceMountPointExists(fullname, 0))
+ code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell.", 1, &rock.fid);
+ if ( stricmp(&namep[1], &fullname[1]) &&
+ !cm_FreelanceMountPointExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
+ !cm_FreelanceSymlinkExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
- else
- code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
}
} else {
if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
found = 1;
- if ( stricmp(namep, fullname) )
+ if (!cm_FreelanceMountPointExists(fullname, 0))
+ code = cm_FreelanceAddMount(fullname, fullname, "root.cell.", 0, &rock.fid);
+ if ( stricmp(namep, fullname) &&
+ !cm_FreelanceMountPointExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0) &&
+ !cm_FreelanceSymlinkExists(namep, flags & CM_FLAG_DFS_REFERRAL ? 1 : 0))
code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
- else
- code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
}
}
if (!found || code < 0) { /* add mount point failed, so give up */
cm_ReleaseSCache(tscp);
return code;
}
+ cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
/* tscp is now locked */
if (!(flags & CM_FLAG_NOMOUNTCHASE)
if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
/* lock the directory entry to prevent racing callback revokes */
lock_ObtainMutex(&dscp->mx);
- if ( dscp->cbServerp && dscp->cbExpires )
+ if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
cm_dnlcEnter(dscp, namep, tscp);
lock_ReleaseMutex(&dscp->mx);
}
return -1;
/* otherwise generate the properly expanded @sys name */
- prefixCount = tp - inp;
+ prefixCount = (int)(tp - inp);
strncpy(outp, inp, prefixCount); /* copy out "a." from "a.@sys" */
outp[prefixCount] = 0; /* null terminate the "a." */
return 1;
}
+long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
+ cm_req_t *reqp, cm_scache_t ** outpScpp)
+{
+ long code = 0;
+ char cellName[CELL_MAXNAMELEN];
+ char volumeName[VL_MAXNAMELEN];
+ size_t len;
+ char * cp;
+ char * tp;
+
+ cm_cell_t * cellp = NULL;
+ cm_volume_t * volp = NULL;
+ cm_fid_t fid;
+ int volType;
+ int mountType = RWVOL;
+
+ osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
+ osi_LogSaveString(afsd_logp, namep));
+
+ if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
+ goto _exit_invalid_path;
+ }
+
+ /* namep is assumed to look like the following:
+
+ @vol:<cellname>%<volume>\0
+ or
+ @vol:<cellname>#<volume>\0
+
+ */
+
+ cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
+ tp = strchr(cp, '%');
+ if (tp == NULL)
+ tp = strchr(cp, '#');
+ if (tp == NULL ||
+ (len = tp - cp) == 0 ||
+ len > CELL_MAXNAMELEN)
+ goto _exit_invalid_path;
+ strncpy(cellName, cp, len);
+ cellName[len] = '\0';
+
+ if (*tp == '#')
+ mountType = ROVOL;
+
+ cp = tp+1; /* cp now points to volume, supposedly */
+ strncpy(volumeName, cp, VL_MAXNAMELEN-1);
+ volumeName[VL_MAXNAMELEN - 1] = 0;
+
+ /* OK, now we have the cell and the volume */
+ osi_Log2(afsd_logp, " Found cell [%s] and volume [%s]",
+ osi_LogSaveString(afsd_logp, cellName),
+ osi_LogSaveString(afsd_logp, volumeName));
+
+ cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
+ if (cellp == NULL) {
+ goto _exit_invalid_path;
+ }
+
+ len = strlen(volumeName);
+ if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
+ volType = BACKVOL;
+ else if (len >= 10 &&
+ strcmp(volumeName + len - 9, ".readonly") == 0)
+ volType = ROVOL;
+ else
+ volType = RWVOL;
+
+ if (cm_VolNameIsID(volumeName)) {
+ code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
+ CM_GETVOL_FLAG_CREATE, &volp);
+ } else {
+ code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
+ CM_GETVOL_FLAG_CREATE, &volp);
+ }
+
+ if (code != 0)
+ goto _exit_cleanup;
+
+ fid.cell = cellp->cellID;
+
+ if (volType == BACKVOL)
+ fid.volume = volp->bk.ID;
+ else if (volType == ROVOL ||
+ (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
+ fid.volume = volp->ro.ID;
+ else
+ fid.volume = volp->rw.ID;
+
+ fid.vnode = 1;
+ fid.unique = 1;
+
+ code = cm_GetSCache(&fid, outpScpp, userp, reqp);
+
+ _exit_cleanup:
+ if (volp)
+ cm_PutVolume(volp);
+
+ if (code == 0)
+ return code;
+
+ _exit_invalid_path:
+ if (flags & CM_FLAG_CHECKPATH)
+ return CM_ERROR_NOSUCHPATH;
+ else
+ return CM_ERROR_NOSUCHFILE;
+}
+
+#ifdef DEBUG_REFCOUNT
+long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
+ cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
+#else
long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
cm_req_t *reqp, cm_scache_t **outpScpp)
+#endif
{
long code;
- char tname[256];
+ char tname[AFSPATHMAX];
int sysNameIndex = 0;
- cm_scache_t *scp = 0;
+ cm_scache_t *scp = NULL;
+
+#ifdef DEBUG_REFCOUNT
+ afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
+ osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
+#endif
if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
if (flags & CM_FLAG_CHECKPATH)
return CM_ERROR_NOSUCHFILE;
}
- for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
- code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
- if (code > 0) {
- code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
- if (code == 0) {
+ if (dscp == cm_data.rootSCachep &&
+ strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
+ return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
+ }
+
+ if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
+ for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
+ code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
+ if (code > 0) {
+ code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
+#ifdef DEBUG_REFCOUNT
+ afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
+ osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
+#endif
+
+ if (code == 0) {
+ *outpScpp = scp;
+ return 0;
+ }
+ if (scp) {
+ cm_ReleaseSCache(scp);
+ scp = NULL;
+ }
+ } else {
+ code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
+#ifdef DEBUG_REFCOUNT
+ afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
+ osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
+#endif
*outpScpp = scp;
- return 0;
- }
- if (scp) {
- cm_ReleaseSCache(scp);
- scp = 0;
+ return code;
}
- } else {
- return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
}
+ } else {
+ code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
+#ifdef DEBUG_REFCOUNT
+ afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
+ osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
+#endif
+ *outpScpp = scp;
+ return code;
}
/* None of the possible sysName expansions could be found */
AFSFetchStatus newDirStatus;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
#ifdef AFS_FREELANCE_CLIENT
if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
#endif
/* make sure we don't screw up the dir status during the merge */
+ code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
+
lock_ObtainMutex(&dscp->mx);
sflags = CM_SCACHESYNC_STOREDATA;
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
lock_ReleaseMutex(&dscp->mx);
- if (code)
+ if (code) {
+ cm_EndDirOp(&dirop);
return code;
+ }
/* make the RPC */
afsFid.Volume = dscp->fid.volume;
afsFid.Vnode = dscp->fid.vnode;
afsFid.Unique = dscp->fid.unique;
- osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%x", (long) dscp);
+ osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
do {
- code = cm_Conn(&dscp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
if (code)
continue;
else
osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
+ if (dirop.scp) {
+ lock_ObtainWrite(&dirop.scp->dirlock);
+ dirop.lockType = CM_DIRLOCK_WRITE;
+ }
lock_ObtainMutex(&dscp->mx);
cm_dnlcRemove(dscp, namep);
cm_SyncOpDone(dscp, NULL, sflags);
- if (code == 0)
- cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
+ if (code == 0) {
+ cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
+ } else if (code == CM_ERROR_NOSUCHFILE) {
+ /* windows would not have allowed the request to delete the file
+ * if it did not believe the file existed. therefore, we must
+ * have an inconsistent view of the world.
+ */
+ dscp->cbServerp = NULL;
+ }
lock_ReleaseMutex(&dscp->mx);
+ if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirDeleteEntry(&dirop, namep);
+#ifdef USE_BPLUS
+ cm_BPlusDirDeleteEntry(&dirop, namep);
+#endif
+ }
+ cm_EndDirOp(&dirop);
+
return code;
}
/* read the link data */
lock_ReleaseMutex(&linkScp->mx);
thyper.LowPart = thyper.HighPart = 0;
+ lock_ObtainRead(&linkScp->bufCreateLock);
code = buf_Get(linkScp, &thyper, &bufp);
+ lock_ReleaseRead(&linkScp->bufCreateLock);
lock_ObtainMutex(&linkScp->mx);
if (code)
return code;
buf_Release(bufp);
return code;
}
+ cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
+
if (cm_HaveBuffer(linkScp, bufp, 0))
break;
if (!linkScp->mountPointStringp[0]) {
strncpy(linkScp->mountPointStringp, bufp->datap, temp);
linkScp->mountPointStringp[temp] = 0; /* null terminate */
+
+ if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
+ linkScp->fileType = CM_SCACHETYPE_DFSLINK;
}
buf_Release(bufp);
} /* don't have sym link contents cached */
char *linkp;
cm_space_t *tsp;
+ *newRootScpp = NULL;
+ *newSpaceBufferp = NULL;
+
lock_ObtainMutex(&linkScp->mx);
code = cm_HandleLink(linkScp, userp, reqp);
- if (code)
+ if (code)
goto done;
/* if we may overflow the buffer, bail out; buffer is signficantly
*newRootScpp = cm_data.rootSCachep;
cm_HoldSCache(cm_data.rootSCachep);
} else if (linkp[0] == '\\' && linkp[1] == '\\') {
- if (!strnicmp(&linkp[2], cm_NetbiosName, (len = strlen(cm_NetbiosName))))
+ if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
{
char * p = &linkp[len + 3];
if (strnicmp(p, "all", 3) == 0)
} else {
linkScp->fileType = CM_SCACHETYPE_DFSLINK;
strcpy(tsp->data, linkp);
- *newRootScpp = NULL;
code = CM_ERROR_PATH_NOT_COVERED;
}
- } else if ( !strnicmp(linkp, "msdfs:", (len = strlen("msdfs:"))) ) {
+ } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
+ !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
linkScp->fileType = CM_SCACHETYPE_DFSLINK;
strcpy(tsp->data, linkp);
- *newRootScpp = NULL;
code = CM_ERROR_PATH_NOT_COVERED;
} else if (*linkp == '\\' || *linkp == '/') {
#if 0
*/
linkScp->fileType = CM_SCACHETYPE_INVALID;
strcpy(tsp->data, linkp);
- *newRootScpp = NULL;
code = CM_ERROR_NOSUCHPATH;
#endif
} else {
/* a relative link */
strcpy(tsp->data, linkp);
- *newRootScpp = NULL;
}
if (pathSuffixp[0] != 0) { /* if suffix string is non-null */
strcat(tsp->data, "\\");
strcat(tsp->data, pathSuffixp);
}
- *newSpaceBufferp = tsp;
+ if (code == 0)
+ *newSpaceBufferp = tsp;
+ else {
+ cm_FreeSpace(tsp);
+
+ if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp)
+ cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
+ }
done:
lock_ReleaseMutex(&linkScp->mx);
return code;
}
-
+#ifdef DEBUG_REFCOUNT
+long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
+ cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp,
+ char * file, long line)
+#else
long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
+#endif
{
long code;
char *tp; /* ptr moving through input buffer */
char tc; /* temp char */
int haveComponent; /* has new component started? */
- char component[256]; /* this is the new component */
+ char component[AFSPATHMAX]; /* this is the new component */
char *cp; /* component name being assembled */
cm_scache_t *tscp; /* current location in the hierarchy */
cm_scache_t *nscp; /* next dude down */
int symlinkCount; /* count of # of symlinks traversed */
int extraFlag; /* avoid chasing mt pts for dir cmd */
int phase = 1; /* 1 = tidPathp, 2 = pathp */
+#define MAX_FID_COUNT 512
+ cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
+ int fid_count = 0; /* number of fids processed in this path walk */
+ int i;
+
+#ifdef DEBUG_REFCOUNT
+ afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
+ osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
+ rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>",
+ flags);
+#endif
tp = tidPathp;
if (tp == NULL) {
tscp = rootSCachep;
cm_HoldSCache(tscp);
symlinkCount = 0;
- dirScp = 0;
+ dirScp = NULL;
+
while (1) {
tc = *tp++;
* is a symlink, we have more to do.
*/
*cp++ = 0; /* add null termination */
- extraFlag = 0;
- if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
- extraFlag = CM_FLAG_NOMOUNTCHASE;
- code = cm_Lookup(tscp, component,
- flags | extraFlag,
- userp, reqp, &nscp);
- if (code) {
- cm_ReleaseSCache(tscp);
- if (dirScp)
- cm_ReleaseSCache(dirScp);
- if (psp)
- cm_FreeSpace(psp);
- if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
- return CM_ERROR_NOSUCHPATH;
- else
- return code;
+ extraFlag = 0;
+ if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
+ extraFlag = CM_FLAG_NOMOUNTCHASE;
+ code = cm_Lookup(tscp, component,
+ flags | extraFlag,
+ userp, reqp, &nscp);
+
+ if (code == 0) {
+ if (!strcmp(component,"..") || !strcmp(component,".")) {
+ /*
+ * roll back the fid list until we find the fid
+ * that matches where we are now. Its not necessarily
+ * one or two fids because they might have been
+ * symlinks or mount points or both that were crossed.
+ */
+ for ( i=fid_count-1; i>=0; i--) {
+ if (!cm_FidCmp(&nscp->fid, &fids[i]))
+ break;
+ }
+ fid_count = i+1;
+ } else {
+ /* add the new fid to the list */
+ for ( i=0; i<fid_count; i++) {
+ if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
+ code = CM_ERROR_TOO_MANY_SYMLINKS;
+ cm_ReleaseSCache(nscp);
+ nscp = NULL;
+ break;
+ }
+ }
+ if (i == fid_count && fid_count < MAX_FID_COUNT) {
+ fids[fid_count++] = nscp->fid;
+ }
+ }
}
- haveComponent = 0; /* component done */
- if (dirScp)
- cm_ReleaseSCache(dirScp);
- dirScp = tscp; /* for some symlinks */
- tscp = nscp; /* already held */
- nscp = 0;
- if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
+
+ if (code) {
+ cm_ReleaseSCache(tscp);
+ if (dirScp)
+ cm_ReleaseSCache(dirScp);
+ if (psp)
+ cm_FreeSpace(psp);
+ if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
+ tscp->fileType == CM_SCACHETYPE_SYMLINK)
+ {
+ osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
+ return CM_ERROR_NOSUCHPATH;
+ } else {
+ osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
+ return code;
+ }
+ }
+
+ haveComponent = 0; /* component done */
+ if (dirScp)
+ cm_ReleaseSCache(dirScp);
+ dirScp = tscp; /* for some symlinks */
+ tscp = nscp; /* already held */
+ nscp = NULL;
+ if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
code = 0;
if (dirScp) {
cm_ReleaseSCache(dirScp);
- dirScp = 0;
+ dirScp = NULL;
}
break;
}
if (code) {
lock_ReleaseMutex(&tscp->mx);
cm_ReleaseSCache(tscp);
- tscp = 0;
+ tscp = NULL;
if (dirScp) {
cm_ReleaseSCache(dirScp);
- dirScp = 0;
+ dirScp = NULL;
}
break;
}
+ cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
/* this is a symlink; assemble a new buffer */
lock_ReleaseMutex(&tscp->mx);
if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
cm_ReleaseSCache(tscp);
- tscp = 0;
+ tscp = NULL;
if (dirScp) {
cm_ReleaseSCache(dirScp);
- dirScp = 0;
+ dirScp = NULL;
}
if (psp)
cm_FreeSpace(psp);
+ osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
return CM_ERROR_TOO_MANY_SYMLINKS;
}
if (tc == 0)
else
restp = tp;
code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
+
+ if (code == 0 && linkScp != NULL) {
+ if (linkScp == cm_data.rootSCachep)
+ fid_count = 0;
+ else {
+ for ( i=0; i<fid_count; i++) {
+ if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
+ code = CM_ERROR_TOO_MANY_SYMLINKS;
+ cm_ReleaseSCache(linkScp);
+ nscp = NULL;
+ break;
+ }
+ }
+ }
+ if (i == fid_count && fid_count < MAX_FID_COUNT) {
+ fids[fid_count++] = linkScp->fid;
+ }
+ }
+
if (code) {
/* something went wrong */
cm_ReleaseSCache(tscp);
- tscp = 0;
+ tscp = NULL;
if (dirScp) {
cm_ReleaseSCache(dirScp);
- dirScp = 0;
+ dirScp = NULL;
}
break;
}
tp = psp->data;
cm_ReleaseSCache(tscp);
tscp = linkScp;
- linkScp = 0;
+ linkScp = NULL;
/* already held
* by AssembleLink
* now, if linkScp is null, that's
*/
if (tscp == NULL) {
tscp = dirScp;
- dirScp = 0;
+ dirScp = NULL;
}
} else {
/* not a symlink, we may be done */
}
if (dirScp) {
cm_ReleaseSCache(dirScp);
- dirScp = 0;
+ dirScp = NULL;
}
code = 0;
break;
}
if (dirScp) {
cm_ReleaseSCache(dirScp);
- dirScp = 0;
+ dirScp = NULL;
}
} /* end of a component */
else
*outScpp = tscp;
else if (tscp)
cm_ReleaseSCache(tscp);
+
+#ifdef DEBUG_REFCOUNT
+ afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
+#endif
+ osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
return code;
}
cm_space_t *spacep;
cm_scache_t *newRootScp;
- osi_Log1(afsd_logp, "Evaluating symlink scp 0x%x", linkScp);
+ osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
if (code)
CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
userp, NULL, reqp, outScpp);
- if (code == CM_ERROR_NOSUCHFILE)
+ if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
code = CM_ERROR_NOSUCHPATH;
/* this stuff is allocated no matter what happened on the namei call,
cm_FreeSpace(spacep);
cm_ReleaseSCache(newRootScp);
+ if (linkScp == *outScpp) {
+ cm_ReleaseSCache(*outScpp);
+ *outScpp = NULL;
+ code = CM_ERROR_NOSUCHPATH;
+ }
+
return code;
}
* check anyway, but we want to minimize the chance that we have to leave stuff
* unstat'd.
*/
-#define CM_BULKMAX 128
+#define CM_BULKMAX (3 * AFSCBMAX)
/* rock for bulk stat calls */
typedef struct cm_bulkStat {
if (tscp) {
if (lock_TryMutex(&tscp->mx)) {
/* we have an entry that we can look at */
- if (cm_HaveCallback(tscp)) {
+ if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
/* we have a callback on it. Don't bother
* fetching this stat entry, since we're happy
* with the info we have.
/* called with a locked scp and a pointer to a buffer. Make bulk stat
* calls on all undeleted files in the page of the directory specified.
*/
-void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
- cm_req_t *reqp)
+afs_int32
+cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
+ cm_req_t *reqp)
{
long code;
- cm_bulkStat_t bb; /* this is *BIG*, probably 12K or so;
+ cm_bulkStat_t bb; /* this is *BIG*, probably 16K or so;
* watch for stack problems */
AFSCBFids fidStruct;
AFSBulkStats statStruct;
cm_scache_t *scp;
cm_fid_t tfid;
struct rx_connection * callp;
+ int inlinebulk = 0; /* Did we use InlineBulkStatus RPC or not? */
- osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
+ osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
/* should be on a buffer boundary */
- osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
+ osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
- bb.counter = 0;
+ memset(&bb, 0, sizeof(bb));
bb.bufOffset = *offsetp;
lock_ReleaseMutex(&dscp->mx);
/* if we failed, bail out early */
if (code && code != CM_ERROR_STOPNOW) {
lock_ObtainMutex(&dscp->mx);
- return;
+ return code;
}
/* otherwise, we may have one or more bulk stat's worth of stuff in bb;
cm_StartCallbackGrantingCall(NULL, &cbReq);
osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
do {
- code = cm_Conn(&dscp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
if (code)
continue;
callp = cm_GetRxConn(connp);
- code = RXAFS_BulkStatus(callp, &fidStruct,
+ if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
+ code = RXAFS_InlineBulkStatus(callp, &fidStruct,
&statStruct, &callbackStruct, &volSync);
+ if (code == RXGEN_OPCODE) {
+ cm_SetServerNoInlineBulk(connp->serverp, 0);
+ } else {
+ inlinebulk = 1;
+ }
+ }
+ if (!inlinebulk) {
+ code = RXAFS_BulkStatus(callp, &fidStruct,
+ &statStruct, &callbackStruct, &volSync);
+ }
rx_PutConnection(callp);
} while (cm_Analyze(connp, userp, reqp, &dscp->fid,
&volSync, NULL, &cbReq, code));
code = cm_MapRPCError(code, reqp);
-
if (code)
- osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
+ osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
+ inlinebulk ? "Inline" : "", code);
else
- osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
+ osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
/* may as well quit on an error, since we're not going to do
* much better on the next immediate call, either.
* Right now, be pretty conservative: if there's a
* callback or a pending call, skip it.
*/
- if (scp->cbServerp == NULL
+ if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
&& !(scp->flags &
(CM_SCACHEFLAG_FETCHING
| CM_SCACHEFLAG_STORING
cm_EndCallbackGrantingCall(scp, &cbReq,
&bb.callbacks[j],
CM_CALLBACK_MAINTAINCOUNT);
- cm_MergeStatus(scp, &bb.stats[j], &volSync,
- userp, 0);
+ cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
}
lock_ReleaseMutex(&scp->mx);
cm_ReleaseSCache(scp);
filex += filesThisCall;
} /* while there are still more files to process */
lock_ObtainMutex(&dscp->mx);
+
+ /* If we did the InlineBulk RPC pull out the return code and log it */
+ if (inlinebulk) {
+ if ((&bb.stats[0])->errorCode) {
+ osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d",
+ (&bb.stats[0])->errorCode);
+ }
+ }
+
osi_Log0(afsd_logp, "END cm_TryBulkStat");
+ return 0;
}
void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
if (code)
goto done;
-
+ cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
if (scp->fileType != CM_SCACHETYPE_FILE) {
code = CM_ERROR_ISDIR;
goto done;
code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
| CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
+
+ /* If we only have 'i' bits, then we should still be able to set
+ the size of a file we created. */
+ if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
+ code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
+ CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
+ | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
+ }
+
if (code)
goto done;
/* done successfully */
code = 0;
+ cm_SyncOpDone(scp, NULL,
+ CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
+ | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
+
done:
lock_ReleaseMutex(&scp->mx);
lock_ReleaseWrite(&scp->bufCreateLock);
cm_req_t *reqp)
{
long code;
- int flags;
AFSFetchStatus afsOutStatus;
AFSVolSync volSync;
cm_conn_t *connp;
if (attrp->mask & CM_ATTRMASK_LENGTH)
return cm_SetLength(scp, &attrp->length, userp, reqp);
- flags = CM_SCACHESYNC_STORESTATUS;
-
lock_ObtainMutex(&scp->mx);
/* otherwise, we have to make an RPC to get the status */
code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
+ if (code) {
+ lock_ReleaseMutex(&scp->mx);
+ return code;
+ }
/* make the attr structure */
cm_StatusFromAttr(&afsInStatus, scp, attrp);
tfid.Volume = scp->fid.volume;
tfid.Vnode = scp->fid.vnode;
tfid.Unique = scp->fid.unique;
-
- lock_ReleaseMutex(&scp->mx);
- if (code)
- return code;
+ lock_ReleaseMutex(&scp->mx);
/* now make the RPC */
- osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%x", (long) scp);
+ osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
do {
- code = cm_Conn(&scp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
if (code)
continue;
lock_ObtainMutex(&scp->mx);
cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
if (code == 0)
- cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
- CM_MERGEFLAG_FORCE);
+ cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
+ CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
/* if we're changing the mode bits, discard the ACL cache,
* since we changed the mode bits.
*/
- if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
+ if (afsInStatus.Mask & AFS_SETMODE)
+ cm_FreeAllACLEnts(scp);
lock_ReleaseMutex(&scp->mx);
return code;
}
cm_callbackRequest_t cbReq;
AFSFid newAFSFid;
cm_fid_t newFid;
- cm_scache_t *scp;
+ cm_scache_t *scp = NULL;
int didEnd;
AFSStoreStatus inStatus;
AFSFetchStatus updatedDirStatus;
AFSCallBack newFileCallback;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
/* can't create names with @sys in them; must expand it manually first.
* return "invalid request" if they try.
return CM_ERROR_ATSYS;
}
+#ifdef AFS_FREELANCE_CLIENT
+ /* Freelance root volume does not hold files */
+ if (cm_freelanceEnabled &&
+ dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+ dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+ {
+ return CM_ERROR_NOACCESS;
+ }
+#endif /* AFS_FREELANCE_CLIENT */
+
/* before starting the RPC, mark that we're changing the file data, so
* that someone who does a chmod will know to wait until our call
* completes.
*/
+ cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
lock_ObtainMutex(&dscp->mx);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+ lock_ReleaseMutex(&dscp->mx);
if (code == 0) {
cm_StartCallbackGrantingCall(NULL, &cbReq);
+ } else {
+ cm_EndDirOp(&dirop);
}
- lock_ReleaseMutex(&dscp->mx);
if (code) {
return code;
}
cm_StatusFromAttr(&inStatus, NULL, attrp);
/* try the RPC now */
- osi_Log1(afsd_logp, "CALL CreateFile scp 0x%x", (long) dscp);
+ osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
do {
- code = cm_Conn(&dscp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
if (code)
continue;
else
osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
+ if (dirop.scp) {
+ lock_ObtainWrite(&dirop.scp->dirlock);
+ dirop.lockType = CM_DIRLOCK_WRITE;
+ }
lock_ObtainMutex(&dscp->mx);
cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
- cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
+ cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
}
lock_ReleaseMutex(&dscp->mx);
code = cm_GetSCache(&newFid, &scp, userp, reqp);
if (code == 0) {
lock_ObtainMutex(&scp->mx);
+ scp->creator = userp; /* remember who created it */
if (!cm_HaveCallback(scp)) {
- cm_MergeStatus(scp, &newFileStatus, &volSync,
+ cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
userp, 0);
cm_EndCallbackGrantingCall(scp, &cbReq,
&newFileCallback, 0);
if (!didEnd)
cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
+ if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirCreateEntry(&dirop, namep, &newFid);
+#ifdef USE_BPLUS
+ cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+#endif
+ }
+ cm_EndDirOp(&dirop);
+
return code;
}
lock_ReleaseWrite(&scp->bufCreateLock);
if (code == 0) {
lock_ObtainMutex(&scp->mx);
- scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
- | CM_SCACHEFLAG_OUTOFSPACE);
+
if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
| CM_SCACHEMASK_CLIENTMODTIME
| CM_SCACHEMASK_LENGTH))
code = cm_StoreMini(scp, userp, reqp);
+
+ if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
+ code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
+ scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
+ }
+
lock_ReleaseMutex(&scp->mx);
}
return code;
cm_callbackRequest_t cbReq;
AFSFid newAFSFid;
cm_fid_t newFid;
- cm_scache_t *scp;
+ cm_scache_t *scp = NULL;
int didEnd;
AFSStoreStatus inStatus;
AFSFetchStatus updatedDirStatus;
AFSCallBack newDirCallback;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
/* can't create names with @sys in them; must expand it manually first.
* return "invalid request" if they try.
return CM_ERROR_ATSYS;
}
+#ifdef AFS_FREELANCE_CLIENT
+ /* Freelance root volume does not hold subdirectories */
+ if (cm_freelanceEnabled &&
+ dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
+ dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
+ {
+ return CM_ERROR_NOACCESS;
+ }
+#endif /* AFS_FREELANCE_CLIENT */
+
/* before starting the RPC, mark that we're changing the directory
* data, so that someone who does a chmod on the dir will wait until
* our call completes.
*/
+ cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
lock_ObtainMutex(&dscp->mx);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
+ lock_ReleaseMutex(&dscp->mx);
if (code == 0) {
cm_StartCallbackGrantingCall(NULL, &cbReq);
+ } else {
+ cm_EndDirOp(&dirop);
}
- lock_ReleaseMutex(&dscp->mx);
if (code) {
return code;
}
cm_StatusFromAttr(&inStatus, NULL, attrp);
/* try the RPC now */
- osi_Log1(afsd_logp, "CALL MakeDir scp 0x%x", (long) dscp);
+ osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
do {
- code = cm_Conn(&dscp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
if (code)
continue;
else
osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
+ if (dirop.scp) {
+ lock_ObtainWrite(&dirop.scp->dirlock);
+ dirop.lockType = CM_DIRLOCK_WRITE;
+ }
lock_ObtainMutex(&dscp->mx);
cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
- cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
+ cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
}
lock_ReleaseMutex(&dscp->mx);
if (code == 0) {
lock_ObtainMutex(&scp->mx);
if (!cm_HaveCallback(scp)) {
- cm_MergeStatus(scp, &newDirStatus, &volSync,
+ cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
userp, 0);
cm_EndCallbackGrantingCall(scp, &cbReq,
&newDirCallback, 0);
if (!didEnd)
cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
+ if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirCreateEntry(&dirop, namep, &newFid);
+#ifdef USE_BPLUS
+ cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+#endif
+ }
+ cm_EndDirOp(&dirop);
+
/* and return error code */
return code;
}
AFSFetchStatus newLinkStatus;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
if (dscp->fid.cell != sscp->fid.cell ||
dscp->fid.volume != sscp->fid.volume) {
return CM_ERROR_CROSSDEVLINK;
}
+ cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
lock_ObtainMutex(&dscp->mx);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&dscp->mx);
+ if (code != 0)
+ cm_EndDirOp(&dirop);
if (code)
return code;
/* try the RPC now */
- osi_Log1(afsd_logp, "CALL Link scp 0x%x", (long) dscp);
+ osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
do {
- code = cm_Conn(&dscp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
if (code) continue;
dirAFSFid.Volume = dscp->fid.volume;
else
osi_Log0(afsd_logp, "CALL Link SUCCESS");
+ if (dirop.scp) {
+ lock_ObtainWrite(&dirop.scp->dirlock);
+ dirop.lockType = CM_DIRLOCK_WRITE;
+ }
lock_ObtainMutex(&dscp->mx);
cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
- cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
+ cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
}
lock_ReleaseMutex(&dscp->mx);
+ if (code == 0) {
+ if (cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirCreateEntry(&dirop, namep, &sscp->fid);
+#ifdef USE_BPLUS
+ cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
+#endif
+ }
+ }
+ cm_EndDirOp(&dirop);
+
return code;
}
AFSFetchStatus newLinkStatus;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
/* before starting the RPC, mark that we're changing the directory data,
* so that someone who does a chmod on the dir will wait until our
* call completes.
*/
+ cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
lock_ObtainMutex(&dscp->mx);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&dscp->mx);
+ if (code != 0)
+ cm_EndDirOp(&dirop);
if (code) {
return code;
}
cm_StatusFromAttr(&inStatus, NULL, attrp);
/* try the RPC now */
- osi_Log1(afsd_logp, "CALL Symlink scp 0x%x", (long) dscp);
+ osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
do {
- code = cm_Conn(&dscp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
if (code)
continue;
else
osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
+ if (dirop.scp) {
+ lock_ObtainWrite(&dirop.scp->dirlock);
+ dirop.lockType = CM_DIRLOCK_WRITE;
+ }
lock_ObtainMutex(&dscp->mx);
cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
- cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
+ cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
}
lock_ReleaseMutex(&dscp->mx);
+ if (code == 0) {
+ if (cm_CheckDirOpForSingleChange(&dirop)) {
+ newFid.cell = dscp->fid.cell;
+ newFid.volume = dscp->fid.volume;
+ newFid.vnode = newAFSFid.Vnode;
+ newFid.unique = newAFSFid.Unique;
+
+ cm_DirCreateEntry(&dirop, namep, &newFid);
+#ifdef USE_BPLUS
+ cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
+#endif
+ }
+ }
+ cm_EndDirOp(&dirop);
+
/* now try to create the new dir's entry, too, but be careful to
* make sure that we don't merge in old info. Since we weren't locking
* out any requests during the file's creation, we may have pretty old
if (code == 0) {
lock_ObtainMutex(&scp->mx);
if (!cm_HaveCallback(scp)) {
- cm_MergeStatus(scp, &newLinkStatus, &volSync,
+ cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
userp, 0);
}
lock_ReleaseMutex(&scp->mx);
AFSFetchStatus updatedDirStatus;
AFSVolSync volSync;
struct rx_connection * callp;
+ cm_dirOp_t dirop;
/* before starting the RPC, mark that we're changing the directory data,
* so that someone who does a chmod on the dir will wait until our
* call completes.
*/
+ cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
lock_ObtainMutex(&dscp->mx);
code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&dscp->mx);
if (code) {
+ cm_EndDirOp(&dirop);
return code;
}
didEnd = 0;
/* try the RPC now */
- osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%x", (long) dscp);
+ osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
do {
- code = cm_Conn(&dscp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
if (code)
continue;
else
osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
+ if (dirop.scp) {
+ lock_ObtainWrite(&dirop.scp->dirlock);
+ dirop.lockType = CM_DIRLOCK_WRITE;
+ }
lock_ObtainMutex(&dscp->mx);
cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
if (code == 0) {
cm_dnlcRemove(dscp, namep);
- cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
+ cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
}
lock_ReleaseMutex(&dscp->mx);
+ if (code == 0) {
+ if (cm_CheckDirOpForSingleChange(&dirop)) {
+ cm_DirDeleteEntry(&dirop, namep);
+#ifdef USE_BPLUS
+ cm_BPlusDirDeleteEntry(&dirop, namep);
+#endif
+ }
+ }
+ cm_EndDirOp(&dirop);
+
/* and return error code */
return code;
}
AFSVolSync volSync;
int oneDir;
struct rx_connection * callp;
+ cm_dirOp_t oldDirOp;
+ cm_fid_t fileFid;
+ int diropCode = -1;
+ cm_dirOp_t newDirOp;
/* before starting the RPC, mark that we're changing the directory data,
* so that someone who does a chmod on the dir will wait until our call
return CM_ERROR_RENAME_IDENTICAL;
oneDir = 1;
+ cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
lock_ObtainMutex(&oldDscp->mx);
cm_dnlcRemove(oldDscp, oldNamep);
cm_dnlcRemove(oldDscp, newNamep);
code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&oldDscp->mx);
+ if (code != 0) {
+ cm_EndDirOp(&oldDirOp);
+ }
}
else {
/* two distinct dir vnodes */
return CM_ERROR_CROSSDEVLINK;
if (oldDscp->fid.vnode < newDscp->fid.vnode) {
+ cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
lock_ObtainMutex(&oldDscp->mx);
cm_dnlcRemove(oldDscp, oldNamep);
code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&oldDscp->mx);
+ if (code != 0)
+ cm_EndDirOp(&oldDirOp);
if (code == 0) {
+ cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
lock_ObtainMutex(&newDscp->mx);
cm_dnlcRemove(newDscp, newNamep);
code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&newDscp->mx);
if (code) {
+ cm_EndDirOp(&newDirOp);
+
/* cleanup first one */
- lock_ObtainMutex(&newDscp->mx);
+ lock_ObtainMutex(&oldDscp->mx);
cm_SyncOpDone(oldDscp, NULL,
CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&oldDscp->mx);
+ cm_EndDirOp(&oldDirOp);
}
}
}
else {
/* lock the new vnode entry first */
+ cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
lock_ObtainMutex(&newDscp->mx);
cm_dnlcRemove(newDscp, newNamep);
code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&newDscp->mx);
+ if (code != 0)
+ cm_EndDirOp(&newDirOp);
if (code == 0) {
+ cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
lock_ObtainMutex(&oldDscp->mx);
cm_dnlcRemove(oldDscp, oldNamep);
code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&oldDscp->mx);
+ if (code != 0)
+ cm_EndDirOp(&oldDirOp);
if (code) {
/* cleanup first one */
lock_ObtainMutex(&newDscp->mx);
cm_SyncOpDone(newDscp, NULL,
CM_SCACHESYNC_STOREDATA);
lock_ReleaseMutex(&newDscp->mx);
+ cm_EndDirOp(&newDirOp);
}
}
}
didEnd = 0;
/* try the RPC now */
- osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x",
- (long) oldDscp, (long) newDscp);
+ osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
+ oldDscp, newDscp);
do {
- code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
+ code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
if (code)
continue;
osi_Log0(afsd_logp, "CALL Rename SUCCESS");
/* update the individual stat cache entries for the directories */
+ if (oldDirOp.scp) {
+ lock_ObtainWrite(&oldDirOp.scp->dirlock);
+ oldDirOp.lockType = CM_DIRLOCK_WRITE;
+ }
lock_ObtainMutex(&oldDscp->mx);
cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
+
+ if (code == 0)
+ cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
+ userp, CM_MERGEFLAG_DIROP);
+ lock_ReleaseMutex(&oldDscp->mx);
+
if (code == 0) {
- cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
- userp, 0);
+ if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
+
+#ifdef USE_BPLUS
+ diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
+ if (diropCode == CM_ERROR_INEXACT_MATCH)
+ diropCode = 0;
+ else if (diropCode == EINVAL)
+#endif
+ diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
+
+ if (diropCode == 0) {
+ if (oneDir) {
+ diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
+#ifdef USE_BPLUS
+ cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
+#endif
+ }
+
+ if (diropCode == 0) {
+ diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
+#ifdef USE_BPLUS
+ cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
+#endif
+ }
+ }
+ }
}
- lock_ReleaseMutex(&oldDscp->mx);
+ cm_EndDirOp(&oldDirOp);
/* and update it for the new one, too, if necessary */
if (!oneDir) {
+ if (newDirOp.scp) {
+ lock_ObtainWrite(&newDirOp.scp->dirlock);
+ newDirOp.lockType = CM_DIRLOCK_WRITE;
+ }
lock_ObtainMutex(&newDscp->mx);
cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
+ if (code == 0)
+ cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
+ userp, CM_MERGEFLAG_DIROP);
+ lock_ReleaseMutex(&newDscp->mx);
+
if (code == 0) {
- cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
- userp, 0);
+ /* we only make the local change if we successfully made
+ the change in the old directory AND there was only one
+ change in the new directory */
+ if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
+ cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
+#ifdef USE_BPLUS
+ cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
+#endif
+ }
}
- lock_ReleaseMutex(&newDscp->mx);
+ cm_EndDirOp(&newDirOp);
}
/* and return error code */
return code;
}
-long cm_Lock(cm_scache_t *scp, unsigned char LockType,
- LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
- u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
- void **lockpp)
-{
- long code;
- int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
- AFSFid tfid;
- AFSVolSync volSync;
- cm_conn_t *connp;
- cm_file_lock_t *fileLock;
- osi_queue_t *q;
- int found = 0;
- struct rx_connection * callp;
+/* Byte range locks:
+
+ The OpenAFS Windows client has to fake byte range locks given no
+ server side support for such locks. This is implemented as keyed
+ byte range locks on the cache manager.
+
+ Keyed byte range locks:
+
+ Each cm_scache_t structure keeps track of a list of keyed locks.
+ The key for a lock identifies an owner of a set of locks (referred
+ to as a client). Each key is represented by a value. The set of
+ key values used within a specific cm_scache_t structure form a
+ namespace that has a scope of just that cm_scache_t structure. The
+ same key value can be used with another cm_scache_t structure and
+ correspond to a completely different client. However it is
+ advantageous for the SMB or IFS layer to make sure that there is a
+ 1-1 mapping between client and keys over all cm_scache_t objects.
+
+ Assume a client C has key Key(C) (although, since the scope of the
+ key is a cm_scache_t, the key can be Key(C,S), where S is the
+ cm_scache_t. But assume a 1-1 relation between keys and clients).
+ A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
+ inclusive (a.k.a. [O,O+L-1]). The function Key(x) is implemented
+ through cm_generateKey() function for both SMB and IFS.
+
+ The list of locks for a cm_scache_t object S is maintained in
+ S->fileLocks. The cache manager will set a lock on the AFS file
+ server in order to assert the locks in S->fileLocks. If only
+ shared locks are in place for S, then the cache manager will obtain
+ a LockRead lock, while if there are any exclusive locks, it will
+ obtain a LockWrite lock. If the exclusive locks are all released
+ while the shared locks remain, then the cache manager will
+ downgrade the lock from LockWrite to LockRead. Similarly, if an
+ exclusive lock is obtained when only shared locks exist, then the
+ cache manager will try to upgrade the lock from LockRead to
+ LockWrite.
+
+ Each lock L owned by client C maintains a key L->key such that
+ L->key == Key(C), the effective range defined by L->LOffset and
+ L->LLength such that the range of bytes affected by the lock is
+ (L->LOffset, +L->LLength), a type maintained in L->LockType which
+ is either exclusive or shared.
+
+ Lock states:
+
+ A lock exists iff it is in S->fileLocks for some cm_scache_t
+ S. Existing locks are in one of the following states: ACTIVE,
+ WAITLOCK, WAITUNLOCK, LOST, DELETED.
+
+ The following sections describe each lock and the associated
+ transitions.
+
+ 1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
+ the lock with the AFS file server. This type of lock can be
+ exercised by a client to read or write to the locked region (as
+ the lock allows).
+
+ 1.1 ACTIVE->LOST: When the AFS file server fails to extend a
+ server lock that was required to assert the lock. Before
+ marking the lock as lost, the cache manager checks if the file
+ has changed on the server. If the file has not changed, then
+ the cache manager will attempt to obtain a new server lock
+ that is sufficient to assert the client side locks for the
+ file. If any of these fail, the lock is marked as LOST.
+ Otherwise, it is left as ACTIVE.
+
+ 1.2 ACTIVE->DELETED: Lock is released.
+
+ 2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
+ grants the lock but the lock is yet to be asserted with the AFS
+ file server. Once the file server grants the lock, the state
+ will transition to an ACTIVE lock.
- /* Look for a conflict. Also, if we are asking for a shared lock,
- * look for another shared lock, so we don't have to do an RPC.
- */
- q = scp->fileLocks;
- while (q) {
- fileLock = (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
- if ((fileLock->flags & (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING)) == 0) {
- if ((LockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
- (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0)
- return CM_ERROR_WOULDBLOCK;
- found = 1;
- }
- q = osi_QNext(q);
- }
+ 2.1 WAITLOCK->ACTIVE: The server granted the lock.
- if (found)
- code = 0;
- else {
- tfid.Volume = scp->fid.volume;
- tfid.Vnode = scp->fid.vnode;
- tfid.Unique = scp->fid.unique;
- lock_ReleaseMutex(&scp->mx);
- do {
- code = cm_Conn(&scp->fid, userp, reqp, &connp);
- if (code)
- break;
+ 2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
+ waiting.
- callp = cm_GetRxConn(connp);
- code = RXAFS_SetLock(callp, &tfid, Which,
- &volSync);
- rx_PutConnection(callp);
+ 2.3 WAITLOCK->LOST: One or more locks from this client were
+ marked as LOST. No further locks will be granted to this
+ client until all lost locks are removed.
- } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
- NULL, NULL, code));
- lock_ObtainMutex(&scp->mx);
- code = cm_MapRPCError(code, reqp);
- }
+ 3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
+ receives a request for a lock that conflicts with an existing
+ ACTIVE or WAITLOCK lock. The lock will be placed in the queue
+ and will be granted at such time the conflicting locks are
+ removed, at which point the state will transition to either
+ WAITLOCK or ACTIVE.
+
+ 3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed. The
+ current serverLock is sufficient to assert this lock, or a
+ sufficient serverLock is obtained.
+
+ 3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
+ however the required serverLock is yet to be asserted with the
+ server.
- if (code == 0 || Timeout != 0) {
- fileLock = malloc(sizeof(cm_file_lock_t));
- fileLock->LockType = LockType;
- cm_HoldUser(userp);
- fileLock->userp = userp;
- fileLock->fid = scp->fid;
- fileLock->LOffset = LOffset;
- fileLock->LLength = LLength;
- fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
- osi_QAdd(&scp->fileLocks, &fileLock->fileq);
- lock_ObtainWrite(&cm_scacheLock);
- osi_QAdd(&cm_allFileLocks, &fileLock->q);
- lock_ReleaseWrite(&cm_scacheLock);
- if (code != 0)
- *lockpp = fileLock;
- }
- return code;
-}
+ 3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
+ released.
-long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
- LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
- cm_user_t *userp, cm_req_t *reqp)
-{
- long code = 0;
- int Which = ((LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
- AFSFid tfid;
- AFSVolSync volSync;
- cm_conn_t *connp;
- cm_file_lock_t *fileLock, *ourLock;
- osi_queue_t *q, *qq;
- int anotherReader = 0;
- int smallLock = 0;
- int found = 0;
- struct rx_connection * callp;
+ 3.5 WAITUNLOCK->LOST: One or more locks from this client were
+ marked as LOST. No further locks will be granted to this
+ client until all lost locks are removed.
+
+ 4. LOST: A lock L is LOST if the server lock that was required to
+ assert the lock could not be obtained or if it could not be
+ extended, or if other locks by the same client were LOST.
+ Essentially, once a lock is LOST, the contract between the cache
+ manager and that specific client is no longer valid.
+
+ The cache manager rechecks the server lock once every minute and
+ extends it as appropriate. If this is not done for 5 minutes,
+ the AFS file server will release the lock (the 5 minute timeout
+ is based on current file server code and is fairly arbitrary).
+ Once released, the lock cannot be re-obtained without verifying
+ that the contents of the file hasn't been modified since the
+ time the lock was released. Re-obtaining the lock without
+ verifying this may lead to data corruption. If the lock can not
+ be obtained safely, then all active locks for the cm_scache_t
+ are marked as LOST.
- if (LargeIntegerLessThan(LLength, scp->length))
- smallLock = 1;
+ 4.1 LOST->DELETED: The lock is released.
- /* Look for our own lock on the list, so as to remove it.
- * Also, determine if we're the last reader; if not, avoid an RPC.
- */
- q = scp->fileLocks;
- while (q) {
- fileLock = (cm_file_lock_t *)
- ((char *) q - offsetof(cm_file_lock_t, fileq));
- if (!found
- && fileLock->userp == userp
- && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
- && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
- found = 1;
- ourLock = fileLock;
- qq = q;
- }
- else if (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK)
- anotherReader = 1;
- q = osi_QNext(q);
- }
+ 5. DELETED: The lock is no longer relevant. Eventually, it will
+ get removed from the cm_scache_t. In the meantime, it will be
+ treated as if it does not exist.
- /* ignore byte ranges */
- if (smallLock && !found)
- return 0;
+ 5.1 DELETED->not exist: The lock is removed from the
+ cm_scache_t.
- /* don't try to unlock other people's locks */
- if (!found)
- return CM_ERROR_WOULDBLOCK;
+ The following are classifications of locks based on their state.
- /* discard lock record */
- osi_QRemove(&scp->fileLocks, qq);
- /*
- * Don't delete it here; let the daemon delete it, to simplify
- * the daemon's traversal of the list.
- */
- lock_ObtainWrite(&cm_scacheLock);
- ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
- cm_ReleaseUser(ourLock->userp);
- lock_ReleaseWrite(&cm_scacheLock);
+ 6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK. These locks
+ have been accepted by the cache manager, but may or may not have
+ been granted back to the client.
- if (!anotherReader) {
- tfid.Volume = scp->fid.volume;
- tfid.Vnode = scp->fid.vnode;
- tfid.Unique = scp->fid.unique;
- lock_ReleaseMutex(&scp->mx);
- osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
- do {
- code = cm_Conn(&scp->fid, userp, reqp, &connp);
- if (code)
- break;
+ 7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
- callp = cm_GetRxConn(connp);
- code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
- rx_PutConnection(callp);
+ 8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
- } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
- NULL, NULL, code));
- code = cm_MapRPCError(code, reqp);
+ Lock operation:
- if (code)
- osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
- else
- osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
+ A client C can READ range (Offset,+Length) of a file represented by
+ cm_scache_t S iff (1):
- lock_ObtainMutex(&scp->mx);
- }
+ 1. for all _a_ in (Offset,+Length), all of the following is true:
- return code;
-}
+ 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
+ (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
+ shared.
-void cm_CheckLocks()
-{
- osi_queue_t *q, *nq;
- cm_file_lock_t *fileLock;
- cm_req_t req;
- AFSFid tfid;
- AFSVolSync volSync;
- cm_conn_t *connp;
- long code;
- struct rx_connection * callp;
+ 1.2 For each LOST lock L in S->fileLocks such that _a_ in
+ (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
+ Key(C)
- cm_InitReq(&req);
+ (When locks are lost on an cm_scache_t, all locks are lost. By
+ 4.2 (below), if there is an exclusive LOST lock, then there
+ can't be any overlapping ACTIVE locks.)
- lock_ObtainWrite(&cm_scacheLock);
- q = cm_allFileLocks;
- while (q) {
- fileLock = (cm_file_lock_t *) q;
- nq = osi_QNext(q);
- if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
- osi_QRemove(&cm_allFileLocks, q);
- free(fileLock);
- }
- else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
- tfid.Volume = fileLock->fid.volume;
- tfid.Vnode = fileLock->fid.vnode;
- tfid.Unique = fileLock->fid.unique;
- lock_ReleaseWrite(&cm_scacheLock);
- osi_Log1(afsd_logp, "CALL ExtendLock lock 0x%x", (long) fileLock);
- do {
- code = cm_Conn(&fileLock->fid, fileLock->userp,
- &req, &connp);
- if (code)
- break;
+ A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
- callp = cm_GetRxConn(connp);
- code = RXAFS_ExtendLock(callp, &tfid,
- &volSync);
- rx_PutConnection(callp);
+ 2. for all _a_ in (Offset,+Length), one of the following is true:
- } while (cm_Analyze(connp, fileLock->userp, &req,
- &fileLock->fid, &volSync, NULL, NULL,
- code));
- code = cm_MapRPCError(code, &req);
- if (code)
- osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
- else
- osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
+ 2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
+ does not exist a LOST lock L such that _a_ in
+ (L->LOffset,+L->LLength).
+
+ 2.2 Byte _a_ of S is owned by C under lock L (as specified in
+ 1.2) AND L->LockType is exclusive.
+
+ A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
+
+ 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
+ true:
+
+ 3.1 If L->LockType is exclusive then there does NOT exist a
+ ACCEPTED lock M in S->fileLocks such that _a_ in
+ (M->LOffset,+M->LLength).
+
+ (If we count all QUEUED locks then we hit cases such as
+ cascading waiting locks where the locks later on in the queue
+ can be granted without compromising file integrity. On the
+ other hand if only ACCEPTED locks are considered, then locks
+ that were received earlier may end up waiting for locks that
+ were received later to be unlocked. The choice of ACCEPTED
+ locks was made to mimic the Windows byte range lock
+ semantics.)
+
+ 3.2 If L->LockType is shared then for each ACCEPTED lock M in
+ S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
+ M->LockType is shared.
+
+ 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
+
+ 4.1 M->key != Key(C)
+
+ 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
+ and (M->LOffset,+M->LLength) do not intersect.
+
+ (Note: If a client loses a lock, it loses all locks.
+ Subsequently, it will not be allowed to obtain any more locks
+ until all existing LOST locks that belong to the client are
+ released. Once all locks are released by a single client,
+ there exists no further contract between the client and AFS
+ about the contents of the file, hence the client can then
+ proceed to obtain new locks and establish a new contract.
+
+ This doesn't quite work as you think it should, because most
+ applications aren't built to deal with losing locks they
+ thought they once had. For now, we don't have a good
+ solution to lost locks.
+
+ Also, for consistency reasons, we have to hold off on
+ granting locks that overlap exclusive LOST locks.)
+
+ A client C can only unlock locks L in S->fileLocks which have
+ L->key == Key(C).
+
+ The representation and invariants are as follows:
+
+ - Each cm_scache_t structure keeps:
+
+ - A queue of byte-range locks (cm_scache_t::fileLocks) which
+ are of type cm_file_lock_t.
+
+ - A record of the highest server-side lock that has been
+ obtained for this object (cm_scache_t::serverLock), which is
+ one of (-1), LockRead, LockWrite.
+
+ - A count of ACCEPTED exclusive and shared locks that are in the
+ queue (cm_scache_t::sharedLocks and
+ cm_scache_t::exclusiveLocks)
+
+ - Each cm_file_lock_t structure keeps:
+
+ - The type of lock (cm_file_lock_t::LockType)
+
+ - The key associated with the lock (cm_file_lock_t::key)
+
+ - The offset and length of the lock (cm_file_lock_t::LOffset
+ and cm_file_lock_t::LLength)
+
+ - The state of the lock.
+
+ - Time of issuance or last successful extension
+
+ Semantic invariants:
+
+ I1. The number of ACCEPTED locks in S->fileLocks are
+ (S->sharedLocks + S->exclusiveLocks)
+
+ External invariants:
+
+ I3. S->serverLock is the lock that we have asserted with the
+ AFS file server for this cm_scache_t.
+
+ I4. S->serverLock == LockRead iff there is at least one ACTIVE
+ shared lock, but no ACTIVE exclusive locks.
+
+ I5. S->serverLock == LockWrite iff there is at least one ACTIVE
+ exclusive lock.
+
+ I6. If L is a LOST lock, then for each lock M in S->fileLocks,
+ M->key == L->key IMPLIES M is LOST or DELETED.
+
+ --asanka
+ */
- lock_ObtainWrite(&cm_scacheLock);
+#define IS_LOCK_ACTIVE(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
+
+#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)
+
+#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)
+
+#define IS_LOCK_LOST(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
+
+#define IS_LOCK_DELETED(lockp) (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
+
+/* unsafe */
+#define IS_LOCK_ACCEPTED(lockp) (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
+
+/* unsafe */
+#define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
+
+/* unsafe */
+#define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
+
+/* unsafe */
+#define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
+
+#if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
+#define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
+#else
+#define SCP_SUPPORTS_BRLOCKS(scp) (1)
+#endif
+
+#define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
+
+#if defined(VICED_CAPABILITY_WRITELOCKACL)
+#define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
+#else
+#define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
+
+/* This should really be defined in any build that this code is being
+ compiled. */
+#error VICED_CAPABILITY_WRITELOCKACL not defined.
+#endif
+
+static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
+{
+ afs_int64 int_begin;
+ afs_int64 int_end;
+
+ int_begin = MAX(pos->offset, neg->offset);
+ int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
+
+ if (int_begin < int_end) {
+ if (int_begin == pos->offset) {
+ pos->length = pos->offset + pos->length - int_end;
+ pos->offset = int_end;
+ } else if (int_end == pos->offset + pos->length) {
+ pos->length = int_begin - pos->offset;
}
- q = nq;
+
+ /* We only subtract ranges if the resulting range is
+ contiguous. If we try to support non-contigous ranges, we
+ aren't actually improving performance. */
}
- lock_ReleaseWrite(&cm_scacheLock);
-}
+}
-long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
+/* Called with scp->mx held. Returns 0 if all is clear to read the
+ specified range by the client identified by key.
+ */
+long cm_LockCheckRead(cm_scache_t *scp,
+ LARGE_INTEGER LOffset,
+ LARGE_INTEGER LLength,
+ cm_key_t key)
{
- long code;
- int Which = ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
- cm_scache_t *scp;
- AFSFid tfid;
- AFSVolSync volSync;
- cm_conn_t *connp;
+#ifndef ADVISORY_LOCKS
+
cm_file_lock_t *fileLock;
osi_queue_t *q;
- cm_req_t req;
- int found = 0;
- struct rx_connection * callp;
+ long code = 0;
+ cm_range_t range;
+ int substract_ranges = FALSE;
- if (vcp_is_dead) {
- code = CM_ERROR_TIMEDOUT;
- goto handleCode;
- }
+ range.offset = LOffset.QuadPart;
+ range.length = LLength.QuadPart;
- cm_InitReq(&req);
+ /*
- /* Look for a conflict. Also, if we are asking for a shared lock,
- * look for another shared lock, so we don't have to do an RPC.
- */
- code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
- if (code)
- return code;
+ 1. for all _a_ in (Offset,+Length), all of the following is true:
- q = scp->fileLocks;
- while (q) {
- fileLock = (cm_file_lock_t *)
- ((char *) q - offsetof(cm_file_lock_t, fileq));
- if ((fileLock->flags &
- (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
- == 0) {
- if ((oldFileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0
- || (fileLock->LockType & LOCKING_ANDX_SHARED_LOCK) == 0) {
- cm_ReleaseSCache(scp);
- return CM_ERROR_WOULDBLOCK;
+ 1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
+ (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
+ shared.
+
+ 1.2 For each LOST lock L in S->fileLocks such that _a_ in
+ (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
+ Key(C)
+
+ */
+
+ lock_ObtainRead(&cm_scacheLock);
+
+ for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
+ fileLock =
+ (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
+
+ if (INTERSECT_RANGE(range, fileLock->range)) {
+ if (IS_LOCK_ACTIVE(fileLock)) {
+ if (fileLock->key == key) {
+
+ /* If there is an active lock for this client, it
+ is safe to substract ranges.*/
+ cm_LockRangeSubtract(&range, &fileLock->range);
+ substract_ranges = TRUE;
+ } else {
+ if (fileLock->lockType != LockRead) {
+ code = CM_ERROR_LOCK_CONFLICT;
+ break;
+ }
+
+ /* even if the entire range is locked for reading,
+ we still can't grant the lock at this point
+ because the client may have lost locks. That
+ is, unless we have already seen an active lock
+ belonging to the client, in which case there
+ can't be any lost locks for this client. */
+ if (substract_ranges)
+ cm_LockRangeSubtract(&range, &fileLock->range);
+ }
+ } else if (IS_LOCK_LOST(fileLock) &&
+ (fileLock->key == key || fileLock->lockType == LockWrite)) {
+ code = CM_ERROR_BADFD;
+ break;
}
- found = 1;
}
- q = osi_QNext(q);
}
- if (found)
- code = 0;
- else {
- tfid.Volume = oldFileLock->fid.volume;
- tfid.Vnode = oldFileLock->fid.vnode;
- tfid.Unique = oldFileLock->fid.unique;
- osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
- do {
- code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
- &req, &connp);
- if (code)
- break;
+ lock_ReleaseRead(&cm_scacheLock);
- callp = cm_GetRxConn(connp);
- code = RXAFS_SetLock(callp, &tfid, Which,
- &volSync);
- rx_PutConnection(callp);
+ osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
+ scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
- } while (cm_Analyze(connp, oldFileLock->userp, &req,
- &oldFileLock->fid, &volSync,
- NULL, NULL, code));
- code = cm_MapRPCError(code, &req);
+ return code;
- if (code)
- osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
- else
- osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
+#else
+
+ return 0;
+
+#endif
+}
+
+/* Called with scp->mx held. Returns 0 if all is clear to write the
+ specified range by the client identified by key.
+ */
+long cm_LockCheckWrite(cm_scache_t *scp,
+ LARGE_INTEGER LOffset,
+ LARGE_INTEGER LLength,
+ cm_key_t key)
+{
+#ifndef ADVISORY_LOCKS
+
+ cm_file_lock_t *fileLock;
+ osi_queue_t *q;
+ long code = 0;
+ cm_range_t range;
+
+ range.offset = LOffset.QuadPart;
+ range.length = LLength.QuadPart;
+
+ /*
+ A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
+
+ 2. for all _a_ in (Offset,+Length), one of the following is true:
+
+ 2.1 Byte _a_ of S is unowned AND there does not exist a LOST
+ lock L such that _a_ in (L->LOffset,+L->LLength).
+
+ 2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
+ exclusive.
+ */
+
+ lock_ObtainRead(&cm_scacheLock);
+
+ for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
+ fileLock =
+ (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
+
+ if (INTERSECT_RANGE(range, fileLock->range)) {
+ if (IS_LOCK_ACTIVE(fileLock)) {
+ if (fileLock->key == key) {
+ if (fileLock->lockType == LockWrite) {
+
+ /* if there is an active lock for this client, it
+ is safe to substract ranges */
+ cm_LockRangeSubtract(&range, &fileLock->range);
+ } else {
+ code = CM_ERROR_LOCK_CONFLICT;
+ break;
+ }
+ } else {
+ code = CM_ERROR_LOCK_CONFLICT;
+ break;
+ }
+ } else if (IS_LOCK_LOST(fileLock)) {
+ code = CM_ERROR_BADFD;
+ break;
+ }
+ }
+ }
+
+ lock_ReleaseRead(&cm_scacheLock);
+
+ osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
+ scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
+
+ return code;
+
+#else
+
+ return 0;
+
+#endif
+}
+
+/* Forward dcl. */
+static void cm_LockMarkSCacheLost(cm_scache_t * scp);
+
+/* Called with cm_scacheLock write locked */
+static cm_file_lock_t * cm_GetFileLock(void) {
+ cm_file_lock_t * l;
+
+ l = (cm_file_lock_t *) cm_freeFileLocks;
+ if (l) {
+ osi_QRemove(&cm_freeFileLocks, &l->q);
+ } else {
+ l = malloc(sizeof(cm_file_lock_t));
+ osi_assertx(l, "null cm_file_lock_t");
+ }
+
+ memset(l, 0, sizeof(cm_file_lock_t));
+
+ return l;
+}
+
+/* Called with cm_scacheLock write locked */
+static void cm_PutFileLock(cm_file_lock_t *l) {
+ osi_QAdd(&cm_freeFileLocks, &l->q);
+}
+
+/* called with scp->mx held. May release it during processing, but
+ leaves it held on exit. */
+long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
+ cm_req_t * reqp) {
+ long code = 0;
+ AFSFid tfid;
+ cm_fid_t cfid;
+ cm_conn_t * connp;
+ struct rx_connection * callp;
+ AFSVolSync volSync;
+
+ tfid.Volume = scp->fid.volume;
+ tfid.Vnode = scp->fid.vnode;
+ tfid.Unique = scp->fid.unique;
+ cfid = scp->fid;
+
+ osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
+
+ lock_ReleaseMutex(&scp->mx);
+
+ do {
+ code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
+ if (code)
+ break;
+
+ callp = cm_GetRxConn(connp);
+ code = RXAFS_SetLock(callp, &tfid, lockType,
+ &volSync);
+ rx_PutConnection(callp);
+
+ } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
+ NULL, NULL, code));
+
+ code = cm_MapRPCError(code, reqp);
+ if (code) {
+ osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
+ } else {
+ osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
+ }
+
+ lock_ObtainMutex(&scp->mx);
+
+ return code;
+}
+
+/* called with scp->mx held. Releases it during processing */
+long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
+ cm_req_t * reqp) {
+ long code = 0;
+ AFSFid tfid;
+ cm_fid_t cfid;
+ cm_conn_t * connp;
+ struct rx_connection * callp;
+ AFSVolSync volSync;
+
+ tfid.Volume = scp->fid.volume;
+ tfid.Vnode = scp->fid.vnode;
+ tfid.Unique = scp->fid.unique;
+ cfid = scp->fid;
+
+ lock_ReleaseMutex(&scp->mx);
+
+ osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
+
+ do {
+ code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
+ if (code)
+ break;
+
+ callp = cm_GetRxConn(connp);
+ code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
+ rx_PutConnection(callp);
+
+ } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
+ NULL, NULL, code));
+ code = cm_MapRPCError(code, reqp);
+ if (code)
+ osi_Log1(afsd_logp,
+ "CALL ReleaseLock FAILURE, code 0x%x", code);
+ else
+ osi_Log0(afsd_logp,
+ "CALL ReleaseLock SUCCESS");
+
+ lock_ObtainMutex(&scp->mx);
+
+ return code;
+}
+
+/* called with scp->mx held. May release it during processing, but
+ will exit with lock held.
+
+ This will return:
+
+ - 0 if the user has permission to get the specified lock for the scp
+
+ - CM_ERROR_NOACCESS if not
+
+ Any other error from cm_SyncOp will be sent down untranslated.
+
+ If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
+ phas_insert (if non-NULL) will receive a boolean value indicating
+ whether the user has INSERT permission or not.
+*/
+long cm_LockCheckPerms(cm_scache_t * scp,
+ int lock_type,
+ cm_user_t * userp,
+ cm_req_t * reqp,
+ int * phas_insert)
+{
+ long rights = 0;
+ long code = 0, code2 = 0;
+
+ /* lock permissions are slightly tricky because of the 'i' bit.
+ If the user has PRSFS_LOCK, she can read-lock the file. If the
+ user has PRSFS_WRITE, she can write-lock the file. However, if
+ the user has PRSFS_INSERT, then she can write-lock new files,
+ but not old ones. Since we don't have information about
+ whether a file is new or not, we assume that if the user owns
+ the scp, then she has the permissions that are granted by
+ PRSFS_INSERT. */
+
+ osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
+ scp, lock_type, userp);
+
+ if (lock_type == LockRead)
+ rights |= PRSFS_LOCK;
+ else if (lock_type == LockWrite)
+ rights |= PRSFS_WRITE | PRSFS_LOCK;
+ else {
+ /* hmmkay */
+ osi_assertx(FALSE, "invalid lock type");
+ return 0;
+ }
+
+ if (phas_insert)
+ *phas_insert = FALSE;
+
+ code = cm_SyncOp(scp, NULL, userp, reqp, rights,
+ CM_SCACHESYNC_GETSTATUS |
+ CM_SCACHESYNC_NEEDCALLBACK);
+
+ if (phas_insert && scp->creator == userp) {
+
+ /* If this file was created by the user, then we check for
+ PRSFS_INSERT. If the file server is recent enough, then
+ this should be sufficient for her to get a write-lock (but
+ not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
+ indicates whether a file server supports getting write
+ locks when the user only has PRSFS_INSERT.
+
+ If the file was not created by the user we skip the check
+ because the INSERT bit will not apply to this user even
+ if it is set.
+ */
+
+ code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
+ CM_SCACHESYNC_GETSTATUS |
+ CM_SCACHESYNC_NEEDCALLBACK);
+
+ if (code2 == CM_ERROR_NOACCESS) {
+ osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
+ } else {
+ *phas_insert = TRUE;
+ osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
+ }
+ }
+
+ cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+
+ osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
+
+ return code;
+}
+
+/* called with scp->mx held */
+long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
+ LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
+ cm_key_t key,
+ int allowWait, cm_user_t *userp, cm_req_t *reqp,
+ cm_file_lock_t **lockpp)
+{
+ long code = 0;
+ int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
+ cm_file_lock_t *fileLock;
+ osi_queue_t *q;
+ cm_range_t range;
+ int wait_unlock = FALSE;
+ int force_client_lock = FALSE;
+
+ osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
+ scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
+ osi_Log3(afsd_logp, "... allowWait %d key 0x%x:%x", allowWait,
+ (unsigned long)(key >> 32), (unsigned long)(key & 0xffffffff));
+
+ /*
+ A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
+
+ 3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
+ true:
+
+ 3.1 If L->LockType is exclusive then there does NOT exist a
+ ACCEPTED lock M in S->fileLocks such that _a_ in
+ (M->LOffset,+M->LLength).
+
+ 3.2 If L->LockType is shared then for each ACCEPTED lock M in
+ S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
+ M->LockType is shared.
+
+ 4. For all LOST locks M in S->fileLocks, ALL of the following are true:
+
+ 4.1 M->key != Key(C)
+
+ 4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
+ and (M->LOffset,+M->LLength) do not intersect.
+ */
+
+ range.offset = LOffset.QuadPart;
+ range.length = LLength.QuadPart;
+
+ lock_ObtainRead(&cm_scacheLock);
+
+ for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
+ fileLock =
+ (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
+
+ if (IS_LOCK_LOST(fileLock)) {
+ if (fileLock->key == key) {
+ code = CM_ERROR_BADFD;
+ break;
+ } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
+ code = CM_ERROR_WOULDBLOCK;
+ wait_unlock = TRUE;
+ break;
+ }
+ }
+
+ /* we don't need to check for deleted locks here since deleted
+ locks are dequeued from scp->fileLocks */
+ if (IS_LOCK_ACCEPTED(fileLock) &&
+ INTERSECT_RANGE(range, fileLock->range)) {
+
+ if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
+ fileLock->lockType != LockRead) {
+ wait_unlock = TRUE;
+ code = CM_ERROR_WOULDBLOCK;
+ break;
+ }
+ }
+ }
+
+ lock_ReleaseRead(&cm_scacheLock);
+
+ if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
+ if (Which == scp->serverLock ||
+ (Which == LockRead && scp->serverLock == LockWrite)) {
+
+ int has_insert = 0;
+
+ /* we already have the lock we need */
+ osi_Log3(afsd_logp, " we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
+ scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
+
+ code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
+
+ /* special case: if we don't have permission to read-lock
+ the file, then we force a clientside lock. This is to
+ compensate for applications that obtain a read-lock for
+ reading files off of directories that don't grant
+ read-locks to the user. */
+ if (code == CM_ERROR_NOACCESS && Which == LockRead) {
+
+ if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
+ osi_Log0(afsd_logp, " User has no read-lock perms, but has INSERT perms.");
+ code = 0;
+ } else {
+ osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
+ force_client_lock = TRUE;
+ }
+ }
+
+ } else if ((scp->exclusiveLocks > 0) ||
+ (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
+ int has_insert = 0;
+
+ /* We are already waiting for some other lock. We should
+ wait for the daemon to catch up instead of generating a
+ flood of SetLock calls. */
+ osi_Log3(afsd_logp, " already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
+ scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
+
+ /* see if we have permission to create the lock in the
+ first place. */
+ code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
+ if (code == 0)
+ code = CM_ERROR_WOULDBLOCK;
+ else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
+
+ if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
+ osi_Log0(afsd_logp,
+ " User has no read-lock perms, but has INSERT perms.");
+ code = CM_ERROR_WOULDBLOCK;
+ } else {
+ osi_Log0(afsd_logp,
+ " User has no read-lock perms. Forcing client-side lock");
+ force_client_lock = TRUE;
+ }
+ }
+
+ /* leave any other codes as-is */
+
+ } else {
+ int newLock;
+ int check_data_version = FALSE;
+ int has_insert = 0;
+
+ /* first check if we have permission to elevate or obtain
+ the lock. */
+ code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
+ if (code) {
+ if (code == CM_ERROR_NOACCESS && Which == LockRead &&
+ (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
+ osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
+ force_client_lock = TRUE;
+ }
+ goto check_code;
+ }
+
+ /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
+
+ if (scp->serverLock == LockRead && Which == LockWrite) {
+
+ /* We want to escalate the lock to a LockWrite.
+ * Unfortunately that's not really possible without
+ * letting go of the current lock. But for now we do
+ * it anyway. */
+
+ osi_Log0(afsd_logp,
+ " attempting to UPGRADE from LockRead to LockWrite.");
+ osi_Log1(afsd_logp,
+ " dataVersion on scp: %I64d", scp->dataVersion);
+
+ /* we assume at this point (because scp->serverLock
+ was valid) that we had a valid server lock. */
+ scp->lockDataVersion = scp->dataVersion;
+ check_data_version = TRUE;
+
+ code = cm_IntReleaseLock(scp, userp, reqp);
+
+ if (code) {
+ /* We couldn't release the lock */
+ goto check_code;
+ } else {
+ scp->serverLock = -1;
+ }
+ }
+
+ /* We need to obtain a server lock of type Which in order
+ * to assert this file lock */
+#ifndef AGGRESSIVE_LOCKS
+ newLock = Which;
+#else
+ newLock = LockWrite;
+#endif
+
+ code = cm_IntSetLock(scp, userp, newLock, reqp);
+
+#ifdef AGGRESSIVE_LOCKS
+ if ((code == CM_ERROR_WOULDBLOCK ||
+ code == CM_ERROR_NOACCESS) && newLock != Which) {
+ /* we wanted LockRead. We tried LockWrite. Now try
+ * LockRead again */
+ newLock = Which;
+
+ /* am I sane? */
+ osi_assertx(newLock == LockRead, "lock type not read");
+
+ code = cm_IntSetLock(scp, userp, newLock, reqp);
+ }
+#endif
+
+ if (code == CM_ERROR_NOACCESS) {
+ if (Which == LockRead) {
+ if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
+ long tcode;
+ /* We requested a read-lock, but we have permission to
+ * get a write-lock. Try that */
+
+ tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
+
+ if (tcode == 0) {
+ newLock = LockWrite;
+
+ osi_Log0(afsd_logp, " User has 'i' perms and the request was for a LockRead. Trying to get a LockWrite instead");
+
+ code = cm_IntSetLock(scp, userp, newLock, reqp);
+ }
+ } else {
+ osi_Log0(afsd_logp, " User has no read-lock perms. Forcing client-side lock");
+ force_client_lock = TRUE;
+ }
+ } else if (Which == LockWrite &&
+ scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
+ long tcode;
+
+ /* Special case: if the lock request was for a
+ * LockWrite and the user owns the file and we weren't
+ * allowed to obtain the serverlock, we either lost a
+ * race (the permissions changed from under us), or we
+ * have 'i' bits, but we aren't allowed to lock the
+ * file. */
+
+ /* check if we lost a race... */
+ tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
+
+ if (tcode == 0) {
+ osi_Log0(afsd_logp, " User has 'i' perms but can't obtain write locks. Using client-side locks.");
+ force_client_lock = TRUE;
+ }
+ }
+ }
+
+ if (code == 0 && check_data_version &&
+ scp->dataVersion != scp->lockDataVersion) {
+ /* We lost a race. Although we successfully obtained
+ * a lock, someone modified the file in between. The
+ * locks have all been technically lost. */
+
+ osi_Log0(afsd_logp,
+ " Data version mismatch while upgrading lock.");
+ osi_Log2(afsd_logp,
+ " Data versions before=%I64d, after=%I64d",
+ scp->lockDataVersion,
+ scp->dataVersion);
+ osi_Log1(afsd_logp,
+ " Releasing stale lock for scp 0x%x", scp);
+
+ code = cm_IntReleaseLock(scp, userp, reqp);
+
+ scp->serverLock = -1;
+
+ code = CM_ERROR_INVAL;
+ } else if (code == 0) {
+ scp->serverLock = newLock;
+ scp->lockDataVersion = scp->dataVersion;
+ }
+
+ if (code != 0 &&
+ (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
+ scp->serverLock == -1) {
+ /* Oops. We lost the lock. */
+ cm_LockMarkSCacheLost(scp);
+ }
+ }
+ } else if (code == 0) { /* server locks not enabled */
+ osi_Log0(afsd_logp,
+ " Skipping server lock for scp");
+ }
+
+ check_code:
+
+ if (code != 0 && !force_client_lock) {
+ /* Special case error translations
+
+ Applications don't expect certain errors from a
+ LockFile/UnlockFile call. We need to translate some error
+ code to codes that apps expect and handle. */
+
+ /* We shouldn't actually need to handle this case since we
+ simulate locks for RO scps anyway. */
+ if (code == CM_ERROR_READONLY) {
+ osi_Log0(afsd_logp, " Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
+ code = CM_ERROR_NOACCESS;
+ }
+ }
+
+ if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
+ force_client_lock) {
+
+ /* clear the error if we are forcing a client lock, so we
+ don't get confused later. */
+ if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
+ code = 0;
+
+ lock_ObtainWrite(&cm_scacheLock);
+ fileLock = cm_GetFileLock();
+ lock_ReleaseWrite(&cm_scacheLock);
+#ifdef DEBUG
+ fileLock->fid = scp->fid;
+#endif
+ fileLock->key = key;
+ fileLock->lockType = Which;
+ cm_HoldUser(userp);
+ fileLock->userp = userp;
+ fileLock->range = range;
+ fileLock->flags = (code == 0 ? 0 :
+ ((wait_unlock)?
+ CM_FILELOCK_FLAG_WAITUNLOCK :
+ CM_FILELOCK_FLAG_WAITLOCK));
+
+ if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
+ fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
+
+ fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
+
+ lock_ObtainWrite(&cm_scacheLock);
+ osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
+ cm_HoldSCacheNoLock(scp);
+ fileLock->scp = scp;
+ osi_QAdd(&cm_allFileLocks, &fileLock->q);
+ lock_ReleaseWrite(&cm_scacheLock);
+
+ if (code != 0) {
+ *lockpp = fileLock;
+ }
+
+ if (IS_LOCK_CLIENTONLY(fileLock)) {
+ scp->clientLocks++;
+ } else if (IS_LOCK_ACCEPTED(fileLock)) {
+ if (Which == LockRead)
+ scp->sharedLocks++;
+ else
+ scp->exclusiveLocks++;
+ }
+
+ osi_Log3(afsd_logp,
+ "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
+ fileLock, fileLock->flags, scp);
+ osi_Log4(afsd_logp,
+ " exclusives[%d] shared[%d] client[%d] serverLock[%d]",
+ scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
+ (int)(signed char) scp->serverLock);
+ } else {
+ osi_Log1(afsd_logp,
+ "cm_Lock Rejecting lock (code = 0x%x)", code);
+ }
+
+ return code;
+}
+
+static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags);
+
+/* Called with scp->mx held */
+long cm_UnlockByKey(cm_scache_t * scp,
+ cm_key_t key,
+ int flags,
+ cm_user_t * userp,
+ cm_req_t * reqp)
+{
+ long code = 0;
+ cm_file_lock_t *fileLock;
+ osi_queue_t *q, *qn;
+ int n_unlocks = 0;
+
+ osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key 0x%x:%x flags=0x%x",
+ scp,
+ (unsigned long)(key >> 32),
+ (unsigned long)(key & 0xffffffff),
+ flags);
+
+ lock_ObtainWrite(&cm_scacheLock);
+
+ for (q = scp->fileLocksH; q; q = qn) {
+ qn = osi_QNext(q);
+
+ fileLock = (cm_file_lock_t *)
+ ((char *) q - offsetof(cm_file_lock_t, fileq));
+
+#ifdef DEBUG
+ osi_Log4(afsd_logp, " Checking lock[0x%x] range[%d,+%d] type[%d]",
+ fileLock,
+ (unsigned long) fileLock->range.offset,
+ (unsigned long) fileLock->range.length,
+ fileLock->lockType);
+ osi_Log3(afsd_logp, " key[0x%x:%x] flags[0x%x]",
+ (unsigned long)(fileLock->key >> 32),
+ (unsigned long)(fileLock->key & 0xffffffff),
+ fileLock->flags);
+
+ if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
+ osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
+ osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
+ fileLock->fid.cell,
+ fileLock->fid.volume,
+ fileLock->fid.vnode,
+ fileLock->fid.unique);
+ osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
+ fileLock->scp->fid.cell,
+ fileLock->scp->fid.volume,
+ fileLock->scp->fid.vnode,
+ fileLock->scp->fid.unique);
+ osi_assertx(FALSE, "invalid fid value");
+ }
+#endif
+
+ if (!IS_LOCK_DELETED(fileLock) &&
+ cm_KeyEquals(fileLock->key, key, flags)) {
+ osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
+ fileLock->range.offset,
+ fileLock->range.length,
+ fileLock->lockType);
+
+ if (scp->fileLocksT == q)
+ scp->fileLocksT = osi_QPrev(q);
+ osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
+
+ if (IS_LOCK_CLIENTONLY(fileLock)) {
+ scp->clientLocks--;
+ } else if (IS_LOCK_ACCEPTED(fileLock)) {
+ if (fileLock->lockType == LockRead)
+ scp->sharedLocks--;
+ else
+ scp->exclusiveLocks--;
+ }
+
+ fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
+
+ cm_ReleaseUser(fileLock->userp);
+ cm_ReleaseSCacheNoLock(scp);
+
+ fileLock->userp = NULL;
+ fileLock->scp = NULL;
+
+ n_unlocks++;
+ }
+ }
+
+ lock_ReleaseWrite(&cm_scacheLock);
+
+ if (n_unlocks == 0) {
+ osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
+ osi_Log3(afsd_logp, " Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
+ scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
+
+ return 0;
+ }
+
+ osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
+
+ osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
+ osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
+ osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
+
+ if (!SERVERLOCKS_ENABLED(scp)) {
+ osi_Log0(afsd_logp, " Skipping server lock for scp");
+ goto done;
+ }
+
+ /* Ideally we would go through the rest of the locks to determine
+ * if one or more locks that were formerly in WAITUNLOCK can now
+ * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
+ * scp->sharedLocks accordingly. However, the retrying of locks
+ * in that manner is done cm_RetryLock() manually.
+ */
+
+ if (scp->serverLock == LockWrite &&
+ scp->exclusiveLocks == 0 &&
+ scp->sharedLocks > 0) {
+
+ /* The serverLock should be downgraded to LockRead */
+ osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
+
+ /* since scp->serverLock looked sane, we are going to assume
+ that we have a valid server lock. */
+ scp->lockDataVersion = scp->dataVersion;
+ osi_Log1(afsd_logp, " dataVersion on scp = %I64d", scp->dataVersion);
+
+ code = cm_IntReleaseLock(scp, userp, reqp);
+
+ if (code) {
+ /* so we couldn't release it. Just let the lock be for now */
+ code = 0;
+ goto done;
+ } else {
+ scp->serverLock = -1;
+ }
+
+ code = cm_IntSetLock(scp, userp, LockRead, reqp);
+
+ if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
+ scp->serverLock = LockRead;
+ } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
+ /* We lost a race condition. Although we have a valid
+ lock on the file, the data has changed and essentially
+ we have lost the lock we had during the transition. */
+
+ osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
+ osi_Log2(afsd_logp, " Data versions before=%I64d, after=%I64d",
+ scp->lockDataVersion,
+ scp->dataVersion);
+
+ code = cm_IntReleaseLock(scp, userp, reqp);
+
+ code = CM_ERROR_INVAL;
+ scp->serverLock = -1;
+ }
+
+ if (code != 0 &&
+ (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
+ (scp->serverLock == -1)) {
+ /* Oopsie */
+ cm_LockMarkSCacheLost(scp);
+ }
+
+ /* failure here has no bearing on the return value of
+ cm_Unlock() */
+ code = 0;
+
+ } else if (scp->serverLock != (-1) &&
+ scp->exclusiveLocks == 0 &&
+ scp->sharedLocks == 0) {
+ /* The serverLock should be released entirely */
+
+ code = cm_IntReleaseLock(scp, userp, reqp);
+
+ if (code == 0)
+ scp->serverLock = (-1);
+ }
+
+ done:
+
+ osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
+ osi_Log4(afsd_logp, " Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
+ scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
+ (int)(signed char) scp->serverLock);
+
+ return code;
+}
+
+long cm_Unlock(cm_scache_t *scp,
+ unsigned char sLockType,
+ LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
+ cm_key_t key,
+ cm_user_t *userp,
+ cm_req_t *reqp)
+{
+ long code = 0;
+ int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
+ cm_file_lock_t *fileLock;
+ osi_queue_t *q;
+ int release_userp = FALSE;
+
+ osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset %d length %d",
+ scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
+ osi_Log2(afsd_logp, "... key 0x%x:%x",
+ (unsigned long) (key >> 32), (unsigned long) (key & 0xffffffff));
+
+ lock_ObtainRead(&cm_scacheLock);
+
+ for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
+ fileLock = (cm_file_lock_t *)
+ ((char *) q - offsetof(cm_file_lock_t, fileq));
+
+#ifdef DEBUG
+ if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
+ osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
+ osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
+ fileLock->fid.cell,
+ fileLock->fid.volume,
+ fileLock->fid.vnode,
+ fileLock->fid.unique);
+ osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
+ fileLock->scp->fid.cell,
+ fileLock->scp->fid.volume,
+ fileLock->scp->fid.vnode,
+ fileLock->scp->fid.unique);
+ osi_assertx(FALSE, "invalid fid value");
+ }
+#endif
+ if (!IS_LOCK_DELETED(fileLock) &&
+ fileLock->key == key &&
+ fileLock->range.offset == LOffset.QuadPart &&
+ fileLock->range.length == LLength.QuadPart) {
+ break;
+ }
+ }
+
+ if (!q) {
+ osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
+
+ lock_ReleaseRead(&cm_scacheLock);
+
+ /* The lock didn't exist anyway. *shrug* */
+ return CM_ERROR_RANGE_NOT_LOCKED;
+ }
+
+ lock_ReleaseRead(&cm_scacheLock);
+
+ /* discard lock record */
+ lock_ObtainWrite(&cm_scacheLock);
+ if (scp->fileLocksT == q)
+ scp->fileLocksT = osi_QPrev(q);
+ osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
+
+ /*
+ * Don't delete it here; let the daemon delete it, to simplify
+ * the daemon's traversal of the list.
+ */
+
+ if (IS_LOCK_CLIENTONLY(fileLock)) {
+ scp->clientLocks--;
+ } else if (IS_LOCK_ACCEPTED(fileLock)) {
+ if (fileLock->lockType == LockRead)
+ scp->sharedLocks--;
+ else
+ scp->exclusiveLocks--;
+ }
+
+ fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
+ if (userp != NULL) {
+ cm_ReleaseUser(fileLock->userp);
+ } else {
+ userp = fileLock->userp;
+ release_userp = TRUE;
+ }
+ fileLock->userp = NULL;
+ cm_ReleaseSCacheNoLock(scp);
+ fileLock->scp = NULL;
+ lock_ReleaseWrite(&cm_scacheLock);
+
+ if (!SERVERLOCKS_ENABLED(scp)) {
+ osi_Log0(afsd_logp, " Skipping server locks for scp");
+ goto done;
+ }
+
+ /* Ideally we would go through the rest of the locks to determine
+ * if one or more locks that were formerly in WAITUNLOCK can now
+ * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
+ * scp->sharedLocks accordingly. However, the retrying of locks
+ * in that manner is done cm_RetryLock() manually.
+ */
+
+ if (scp->serverLock == LockWrite &&
+ scp->exclusiveLocks == 0 &&
+ scp->sharedLocks > 0) {
+
+ /* The serverLock should be downgraded to LockRead */
+ osi_Log0(afsd_logp, " DOWNGRADE lock from LockWrite to LockRead");
+
+ /* Since we already had a lock, we assume that there is a
+ valid server lock. */
+ scp->lockDataVersion = scp->dataVersion;
+ osi_Log1(afsd_logp, " dataVersion on scp is %I64d", scp->dataVersion);
+
+ /* before we downgrade, make sure that we have enough
+ permissions to get the read lock. */
+ code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
+ if (code != 0) {
+
+ osi_Log0(afsd_logp, " SKIPPING downgrade because user doesn't have perms to get downgraded lock");
+
+ code = 0;
+ goto done;
+ }
+
+ code = cm_IntReleaseLock(scp, userp, reqp);
+
+ if (code) {
+ /* so we couldn't release it. Just let the lock be for now */
+ code = 0;
+ goto done;
+ } else {
+ scp->serverLock = -1;
+ }
+
+ code = cm_IntSetLock(scp, userp, LockRead, reqp);
+
+ if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
+ scp->serverLock = LockRead;
+ } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
+ /* Lost a race. We obtained a new lock, but that is
+ meaningless since someone modified the file
+ inbetween. */
+
+ osi_Log0(afsd_logp,
+ "Data version mismatch while downgrading lock");
+ osi_Log2(afsd_logp,
+ " Data versions before=%I64d, after=%I64d",
+ scp->lockDataVersion,
+ scp->dataVersion);
+
+ code = cm_IntReleaseLock(scp, userp, reqp);
+
+ scp->serverLock = -1;
+ code = CM_ERROR_INVAL;
+ }
+
+ if (code != 0 &&
+ (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
+ (scp->serverLock == -1)) {
+ /* Oopsie */
+ cm_LockMarkSCacheLost(scp);
+ }
+
+ /* failure here has no bearing on the return value of
+ cm_Unlock() */
+ code = 0;
+
+ } else if (scp->serverLock != (-1) &&
+ scp->exclusiveLocks == 0 &&
+ scp->sharedLocks == 0) {
+ /* The serverLock should be released entirely */
+
+ code = cm_IntReleaseLock(scp, userp, reqp);
+
+ if (code == 0) {
+ scp->serverLock = (-1);
+ }
+ }
+
+ if (release_userp)
+ cm_ReleaseUser(userp);
+
+ done:
+
+ osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
+ osi_Log4(afsd_logp, " leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
+ scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
+ (int)(signed char) scp->serverLock);
+
+ return code;
+}
+
+/* called with scp->mx held */
+static void cm_LockMarkSCacheLost(cm_scache_t * scp)
+{
+ cm_file_lock_t *fileLock;
+ osi_queue_t *q;
+
+ osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
+
+#ifdef DEBUG
+ /* With the current code, we can't lose a lock on a RO scp */
+ osi_assertx(!(scp->flags & CM_SCACHEFLAG_RO), "CM_SCACHEFLAG_RO unexpected");
+#endif
+
+ /* cm_scacheLock needed because we are modifying fileLock->flags */
+ lock_ObtainWrite(&cm_scacheLock);
+
+ for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
+ fileLock =
+ (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
+
+ if (IS_LOCK_ACTIVE(fileLock) &&
+ !IS_LOCK_CLIENTONLY(fileLock)) {
+ if (fileLock->lockType == LockRead)
+ scp->sharedLocks--;
+ else
+ scp->exclusiveLocks--;
+
+ fileLock->flags |= CM_FILELOCK_FLAG_LOST;
+ }
+ }
+
+ scp->serverLock = -1;
+ scp->lockDataVersion = -1;
+ lock_ReleaseWrite(&cm_scacheLock);
+}
+
+/* Called with no relevant locks held */
+void cm_CheckLocks()
+{
+ osi_queue_t *q, *nq;
+ cm_file_lock_t *fileLock;
+ cm_req_t req;
+ AFSFid tfid;
+ AFSVolSync volSync;
+ cm_conn_t *connp;
+ long code;
+ struct rx_connection * callp;
+ cm_scache_t * scp;
+
+ cm_InitReq(&req);
+
+ lock_ObtainWrite(&cm_scacheLock);
+
+ cm_lockRefreshCycle++;
+
+ osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
+
+ for (q = cm_allFileLocks; q; q = nq) {
+ fileLock = (cm_file_lock_t *) q;
+ nq = osi_QNext(q);
+ code = -1;
+
+ if (IS_LOCK_DELETED(fileLock)) {
+
+ osi_QRemove(&cm_allFileLocks, q);
+ cm_PutFileLock(fileLock);
+
+ } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
+
+ /* Server locks must have been enabled for us to have
+ received an active non-client-only lock. */
+ osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
+
+ scp = fileLock->scp;
+ osi_assertx(scp != NULL, "null cm_scache_t");
+
+ cm_HoldSCacheNoLock(scp);
+
+#ifdef DEBUG
+ if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
+ osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
+ osi_Log4(afsd_logp, " fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
+ fileLock->fid.cell,
+ fileLock->fid.volume,
+ fileLock->fid.vnode,
+ fileLock->fid.unique);
+ osi_Log4(afsd_logp, " scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
+ fileLock->scp->fid.cell,
+ fileLock->scp->fid.volume,
+ fileLock->scp->fid.vnode,
+ fileLock->scp->fid.unique);
+ osi_assertx(FALSE, "invalid fid");
+ }
+#endif
+ /* Server locks are extended once per scp per refresh
+ cycle. */
+ if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
+
+ int scp_done = FALSE;
+
+ osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
+
+ lock_ReleaseWrite(&cm_scacheLock);
+ lock_ObtainMutex(&scp->mx);
+
+ /* did the lock change while we weren't holding the lock? */
+ if (!IS_LOCK_ACTIVE(fileLock))
+ goto post_syncopdone;
+
+ code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
+ CM_SCACHESYNC_NEEDCALLBACK
+ | CM_SCACHESYNC_GETSTATUS
+ | CM_SCACHESYNC_LOCK);
+
+ if (code) {
+ osi_Log1(smb_logp,
+ "cm_CheckLocks SyncOp failure code 0x%x", code);
+ goto post_syncopdone;
+ }
+
+ /* cm_SyncOp releases scp->mx during which the lock
+ may get released. */
+ if (!IS_LOCK_ACTIVE(fileLock))
+ goto pre_syncopdone;
+
+ if (scp->serverLock != -1) {
+ cm_fid_t cfid;
+ cm_user_t * userp;
+
+ tfid.Volume = scp->fid.volume;
+ tfid.Vnode = scp->fid.vnode;
+ tfid.Unique = scp->fid.unique;
+ cfid = scp->fid;
+ userp = fileLock->userp;
+
+ osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
+ fileLock,
+ scp,
+ (int) scp->serverLock);
+
+ lock_ReleaseMutex(&scp->mx);
+
+ do {
+ code = cm_ConnFromFID(&cfid, userp,
+ &req, &connp);
+ if (code)
+ break;
+
+ callp = cm_GetRxConn(connp);
+ code = RXAFS_ExtendLock(callp, &tfid,
+ &volSync);
+ rx_PutConnection(callp);
+
+ osi_Log1(afsd_logp, " ExtendLock returns %d", code);
+
+ } while (cm_Analyze(connp, userp, &req,
+ &cfid, &volSync, NULL, NULL,
+ code));
+
+ code = cm_MapRPCError(code, &req);
+
+ lock_ObtainMutex(&scp->mx);
+
+ if (code) {
+ osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
+ } else {
+ osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
+ scp->lockDataVersion = scp->dataVersion;
+ }
+
+ if ((code == EINVAL || code == CM_ERROR_INVAL) &&
+ scp->lockDataVersion == scp->dataVersion) {
+ int lockType;
+
+ lockType =
+ (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
+
+ /* we might still have a chance to obtain a
+ new lock */
+
+ code = cm_IntSetLock(scp, userp, lockType, &req);
+
+ if (code) {
+ code = CM_ERROR_INVAL;
+ } else if (scp->lockDataVersion != scp->dataVersion) {
+
+ /* now check if we still have the file at
+ the right data version. */
+ osi_Log1(afsd_logp,
+ "Data version mismatch on scp 0x%p",
+ scp);
+ osi_Log2(afsd_logp,
+ " Data versions: before=%I64d, after=%I64d",
+ scp->lockDataVersion,
+ scp->dataVersion);
+
+ code = cm_IntReleaseLock(scp, userp, &req);
+
+ code = CM_ERROR_INVAL;
+ }
+ }
+
+ if (code == EINVAL || code == CM_ERROR_INVAL) {
+ cm_LockMarkSCacheLost(scp);
+ }
+
+ } else {
+ /* interestingly, we have found an active lock
+ belonging to an scache that has no
+ serverLock */
+ cm_LockMarkSCacheLost(scp);
+ }
+
+ scp_done = TRUE;
+
+ pre_syncopdone:
+
+ cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
+ post_syncopdone:
+ lock_ReleaseMutex(&scp->mx);
+
+ lock_ObtainWrite(&cm_scacheLock);
+
+ if (code == 0) {
+ fileLock->lastUpdate = time(NULL);
+ }
+
+ if (scp_done)
+ scp->lastRefreshCycle = cm_lockRefreshCycle;
+
+ } else {
+ /* we have already refreshed the locks on this scp */
+ fileLock->lastUpdate = time(NULL);
+ }
+
+ cm_ReleaseSCacheNoLock(scp);
+
+ } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
+ /* TODO: Check callbacks */
+ }
+ }
+
+ lock_ReleaseWrite(&cm_scacheLock);
+ osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
+}
+
+/* NOT called with scp->mx held. */
+long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
+{
+ long code = 0;
+ cm_scache_t *scp = NULL;
+ cm_file_lock_t *fileLock;
+ osi_queue_t *q;
+ cm_req_t req;
+ int newLock = -1;
+ int force_client_lock = FALSE;
+ int has_insert = FALSE;
+ int check_data_version = FALSE;
+
+ cm_InitReq(&req);
+
+ if (client_is_dead) {
+ code = CM_ERROR_TIMEDOUT;
+ goto updateLock;
+ }
+
+ lock_ObtainRead(&cm_scacheLock);
+
+ osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
+ osi_Log4(afsd_logp, " offset(%x:%x) length(%x:%x)",
+ (unsigned)(oldFileLock->range.offset >> 32),
+ (unsigned)(oldFileLock->range.offset & 0xffffffff),
+ (unsigned)(oldFileLock->range.length >> 32),
+ (unsigned)(oldFileLock->range.length & 0xffffffff));
+ osi_Log3(afsd_logp, " key(%x:%x) flags=%x",
+ (unsigned)(oldFileLock->key >> 32),
+ (unsigned)(oldFileLock->key & 0xffffffff),
+ (unsigned)(oldFileLock->flags));
+
+ /* if the lock has already been granted, then we have nothing to do */
+ if (IS_LOCK_ACTIVE(oldFileLock)) {
+ lock_ReleaseRead(&cm_scacheLock);
+ osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
+ return 0;
+ }
+
+ /* we can't do anything with lost or deleted locks at the moment. */
+ if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
+ code = CM_ERROR_BADFD;
+ osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
+ lock_ReleaseRead(&cm_scacheLock);
+ goto updateLock;
+ }
+
+ scp = oldFileLock->scp;
+
+ osi_assertx(scp != NULL, "null cm_scache_t");
+
+ lock_ReleaseRead(&cm_scacheLock);
+ lock_ObtainMutex(&scp->mx);
+
+ code = cm_LockCheckPerms(scp, oldFileLock->lockType,
+ oldFileLock->userp,
+ &req, &has_insert);
+
+ if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
+ if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
+ force_client_lock = TRUE;
+ }
+ code = 0;
+ } else if (code) {
+ lock_ReleaseMutex(&scp->mx);
+ return code;
+ }
+
+ lock_ObtainWrite(&cm_scacheLock);
+
+ /* Check if we already have a sufficient server lock to allow this
+ lock to go through. */
+ if (IS_LOCK_WAITLOCK(oldFileLock) &&
+ (!SERVERLOCKS_ENABLED(scp) ||
+ scp->serverLock == oldFileLock->lockType ||
+ scp->serverLock == LockWrite)) {
+
+ oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
+
+ if (SERVERLOCKS_ENABLED(scp)) {
+ osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock. Granting",
+ (int) scp->serverLock);
+ } else {
+ osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
+ }
+
+ lock_ReleaseWrite(&cm_scacheLock);
+ lock_ReleaseMutex(&scp->mx);
+
+ return 0;
+ }
+
+ if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
+
+ /* check if the conflicting locks have dissappeared already */
+ for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
+
+ fileLock = (cm_file_lock_t *)
+ ((char *) q - offsetof(cm_file_lock_t, fileq));
+
+ if (IS_LOCK_LOST(fileLock)) {
+ if (fileLock->key == oldFileLock->key) {
+ code = CM_ERROR_BADFD;
+ oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
+ osi_Log1(afsd_logp, " found lost lock %p for same key. Marking lock as lost",
+ fileLock);
+ break;
+ } else if (fileLock->lockType == LockWrite &&
+ INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
+ osi_Log1(afsd_logp, " found conflicting LOST lock %p", fileLock);
+ code = CM_ERROR_WOULDBLOCK;
+ break;
+ }
+ }
+
+ if (IS_LOCK_ACCEPTED(fileLock) &&
+ INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
+
+ if (oldFileLock->lockType != LockRead ||
+ fileLock->lockType != LockRead) {
+
+ osi_Log1(afsd_logp, " found conflicting lock %p", fileLock);
+ code = CM_ERROR_WOULDBLOCK;
+ break;
+ }
+ }
+ }
+ }
+
+ if (code != 0) {
+ lock_ReleaseWrite(&cm_scacheLock);
+ lock_ReleaseMutex(&scp->mx);
+
+ goto handleCode;
+ }
+
+ /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
+ If it is WAITUNLOCK, then we didn't find any conflicting lock
+ but we haven't verfied whether the serverLock is sufficient to
+ assert it. If it is WAITLOCK, then the serverLock is
+ insufficient to assert it. Eitherway, we are ready to accept
+ the lock as either ACTIVE or WAITLOCK depending on the
+ serverLock. */
+
+ /* First, promote the WAITUNLOCK to a WAITLOCK */
+ if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
+ if (oldFileLock->lockType == LockRead)
+ scp->sharedLocks++;
+ else
+ scp->exclusiveLocks++;
+
+ oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
+ oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
+ }
+
+ osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
+
+ if (force_client_lock ||
+ !SERVERLOCKS_ENABLED(scp) ||
+ scp->serverLock == oldFileLock->lockType ||
+ (oldFileLock->lockType == LockRead &&
+ scp->serverLock == LockWrite)) {
+
+ oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
+
+ if ((force_client_lock ||
+ !SERVERLOCKS_ENABLED(scp)) &&
+ !IS_LOCK_CLIENTONLY(oldFileLock)) {
+
+ oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
+
+ if (oldFileLock->lockType == LockRead)
+ scp->sharedLocks--;
+ else
+ scp->exclusiveLocks--;
+
+ scp->clientLocks++;
+ }
+
+ lock_ReleaseWrite(&cm_scacheLock);
+ lock_ReleaseMutex(&scp->mx);
+
+ return 0;
+
+ } else {
+ cm_user_t * userp;
+
+ code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
+ CM_SCACHESYNC_NEEDCALLBACK
+ | CM_SCACHESYNC_GETSTATUS
+ | CM_SCACHESYNC_LOCK);
+ if (code) {
+ osi_Log1(smb_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
+ lock_ReleaseWrite(&cm_scacheLock);
+ goto post_syncopdone;
+ }
+
+ if (!IS_LOCK_WAITLOCK(oldFileLock))
+ goto pre_syncopdone;
+
+ userp = oldFileLock->userp;
+
+#ifndef AGGRESSIVE_LOCKS
+ newLock = oldFileLock->lockType;
+#else
+ newLock = LockWrite;
+#endif
+
+ if (has_insert) {
+ /* if has_insert is non-zero, then:
+ - the lock a LockRead
+ - we don't have permission to get a LockRead
+ - we do have permission to get a LockWrite
+ - the server supports VICED_CAPABILITY_WRITELOCKACL
+ */
+
+ newLock = LockWrite;
+ }
+
+ lock_ReleaseWrite(&cm_scacheLock);
+
+ /* when we get here, either we have a read-lock and want a
+ write-lock or we don't have any locks and we want some
+ lock. */
+
+ if (scp->serverLock == LockRead) {
+
+ osi_assertx(newLock == LockWrite, "!LockWrite");
+
+ osi_Log0(afsd_logp, " Attempting to UPGRADE from LockRead to LockWrite");
+
+ scp->lockDataVersion = scp->dataVersion;
+ check_data_version = TRUE;
+
+ code = cm_IntReleaseLock(scp, userp, &req);
+
+ if (code)
+ goto pre_syncopdone;
+ else
+ scp->serverLock = -1;
+ }
+
+ code = cm_IntSetLock(scp, userp, newLock, &req);
+
+ if (code == 0) {
+ if (scp->dataVersion != scp->lockDataVersion) {
+ /* we lost a race. too bad */
+
+ osi_Log0(afsd_logp,
+ " Data version mismatch while upgrading lock.");
+ osi_Log2(afsd_logp,
+ " Data versions before=%I64d, after=%I64d",
+ scp->lockDataVersion,
+ scp->dataVersion);
+ osi_Log1(afsd_logp,
+ " Releasing stale lock for scp 0x%x", scp);
+
+ code = cm_IntReleaseLock(scp, userp, &req);
+
+ scp->serverLock = -1;
+
+ code = CM_ERROR_INVAL;
+
+ cm_LockMarkSCacheLost(scp);
+ } else {
+ scp->serverLock = newLock;
+ }
+ }
+
+ pre_syncopdone:
+ cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+ post_syncopdone:
+ ;
}
handleCode:
if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
- lock_ObtainMutex(&scp->mx);
- osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
- lock_ReleaseMutex(&scp->mx);
+ lock_ObtainWrite(&cm_scacheLock);
+ if (scp->fileLocksT == &oldFileLock->fileq)
+ scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
+ osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
+ lock_ReleaseWrite(&cm_scacheLock);
}
+ lock_ReleaseMutex(&scp->mx);
+
+ updateLock:
lock_ObtainWrite(&cm_scacheLock);
- if (code == 0)
- oldFileLock->flags = 0;
- else if (code != CM_ERROR_WOULDBLOCK) {
- oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
+ if (code == 0) {
+ oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
+ } else if (code != CM_ERROR_WOULDBLOCK) {
+ oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
cm_ReleaseUser(oldFileLock->userp);
oldFileLock->userp = NULL;
+ if (oldFileLock->scp) {
+ cm_ReleaseSCacheNoLock(oldFileLock->scp);
+ oldFileLock->scp = NULL;
+ }
}
lock_ReleaseWrite(&cm_scacheLock);
return code;
}
+
+cm_key_t cm_GenerateKey(unsigned int session_id, unsigned long process_id, unsigned int file_id)
+{
+#ifdef DEBUG
+ osi_assertx((process_id & 0xffffffff) == process_id, "unexpected process_id");
+ osi_assertx((session_id & 0xffff) == session_id, "unexpected session_id");
+ osi_assertx((file_id & 0xffff) == file_id, "unexpected file_id");
+#endif
+
+ return
+ (((cm_key_t) (process_id & 0xffffffff)) << 32) |
+ (((cm_key_t) (session_id & 0xffff)) << 16) |
+ (((cm_key_t) (file_id & 0xffff)));
+}
+
+static int cm_KeyEquals(cm_key_t k1, cm_key_t k2, int flags)
+{
+ if (flags & CM_UNLOCK_BY_FID) {
+ return ((k1 & 0xffffffff) == (k2 & 0xffffffff));
+ } else {
+ return (k1 == k2);
+ }
+}
+
+void cm_ReleaseAllLocks(void)
+{
+ cm_scache_t *scp;
+ cm_req_t req;
+ cm_user_t *userp;
+ cm_key_t key;
+ cm_file_lock_t *fileLock;
+ unsigned int i;
+
+ for (i = 0; i < cm_data.scacheHashTableSize; i++)
+ {
+ for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
+ while (scp->fileLocksH != NULL) {
+ lock_ObtainMutex(&scp->mx);
+ lock_ObtainWrite(&cm_scacheLock);
+ if (!scp->fileLocksH) {
+ lock_ReleaseWrite(&cm_scacheLock);
+ lock_ReleaseMutex(&scp->mx);
+ break;
+ }
+ fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
+ userp = fileLock->userp;
+ cm_HoldUser(userp);
+ key = fileLock->key;
+ cm_HoldSCacheNoLock(scp);
+ lock_ReleaseWrite(&cm_scacheLock);
+ cm_UnlockByKey(scp, key, 0, userp, &req);
+ cm_ReleaseSCache(scp);
+ cm_ReleaseUser(userp);
+ lock_ReleaseMutex(&scp->mx);
+ }
+ }
+ }
+}