windows-smb-and-symlinks-20041213
[openafs.git] / src / WINNT / afsd / smb3.c
index 44acc8d..9266d37 100644 (file)
 
 #ifndef DJGPP
 #include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <lmaccess.h>
 #endif /* !DJGPP */
 #include <stdlib.h>
 #include <malloc.h>
 #include <string.h>
 #include <stdio.h>
 #include <time.h>
-
 #include <osi.h>
 
 #include "afsd.h"
@@ -32,6 +34,8 @@ osi_mutex_t smb_Dir_Watch_Lock;
 
 smb_tran2Dispatch_t smb_tran2DispatchTable[SMB_TRAN2_NOPCODES];
 
+smb_tran2Dispatch_t smb_rapDispatchTable[SMB_RAP_NOPCODES];
+
 /* protected by the smb_globalLock */
 smb_tran2Packet_t *smb_tran2AssemblyQueuep;
 
@@ -39,18 +43,18 @@ smb_tran2Packet_t *smb_tran2AssemblyQueuep;
  * request */
 cm_user_t *smb_GetTran2User(smb_vc_t *vcp, smb_tran2Packet_t *inp)
 {
-       smb_user_t *uidp;
+    smb_user_t *uidp;
     cm_user_t *up = NULL;
         
     uidp = smb_FindUID(vcp, inp->uid, 0);
     if (!uidp) return NULL;
         
-       lock_ObtainMutex(&uidp->mx);
+    lock_ObtainMutex(&uidp->mx);
     if (uidp->unp) {
         up = uidp->unp->userp;
         cm_HoldUser(up);
     }
-       lock_ReleaseMutex(&uidp->mx);
+    lock_ReleaseMutex(&uidp->mx);
 
     smb_ReleaseUID(uidp);
 
@@ -64,77 +68,721 @@ cm_user_t *smb_GetTran2User(smb_vc_t *vcp, smb_tran2Packet_t *inp)
  */
 unsigned long smb_ExtAttributes(cm_scache_t *scp)
 {
-       unsigned long attrs;
-
-       if (scp->fileType == CM_SCACHETYPE_DIRECTORY
-           || scp->fileType == CM_SCACHETYPE_MOUNTPOINT)
-               attrs = SMB_ATTR_DIRECTORY;
-       else
-               attrs = 0;
-       /*
-        * We used to mark a file RO if it was in an RO volume, but that
-        * turns out to be impolitic in NT.  See defect 10007.
-        */
+    unsigned long attrs;
+
+    if (scp->fileType == CM_SCACHETYPE_DIRECTORY ||
+        scp->fileType == CM_SCACHETYPE_MOUNTPOINT) 
+    {
+        attrs = SMB_ATTR_DIRECTORY;
+#ifdef SPECIAL_FOLDERS
+        attrs |= SMB_ATTR_SYSTEM;              /* FILE_ATTRIBUTE_SYSTEM */
+#endif /* SPECIAL_FOLDERS */
+    } else
+        attrs = 0;
+    /*
+     * We used to mark a file RO if it was in an RO volume, but that
+     * turns out to be impolitic in NT.  See defect 10007.
+     */
 #ifdef notdef
-       if ((scp->unixModeBits & 0222) == 0 || (scp->flags & CM_SCACHEFLAG_RO))
+    if ((scp->unixModeBits & 0222) == 0 || (scp->flags & CM_SCACHEFLAG_RO))
 #endif
        if ((scp->unixModeBits & 0222) == 0)
-               attrs |= SMB_ATTR_READONLY;             /* Read-only */
+            attrs |= SMB_ATTR_READONLY;                /* Read-only */
 
-       if (attrs == 0)
-               attrs = SMB_ATTR_NORMAL;                /* FILE_ATTRIBUTE_NORMAL */
+    if (attrs == 0)
+        attrs = SMB_ATTR_NORMAL;               /* FILE_ATTRIBUTE_NORMAL */
 
-       return attrs;
+    return attrs;
 }
 
 int smb_V3IsStarMask(char *maskp)
 {
     char tc;
 
-       while (tc = *maskp++)
+    while (tc = *maskp++)
         if (tc == '?' || tc == '*') 
             return 1;
-       return 0;
+    return 0;
 }
 
 unsigned char *smb_ParseString(unsigned char *inp, char **chainpp)
 {
     if (chainpp) {
-               /* skip over null-terminated string */
-               *chainpp = inp + strlen(inp) + 1;
+        /* skip over null-terminated string */
+        *chainpp = inp + strlen(inp) + 1;
     }
     return inp;
 }   
 
+/*DEBUG do not checkin*/
+void OutputDebugF(char * format, ...) {
+    va_list args;
+    int len;
+    char * buffer;
+
+    va_start( args, format );
+    len = _vscprintf( format, args ) // _vscprintf doesn't count
+                               + 3; // terminating '\0' + '\n'
+    buffer = malloc( len * sizeof(char) );
+    vsprintf( buffer, format, args );
+    osi_Log0(smb_logp, osi_LogSaveString(smb_logp, buffer));
+    strcat(buffer, "\n");
+    OutputDebugString(buffer);
+    free( buffer );
+}
+
+void OutputDebugHexDump(unsigned char * buffer, int len) {
+    int i,j,k;
+    char buf[256];
+    static char tr[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+    OutputDebugF("Hexdump length [%d]",len);
+
+    for (i=0;i<len;i++) {
+        if(!(i%16)) {
+            if(i) {
+                osi_Log0(smb_logp, osi_LogSaveString(smb_logp, buf));
+                strcat(buf,"\n");
+                OutputDebugString(buf);
+            }
+            sprintf(buf,"%5x",i);
+            memset(buf+5,' ',80);
+            buf[85] = 0;
+        }
+
+        j = (i%16);
+        j = j*3 + 7 + ((j>7)?1:0);
+        k = buffer[i];
+
+        buf[j] = tr[k / 16]; buf[j+1] = tr[k % 16];
+
+        j = (i%16);
+        j = j + 56 + ((j>7)?1:0);
+
+        buf[j] = (k>32 && k<127)?k:'.';
+    }    
+    if(i) {
+        osi_Log0(smb_logp, osi_LogSaveString(smb_logp, buf));
+        strcat(buf,"\n");
+        OutputDebugString(buf);
+    }   
+}
+/**/
+
+#define SMB_EXT_SEC_PACKAGE_NAME "Negotiate"
+void smb_NegotiateExtendedSecurity(void ** secBlob, int * secBlobLength) {
+    SECURITY_STATUS status, istatus;
+    CredHandle creds = {0,0};
+    TimeStamp expiry;
+    SecBufferDesc secOut;
+    SecBuffer secTok;
+    CtxtHandle ctx;
+    ULONG flags;
+
+    *secBlob = NULL;
+    *secBlobLength = 0;
+
+    OutputDebugF("Negotiating Extended Security");
+
+    status = AcquireCredentialsHandle( NULL,
+                                       SMB_EXT_SEC_PACKAGE_NAME,
+                                       SECPKG_CRED_INBOUND,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       &creds,
+                                       &expiry);
+
+    if (status != SEC_E_OK) {       
+        /* Really bad. We return an empty security blob */
+        OutputDebugF("AcquireCredentialsHandle failed with %lX", status);
+        goto nes_0;
+    }
+
+    secOut.cBuffers = 1;
+    secOut.pBuffers = &secTok;
+    secOut.ulVersion = SECBUFFER_VERSION;
+
+    secTok.BufferType = SECBUFFER_TOKEN;
+    secTok.cbBuffer = 0;
+    secTok.pvBuffer = NULL;
+
+    ctx.dwLower = ctx.dwUpper = 0;
+
+    status = AcceptSecurityContext( &creds,
+                                    NULL,
+                                    NULL,
+                                    ASC_REQ_CONNECTION | ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOCATE_MEMORY,
+                                    SECURITY_NETWORK_DREP,
+                                    &ctx,
+                                    &secOut,
+                                    &flags,
+                                    &expiry
+                                    );
+
+    if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
+        OutputDebugF("Completing token...");
+        istatus = CompleteAuthToken(&ctx, &secOut);
+        if ( istatus != SEC_E_OK )
+            OutputDebugF("Token completion failed: %x", istatus);
+    }
+
+    if (status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) {
+        if (secTok.pvBuffer) {
+            *secBlobLength = secTok.cbBuffer;
+            *secBlob = malloc( secTok.cbBuffer );
+            memcpy(*secBlob, secTok.pvBuffer, secTok.cbBuffer );
+        }
+    } else {
+        if ( status != SEC_E_OK )
+            OutputDebugF("AcceptSecurityContext status != CONTINUE  %lX", status);
+    }
+
+    /* Discard partial security context */
+    DeleteSecurityContext(&ctx);
+
+    if (secTok.pvBuffer) FreeContextBuffer( secTok.pvBuffer );
+
+    /* Discard credentials handle.  We'll reacquire one when we get the session setup X */
+    FreeCredentialsHandle(&creds);
+
+  nes_0:
+    return;
+}
+
+struct smb_ext_context {
+    CredHandle creds;
+    CtxtHandle ctx;
+    int partialTokenLen;
+    void * partialToken;
+};      
+
+long smb_AuthenticateUserExt(smb_vc_t * vcp, char * usern, char * secBlobIn, int secBlobInLength, char ** secBlobOut, int * secBlobOutLength) {
+    SECURITY_STATUS status, istatus;
+    CredHandle creds;
+    TimeStamp expiry;
+    long code = 0;
+    SecBufferDesc secBufIn;
+    SecBuffer secTokIn;
+    SecBufferDesc secBufOut;
+    SecBuffer secTokOut;
+    CtxtHandle ctx;
+    struct smb_ext_context * secCtx = NULL;
+    struct smb_ext_context * newSecCtx = NULL;
+    void * assembledBlob = NULL;
+    int assembledBlobLength = 0;
+    ULONG flags;
+
+    OutputDebugF("In smb_AuthenticateUserExt");
+
+    *secBlobOut = NULL;
+    *secBlobOutLength = 0;
+
+    if (vcp->flags & SMB_VCFLAG_AUTH_IN_PROGRESS) {
+        secCtx = vcp->secCtx;
+        lock_ObtainMutex(&vcp->mx);
+        vcp->flags &= ~SMB_VCFLAG_AUTH_IN_PROGRESS;
+        vcp->secCtx = NULL;
+        lock_ReleaseMutex(&vcp->mx);
+    }
+
+    if (secBlobIn) {
+        OutputDebugF("Received incoming token:");
+        OutputDebugHexDump(secBlobIn,secBlobInLength);
+    }
+    
+    if (secCtx) {
+        OutputDebugF("Continuing with existing context.");             
+        creds = secCtx->creds;
+        ctx = secCtx->ctx;
+
+        if (secCtx->partialToken) {
+            assembledBlobLength = secCtx->partialTokenLen + secBlobInLength;
+            assembledBlob = malloc(assembledBlobLength);
+            memcpy(assembledBlob,secCtx->partialToken, secCtx->partialTokenLen);
+            memcpy(((BYTE *)assembledBlob) + secCtx->partialTokenLen, secBlobIn, secBlobInLength);
+        }
+    } else {
+        status = AcquireCredentialsHandle( NULL,
+                                           SMB_EXT_SEC_PACKAGE_NAME,
+                                           SECPKG_CRED_INBOUND,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           &creds,
+                                           &expiry);
+
+        if (status != SEC_E_OK) {
+            OutputDebugF("Can't acquire Credentials handle [%lX]", status);
+            code = CM_ERROR_BADPASSWORD; /* means "try again when I'm sober" */
+            goto aue_0;
+        }
+
+        ctx.dwLower = 0;
+        ctx.dwUpper = 0;
+    }
+
+    secBufIn.cBuffers = 1;
+    secBufIn.pBuffers = &secTokIn;
+    secBufIn.ulVersion = SECBUFFER_VERSION;
+
+    secTokIn.BufferType = SECBUFFER_TOKEN;
+    if (assembledBlob) {
+        secTokIn.cbBuffer = assembledBlobLength;
+        secTokIn.pvBuffer = assembledBlob;
+    } else {
+        secTokIn.cbBuffer = secBlobInLength;
+        secTokIn.pvBuffer = secBlobIn;
+    }
+
+    secBufOut.cBuffers = 1;
+    secBufOut.pBuffers = &secTokOut;
+    secBufOut.ulVersion = SECBUFFER_VERSION;
+
+    secTokOut.BufferType = SECBUFFER_TOKEN;
+    secTokOut.cbBuffer = 0;
+    secTokOut.pvBuffer = NULL;
+
+    status = AcceptSecurityContext( &creds,
+                                    ((secCtx)?&ctx:NULL),
+                                    &secBufIn,
+                                    ASC_REQ_CONNECTION | ASC_REQ_EXTENDED_ERROR        | ASC_REQ_ALLOCATE_MEMORY,
+                                    SECURITY_NETWORK_DREP,
+                                    &ctx,
+                                    &secBufOut,
+                                    &flags,
+                                    &expiry
+                                    );
+
+    if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
+        OutputDebugF("Completing token...");
+        istatus = CompleteAuthToken(&ctx, &secBufOut);
+        if ( istatus != SEC_E_OK )
+            OutputDebugF("Token completion failed: %lX", istatus);
+    }
+
+    if (status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) {
+        OutputDebugF("Continue needed");
+
+        newSecCtx = malloc(sizeof(*newSecCtx));
+
+        newSecCtx->creds = creds;
+        newSecCtx->ctx = ctx;
+        newSecCtx->partialToken = NULL;
+        newSecCtx->partialTokenLen = 0;
+
+        lock_ObtainMutex( &vcp->mx );
+        vcp->flags |= SMB_VCFLAG_AUTH_IN_PROGRESS;
+        vcp->secCtx = newSecCtx;
+        lock_ReleaseMutex( &vcp->mx );
+
+        code = CM_ERROR_GSSCONTINUE;
+    }
+
+    if ((status == SEC_I_COMPLETE_NEEDED || status == SEC_E_OK || 
+          status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) && 
+         secTokOut.pvBuffer) {
+        OutputDebugF("Need to send token back to client");
+
+        *secBlobOutLength = secTokOut.cbBuffer;
+        *secBlobOut = malloc(secTokOut.cbBuffer);
+        memcpy(*secBlobOut, secTokOut.pvBuffer, secTokOut.cbBuffer);
+
+        OutputDebugF("Outgoing token:");
+        OutputDebugHexDump(*secBlobOut,*secBlobOutLength);
+    } else if (status == SEC_E_INCOMPLETE_MESSAGE) {
+        OutputDebugF("Incomplete message");
+
+        newSecCtx = malloc(sizeof(*newSecCtx));
+
+        newSecCtx->creds = creds;
+        newSecCtx->ctx = ctx;
+        newSecCtx->partialToken = malloc(secTokOut.cbBuffer);
+        memcpy(newSecCtx->partialToken, secTokOut.pvBuffer, secTokOut.cbBuffer);
+        newSecCtx->partialTokenLen = secTokOut.cbBuffer;
+
+        lock_ObtainMutex( &vcp->mx );
+        vcp->flags |= SMB_VCFLAG_AUTH_IN_PROGRESS;
+        vcp->secCtx = newSecCtx;
+        lock_ReleaseMutex( &vcp->mx );
+
+        code = CM_ERROR_GSSCONTINUE;
+    }
+
+    if (status == SEC_E_OK || status == SEC_I_COMPLETE_NEEDED) {
+        /* woo hoo! */
+        SecPkgContext_Names names;
+
+        OutputDebugF("Authentication completed");
+        OutputDebugF("Returned flags : [%lX]", flags);
+
+        if (!QueryContextAttributes(&ctx, SECPKG_ATTR_NAMES, &names)) {
+            OutputDebugF("Received name [%s]", names.sUserName);
+            strcpy(usern, names.sUserName);
+            strlwr(usern); /* in tandem with smb_GetNormalizedUsername */
+            FreeContextBuffer(names.sUserName);
+        } else {
+            /* Force the user to retry if the context is invalid */
+            OutputDebugF("QueryContextAttributes Names failed [%x]", GetLastError());
+            code = CM_ERROR_BADPASSWORD; 
+        }
+    } else if (!code) {
+        switch ( status ) {
+        case SEC_E_INVALID_TOKEN:
+            OutputDebugF("Returning bad password :: INVALID_TOKEN");
+            break;
+        case SEC_E_INVALID_HANDLE:
+            OutputDebugF("Returning bad password :: INVALID_HANDLE");
+            break;
+        case SEC_E_LOGON_DENIED:
+            OutputDebugF("Returning bad password :: LOGON_DENIED");
+            break;
+        case SEC_E_UNKNOWN_CREDENTIALS:
+            OutputDebugF("Returning bad password :: UNKNOWN_CREDENTIALS");
+            break;
+        case SEC_E_NO_CREDENTIALS:
+            OutputDebugF("Returning bad password :: NO_CREDENTIALS");
+            break;
+        case SEC_E_CONTEXT_EXPIRED:
+            OutputDebugF("Returning bad password :: CONTEXT_EXPIRED");
+            break;
+        case SEC_E_INCOMPLETE_CREDENTIALS:
+            OutputDebugF("Returning bad password :: INCOMPLETE_CREDENTIALS");
+            break;
+        case SEC_E_WRONG_PRINCIPAL:
+            OutputDebugF("Returning bad password :: WRONG_PRINCIPAL");
+            break;
+        case SEC_E_TIME_SKEW:
+            OutputDebugF("Returning bad password :: TIME_SKEW");
+            break;
+        default:
+            OutputDebugF("Returning bad password :: Status == %lX", status);
+        }
+        code = CM_ERROR_BADPASSWORD;
+    }
+
+    if (secCtx) {
+        if (secCtx->partialToken) free(secCtx->partialToken);
+        free(secCtx);
+    }
+
+    if (assembledBlob) {
+        free(assembledBlob);
+    }
+
+    if (secTokOut.pvBuffer)
+        FreeContextBuffer(secTokOut.pvBuffer);
+
+    if (code != CM_ERROR_GSSCONTINUE) {
+        DeleteSecurityContext(&ctx);
+        FreeCredentialsHandle(&creds);
+    }
+
+  aue_0:
+    return code;
+}
+
+#define P_LEN 256
+#define P_RESP_LEN 128
+
+/* LsaLogonUser expects input parameters to be in a contiguous block of memory. 
+   So put stuff in a struct. */
+struct Lm20AuthBlob {
+    MSV1_0_LM20_LOGON lmlogon;
+    BYTE ciResponse[P_RESP_LEN];    /* Unicode representation */
+    BYTE csResponse[P_RESP_LEN];    /* ANSI representation */
+    WCHAR accountNameW[P_LEN];
+    WCHAR primaryDomainW[P_LEN];
+    WCHAR workstationW[MAX_COMPUTERNAME_LENGTH + 1];
+    TOKEN_GROUPS tgroups;
+    TOKEN_SOURCE tsource;
+};
+
+long smb_AuthenticateUserLM(smb_vc_t *vcp, char * accountName, char * primaryDomain, char * ciPwd, unsigned ciPwdLength, char * csPwd, unsigned csPwdLength) 
+{
+    NTSTATUS nts, ntsEx;
+    struct Lm20AuthBlob lmAuth;
+    PMSV1_0_LM20_LOGON_PROFILE lmprofilep;
+    QUOTA_LIMITS quotaLimits;
+    DWORD size;
+    ULONG lmprofilepSize;
+    LUID lmSession;
+    HANDLE lmToken;
+
+    OutputDebugF("In smb_AuthenticateUser for user [%s] domain [%s]", accountName, primaryDomain);
+    OutputDebugF("ciPwdLength is %d and csPwdLength is %d", ciPwdLength, csPwdLength);
+
+    if (ciPwdLength > P_RESP_LEN || csPwdLength > P_RESP_LEN) {
+        OutputDebugF("ciPwdLength or csPwdLength is too long");
+        return CM_ERROR_BADPASSWORD;
+    }
+
+    memset(&lmAuth,0,sizeof(lmAuth));
+
+    lmAuth.lmlogon.MessageType = MsV1_0NetworkLogon;
+       
+    lmAuth.lmlogon.LogonDomainName.Buffer = lmAuth.primaryDomainW;
+    mbstowcs(lmAuth.primaryDomainW, primaryDomain, P_LEN);
+    lmAuth.lmlogon.LogonDomainName.Length = wcslen(lmAuth.primaryDomainW) * sizeof(WCHAR);
+    lmAuth.lmlogon.LogonDomainName.MaximumLength = P_LEN * sizeof(WCHAR);
+
+    lmAuth.lmlogon.UserName.Buffer = lmAuth.accountNameW;
+    mbstowcs(lmAuth.accountNameW, accountName, P_LEN);
+    lmAuth.lmlogon.UserName.Length = wcslen(lmAuth.accountNameW) * sizeof(WCHAR);
+    lmAuth.lmlogon.UserName.MaximumLength = P_LEN * sizeof(WCHAR);
+
+    lmAuth.lmlogon.Workstation.Buffer = lmAuth.workstationW;
+    lmAuth.lmlogon.Workstation.MaximumLength = (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR);
+    size = MAX_COMPUTERNAME_LENGTH + 1;
+    GetComputerNameW(lmAuth.workstationW, &size);
+    lmAuth.lmlogon.Workstation.Length = wcslen(lmAuth.workstationW) * sizeof(WCHAR);
+
+    memcpy(lmAuth.lmlogon.ChallengeToClient, vcp->encKey, MSV1_0_CHALLENGE_LENGTH);
+
+    lmAuth.lmlogon.CaseInsensitiveChallengeResponse.Buffer = lmAuth.ciResponse;
+    lmAuth.lmlogon.CaseInsensitiveChallengeResponse.Length = ciPwdLength;
+    lmAuth.lmlogon.CaseInsensitiveChallengeResponse.MaximumLength = P_RESP_LEN;
+    memcpy(lmAuth.ciResponse, ciPwd, ciPwdLength);
+
+    lmAuth.lmlogon.CaseSensitiveChallengeResponse.Buffer = lmAuth.csResponse;
+    lmAuth.lmlogon.CaseSensitiveChallengeResponse.Length = csPwdLength;
+    lmAuth.lmlogon.CaseSensitiveChallengeResponse.MaximumLength = P_RESP_LEN;
+    memcpy(lmAuth.csResponse, csPwd, csPwdLength);
+
+    lmAuth.lmlogon.ParameterControl = 0;
+
+    lmAuth.tgroups.GroupCount = 0;
+    lmAuth.tgroups.Groups[0].Sid = NULL;
+    lmAuth.tgroups.Groups[0].Attributes = 0;
+
+    lmAuth.tsource.SourceIdentifier.HighPart = 0;
+    lmAuth.tsource.SourceIdentifier.LowPart = (DWORD) vcp;
+    strcpy(lmAuth.tsource.SourceName,"OpenAFS"); /* 8 char limit */
+
+    nts = LsaLogonUser( smb_lsaHandle,
+                        &smb_lsaLogonOrigin,
+                        Network, /*3*/
+                        smb_lsaSecPackage,
+                        &lmAuth,
+                        sizeof(lmAuth),
+                        &lmAuth.tgroups,
+                        &lmAuth.tsource,
+                        &lmprofilep,
+                        &lmprofilepSize,
+                        &lmSession,
+                        &lmToken,
+                        &quotaLimits,
+                        &ntsEx);
+
+    OutputDebugF("Return from LsaLogonUser is 0x%lX", nts);
+    OutputDebugF("Extended status is 0x%lX", ntsEx);
+
+    if (nts == ERROR_SUCCESS) {
+        /* free the token */
+        LsaFreeReturnBuffer(lmprofilep);
+        CloseHandle(lmToken);
+        return 0;
+    } else {
+        /* No AFS for you */
+        if (nts == 0xC000015BL)
+            return CM_ERROR_BADLOGONTYPE;
+        else /* our catchall is a bad password though we could be more specific */
+            return CM_ERROR_BADPASSWORD;
+    }       
+}
+
+/* The buffer pointed to by usern is assumed to be at least SMB_MAX_USERNAME_LENGTH bytes */
+long smb_GetNormalizedUsername(char * usern, const char * accountName, const char * domainName) 
+{
+    char * atsign;
+    const char * domain;
+
+    /* check if we have sane input */
+    if ((strlen(accountName) + strlen(domainName) + 1) > SMB_MAX_USERNAME_LENGTH)
+        return 1;
+
+    /* we could get : [accountName][domainName]
+       [user][domain]
+       [user@domain][]
+       [user][]/[user][?]
+       [][]/[][?] */
+
+    atsign = strchr(accountName, '@');
+
+    if (atsign) /* [user@domain][] -> [user@domain][domain] */
+        domain = atsign + 1;
+    else
+        domain = domainName;
+
+    /* if for some reason the client doesn't know what domain to use,
+       it will either return an empty string or a '?' */
+    if (!domain[0] || domain[0] == '?')
+        /* Empty domains and empty usernames are usually sent from tokenless contexts.
+           This way such logins will get an empty username (easy to check).  I don't know 
+           when a non-empty username would be supplied with an anonymous domain, but *shrug* */
+        strcpy(usern,accountName);
+    else {
+        /* TODO: what about WIN.MIT.EDU\user vs. WIN\user? */
+        strcpy(usern,domain);
+        strcat(usern,"\\");
+        if (atsign)
+            strncat(usern,accountName,atsign - accountName);
+        else
+            strcat(usern,accountName);
+    }       
+
+    strlwr(usern);
+
+    return 0;
+}
+
+/* When using SMB auth, all SMB sessions have to pass through here first to
+ * authenticate the user. 
+ * Caveat: If not use the SMB auth the protocol does not require sending a
+ * session setup packet, which means that we can't rely on a UID in subsequent
+ * packets.  Though in practice we get one anyway.
+ */
 long smb_ReceiveV3SessionSetupX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
     char *tp;
-    char *usern, *pwd, *pwdx;
     smb_user_t *uidp;
     unsigned short newUid;
-    unsigned long caps;
+    unsigned long caps = 0;
     cm_user_t *userp;
     smb_username_t *unp;
     char *s1 = " ";
+    long code = 0; 
+    char usern[SMB_MAX_USERNAME_LENGTH];
+    char *secBlobOut = NULL;
+    int  secBlobOutLength = 0;
 
     /* Check for bad conns */
     if (vcp->flags & SMB_VCFLAG_REMOTECONN)
         return CM_ERROR_REMOTECONN;
 
-    /* For NT LM 0.12 and up, get capabilities */
     if (vcp->flags & SMB_VCFLAG_USENT) {
-        caps = smb_GetSMBParm(inp, 11);
-        if (caps & 0x40)
+        if (smb_authType == SMB_AUTH_EXTENDED) {
+            /* extended authentication */
+            char *secBlobIn;
+            int secBlobInLength;
+        
+            if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
+                caps = smb_GetSMBParm(inp,10) | (((unsigned long) smb_GetSMBParm(inp,11)) << 16);
+            }
+
+            secBlobInLength = smb_GetSMBParm(inp, 7);
+            secBlobIn = smb_GetSMBData(inp, NULL);
+
+            code = smb_AuthenticateUserExt(vcp, usern, secBlobIn, secBlobInLength, &secBlobOut, &secBlobOutLength);
+
+            if (code == CM_ERROR_GSSCONTINUE) {
+                smb_SetSMBParm(outp, 2, 0);
+                smb_SetSMBParm(outp, 3, secBlobOutLength);
+                smb_SetSMBDataLength(outp, secBlobOutLength + smb_ServerOSLength + smb_ServerLanManagerLength + smb_ServerDomainNameLength);
+                tp = smb_GetSMBData(outp, NULL);
+                if (secBlobOutLength) {
+                    memcpy(tp, secBlobOut, secBlobOutLength);
+                    free(secBlobOut);
+                    tp += secBlobOutLength;
+                }      
+                memcpy(tp,smb_ServerOS,smb_ServerOSLength);
+                tp += smb_ServerOSLength;
+                memcpy(tp,smb_ServerLanManager,smb_ServerLanManagerLength);
+                tp += smb_ServerLanManagerLength;
+                memcpy(tp,smb_ServerDomainName,smb_ServerDomainNameLength);
+                tp += smb_ServerDomainNameLength;
+            }
+
+            /* TODO: handle return code and continue auth. Also free secBlobOut if applicable. */
+        } else {
+            unsigned ciPwdLength, csPwdLength;
+            char *ciPwd, *csPwd;
+            char *accountName;
+            char *primaryDomain;
+            int  datalen;
+
+            /* TODO: parse for extended auth as well */
+            ciPwdLength = smb_GetSMBParm(inp, 7); /* case insensitive password length */
+            csPwdLength = smb_GetSMBParm(inp, 8); /* case sensitive password length */
+
+            tp = smb_GetSMBData(inp, &datalen);
+
+            OutputDebugF("Session packet data size [%d]",datalen);
+
+            ciPwd = tp;
+            tp += ciPwdLength;
+            csPwd = tp;
+            tp += csPwdLength;
+
+            accountName = smb_ParseString(tp, &tp);
+            primaryDomain = smb_ParseString(tp, NULL);
+
+            if (smb_GetNormalizedUsername(usern, accountName, primaryDomain)) {
+                /* shouldn't happen */
+                code = CM_ERROR_BADSMB;
+                goto after_read_packet;
+            }
+
+            /* capabilities are only valid for first session packet */
+            if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
+                caps = smb_GetSMBParm(inp, 11) | (((unsigned long)smb_GetSMBParm(inp, 12)) << 16);
+            }
+
+            if (smb_authType == SMB_AUTH_NTLM) {
+                code = smb_AuthenticateUserLM(vcp, accountName, primaryDomain, ciPwd, ciPwdLength, csPwd, csPwdLength);
+            }
+        }
+    }  else { /* V3 */
+        unsigned ciPwdLength;
+        char *ciPwd;
+        char *accountName;
+        char *primaryDomain;
+
+        ciPwdLength = smb_GetSMBParm(inp, 7);
+        tp = smb_GetSMBData(inp, NULL);
+        ciPwd = tp;
+        tp += ciPwdLength;
+
+        accountName = smb_ParseString(tp, &tp);
+        primaryDomain = smb_ParseString(tp, NULL);
+
+        if ( smb_GetNormalizedUsername(usern, accountName, primaryDomain)) {
+            /* shouldn't happen */
+            code = CM_ERROR_BADSMB;
+            goto after_read_packet;
+        }
+
+        /* even if we wanted extended auth, if we only negotiated V3, we have to fallback
+         * to NTLM.
+         */
+        if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
+            code = smb_AuthenticateUserLM(vcp,accountName,primaryDomain,ciPwd,ciPwdLength,"",0);
+        }
+    }
+
+  after_read_packet:
+    /* note down that we received a session setup X and set the capabilities flag */
+    if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
+        lock_ObtainMutex(&vcp->mx);
+        vcp->flags |= SMB_VCFLAG_SESSX_RCVD;
+        /* for the moment we can only deal with NTSTATUS */
+        if (caps & NTNEGOTIATE_CAPABILITY_NTSTATUS) {
             vcp->flags |= SMB_VCFLAG_STATUS32;
-        /* for now, ignore other capability bits */
+        }       
+        lock_ReleaseMutex(&vcp->mx);
     }
 
-    /* Parse the data */
-    tp = smb_GetSMBData(inp, NULL);
-    if (vcp->flags & SMB_VCFLAG_USENT)
-        pwdx = smb_ParseString(tp, &tp);
-    pwd = smb_ParseString(tp, &tp);
-    usern = smb_ParseString(tp, &tp);
+    /* code would be non-zero if there was an authentication failure.
+       Ideally we would like to invalidate the uid for this session or break
+       early to avoid accidently stealing someone else's tokens. */
+
+    if (code) {
+        return code;
+    }
+
+    OutputDebugF("Received username=[%s]", usern);
 
     /* On Windows 2000, this function appears to be called more often than
        it is expected to be called. This resulted in multiple smb_user_t
@@ -150,8 +798,8 @@ long smb_ReceiveV3SessionSetupX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
         unp = uidp->unp;
         userp = unp->userp;
         newUid = (unsigned short)uidp->userID;  /* For some reason these are different types!*/
-               osi_LogEvent("AFS smb_ReceiveV3SessionSetupX",NULL,"FindUserByName:Lana[%d],lsn[%d],userid[%d],name[%s]",vcp->lana,vcp->lsn,newUid,usern);
-               osi_Log3(smb_logp,"smb_ReceiveV3SessionSetupX FindUserByName:Lana[%d],lsn[%d],userid[%d]",vcp->lana,vcp->lsn,newUid);
+        osi_LogEvent("AFS smb_ReceiveV3SessionSetupX",NULL,"FindUserByName:Lana[%d],lsn[%d],userid[%d],name[%s]",vcp->lana,vcp->lsn,newUid,osi_LogSaveString(smb_logp, usern));
+        osi_Log3(smb_logp,"smb_ReceiveV3SessionSetupX FindUserByName:Lana[%d],lsn[%d],userid[%d]",vcp->lana,vcp->lsn,newUid);
         smb_ReleaseUID(uidp);
     }
     else {
@@ -176,8 +824,8 @@ long smb_ReceiveV3SessionSetupX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
         uidp = smb_FindUID(vcp, newUid, SMB_FLAG_CREATE);
         lock_ObtainMutex(&uidp->mx);
         uidp->unp = unp;
-               osi_LogEvent("AFS smb_ReceiveV3SessionSetupX",NULL,"MakeNewUser:VCP[%x],Lana[%d],lsn[%d],userid[%d],TicketKTCName[%s]",(int)vcp,vcp->lana,vcp->lsn,newUid,usern);
-               osi_Log4(smb_logp,"smb_ReceiveV3SessionSetupX MakeNewUser:VCP[%x],Lana[%d],lsn[%d],userid[%d]",vcp,vcp->lana,vcp->lsn,newUid);
+        osi_LogEvent("AFS smb_ReceiveV3SessionSetupX",NULL,"MakeNewUser:VCP[%x],Lana[%d],lsn[%d],userid[%d],TicketKTCName[%s]",(int)vcp,vcp->lana,vcp->lsn,newUid,osi_LogSaveString(smb_logp, usern));
+        osi_Log4(smb_logp,"smb_ReceiveV3SessionSetupX MakeNewUser:VCP[%x],Lana[%d],lsn[%d],userid[%d]",vcp,vcp->lana,vcp->lsn,newUid);
         lock_ReleaseMutex(&uidp->mx);
         smb_ReleaseUID(uidp);
     }
@@ -189,113 +837,183 @@ long smb_ReceiveV3SessionSetupX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
 
     osi_Log3(smb_logp, "SMB3 session setup name %s creating ID %d%s",
              osi_LogSaveString(smb_logp, usern), newUid, osi_LogSaveString(smb_logp, s1));
+
     smb_SetSMBParm(outp, 2, 0);
-    smb_SetSMBDataLength(outp, 0);
+
+    if (vcp->flags & SMB_VCFLAG_USENT) {
+        if (smb_authType == SMB_AUTH_EXTENDED) {
+            smb_SetSMBParm(outp, 3, secBlobOutLength);
+            smb_SetSMBDataLength(outp, secBlobOutLength + smb_ServerOSLength + smb_ServerLanManagerLength + smb_ServerDomainNameLength);
+            tp = smb_GetSMBData(outp, NULL);
+            if (secBlobOutLength) {
+                memcpy(tp, secBlobOut, secBlobOutLength);
+                free(secBlobOut);
+                tp += secBlobOutLength;
+            }  
+            memcpy(tp,smb_ServerOS,smb_ServerOSLength);
+            tp += smb_ServerOSLength;
+            memcpy(tp,smb_ServerLanManager,smb_ServerLanManagerLength);
+            tp += smb_ServerLanManagerLength;
+            memcpy(tp,smb_ServerDomainName,smb_ServerDomainNameLength);
+            tp += smb_ServerDomainNameLength;
+        } else {
+            smb_SetSMBDataLength(outp, 0);
+        }
+    } else {
+        if (smb_authType == SMB_AUTH_EXTENDED) {
+            smb_SetSMBDataLength(outp, smb_ServerOSLength + smb_ServerLanManagerLength + smb_ServerDomainNameLength);
+            tp = smb_GetSMBData(outp, NULL);
+            memcpy(tp,smb_ServerOS,smb_ServerOSLength);
+            tp += smb_ServerOSLength;
+            memcpy(tp,smb_ServerLanManager,smb_ServerLanManagerLength);
+            tp += smb_ServerLanManagerLength;
+            memcpy(tp,smb_ServerDomainName,smb_ServerDomainNameLength);
+            tp += smb_ServerDomainNameLength;
+        } else {
+            smb_SetSMBDataLength(outp, 0);
+        }
+    }
+
     return 0;
 }
 
 long smb_ReceiveV3UserLogoffX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       smb_user_t *uidp;
+    smb_user_t *uidp;
 
-       /* don't get tokens from this VC */
-       vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
+    /* don't get tokens from this VC */
+    vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
 
-       inp->flags |= SMB_PACKETFLAG_PROFILE_UPDATE_OK;
+    inp->flags |= SMB_PACKETFLAG_PROFILE_UPDATE_OK;
 
-       /* find the tree and free it */
+    /* find the tree and free it */
     uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
     /* TODO: smb_ReleaseUID() ? */
     if (uidp) {
-               char *s1 = NULL, *s2 = NULL;
-
-               if (s2 == NULL) s2 = " ";
-               if (s1 == NULL) {s1 = s2; s2 = " ";}
-
-               osi_Log4(smb_logp, "SMB3 user logoffX uid %d name %s%s%s",
-                  uidp->userID,
-                  osi_LogSaveString(smb_logp,
-                                    (uidp->unp) ? uidp->unp->name: " "), s1, s2);
-
-               lock_ObtainMutex(&uidp->mx);
-               uidp->flags |= SMB_USERFLAG_DELETE;
-               /*
-                * it doesn't get deleted right away
-                * because the vcp points to it
-                */
+        char *s1 = NULL, *s2 = NULL;
+
+        if (s2 == NULL) s2 = " ";
+        if (s1 == NULL) {s1 = s2; s2 = " ";}
+
+        osi_Log4(smb_logp, "SMB3 user logoffX uid %d name %s%s%s", uidp->userID,
+                  osi_LogSaveString(smb_logp, (uidp->unp) ? uidp->unp->name: " "), s1, s2);
+
+        lock_ObtainMutex(&uidp->mx);
+        uidp->flags |= SMB_USERFLAG_DELETE;
+        /*
+         * it doesn't get deleted right away
+         * because the vcp points to it
+         */
         lock_ReleaseMutex(&uidp->mx);
     }
-       else    
-               osi_Log0(smb_logp, "SMB3 user logoffX");
+    else    
+        osi_Log0(smb_logp, "SMB3 user logoffX");
 
     smb_SetSMBDataLength(outp, 0);
     return 0;
 }
 
 #define SMB_SUPPORT_SEARCH_BITS        0x0001
+#define SMB_SHARE_IS_IN_DFS            0x0002
 
 long smb_ReceiveV3TreeConnectX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
     smb_tid_t *tidp;
+    smb_user_t *uidp;
     unsigned short newTid;
     char shareName[256];
-       char *sharePath;
-       int shareFound;
+    char *sharePath;
+    int shareFound;
     char *tp;
     char *pathp;
     char *passwordp;
-       char *servicep;
+    char *servicep;
     cm_user_t *userp;
+    int ipc = 0;
         
-       osi_Log0(smb_logp, "SMB3 receive tree connect");
+    osi_Log0(smb_logp, "SMB3 receive tree connect");
 
-       /* parse input parameters */
-       tp = smb_GetSMBData(inp, NULL);
+    /* parse input parameters */
+    tp = smb_GetSMBData(inp, NULL);
     passwordp = smb_ParseString(tp, &tp);
-       pathp = smb_ParseString(tp, &tp);
-       servicep = smb_ParseString(tp, &tp);
+    pathp = smb_ParseString(tp, &tp);
+    if (smb_StoreAnsiFilenames)
+        OemToChar(pathp,pathp);
+    servicep = smb_ParseString(tp, &tp);
 
-       tp = strrchr(pathp, '\\');
+    tp = strrchr(pathp, '\\');
     if (!tp) {
         return CM_ERROR_BADSMB;
     }
     strcpy(shareName, tp+1);
 
-       if (strcmp(servicep, "IPC") == 0 || strcmp(shareName, "IPC$") == 0)
-               return CM_ERROR_NOIPC;
+    osi_Log2(smb_logp, "Tree connect pathp[%s] shareName[%s]",
+             osi_LogSaveString(smb_logp, pathp),
+             osi_LogSaveString(smb_logp, shareName));
+
+    if (strcmp(servicep, "IPC") == 0 || strcmp(shareName, "IPC$") == 0) {
+#ifndef NO_IPC
+        osi_Log0(smb_logp, "TreeConnectX connecting to IPC$");
+        ipc = 1;
+#else
+        return CM_ERROR_NOIPC;
+#endif
+    }
 
     userp = smb_GetUser(vcp, inp);
 
-       lock_ObtainMutex(&vcp->mx);
+    lock_ObtainMutex(&vcp->mx);
     newTid = vcp->tidCounter++;
-       lock_ReleaseMutex(&vcp->mx);
+    lock_ReleaseMutex(&vcp->mx);
         
-       tidp = smb_FindTID(vcp, newTid, SMB_FLAG_CREATE);
-       shareFound = smb_FindShare(vcp, inp, shareName, &sharePath);
+    tidp = smb_FindTID(vcp, newTid, SMB_FLAG_CREATE);
+
+    if (!ipc) {
+        uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
+       shareFound = smb_FindShare(vcp, uidp, shareName, &sharePath);
+        if (uidp)
+            smb_ReleaseUID(uidp);
        if (!shareFound) {
-               smb_ReleaseTID(tidp);
-               return CM_ERROR_BADSHARENAME;
+            smb_ReleaseTID(tidp);
+            return CM_ERROR_BADSHARENAME;
        }
+
+       if (vcp->flags & SMB_VCFLAG_USENT)
+        {
+            int policy = smb_FindShareCSCPolicy(shareName);
+            smb_SetSMBParm(outp, 2, SMB_SUPPORT_SEARCH_BITS | 
+#ifdef DFS_SUPPORT
+                            SMB_SHARE_IS_IN_DFS |
+#endif
+                            (policy << 2));
+        }
+    } else {
+        smb_SetSMBParm(outp, 2, 0);
+        sharePath = NULL;
+    }
+
     lock_ObtainMutex(&tidp->mx);
     tidp->userp = userp;
-       tidp->pathname = sharePath;
+    tidp->pathname = sharePath;
+    if (ipc) 
+        tidp->flags |= SMB_TIDFLAG_IPC;
     lock_ReleaseMutex(&tidp->mx);
     smb_ReleaseTID(tidp);
 
-       if (vcp->flags & SMB_VCFLAG_USENT)
-    {
-        int policy = smb_FindShareCSCPolicy(shareName);
-        smb_SetSMBParm(outp, 2, SMB_SUPPORT_SEARCH_BITS | (policy << 2));
+    ((smb_t *)outp)->tid = newTid;
+    ((smb_t *)inp)->tid = newTid;
+    tp = smb_GetSMBData(outp, NULL);
+    if (!ipc) {
+        /* XXX - why is this a drive letter? - jaltman */
+        *tp++ = 'A';
+        *tp++ = ':';
+        *tp++ = 0;
+        smb_SetSMBDataLength(outp, 3);
+    } else {
+        strcpy(tp, "IPC");
+        smb_SetSMBDataLength(outp, 4);
     }
 
-       ((smb_t *)outp)->tid = newTid;
-       ((smb_t *)inp)->tid = newTid;
-       tp = smb_GetSMBData(outp, NULL);
-    *tp++ = 'A';
-    *tp++ = ':';
-    *tp++ = 0;
-    smb_SetSMBDataLength(outp, 3);
-
     osi_Log1(smb_logp, "SMB3 tree connect created ID %d", newTid);
     return 0;
 }
@@ -303,12 +1021,12 @@ long smb_ReceiveV3TreeConnectX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *o
 /* must be called with global tran lock held */
 smb_tran2Packet_t *smb_FindTran2Packet(smb_vc_t *vcp, smb_packet_t *inp)
 {
-       smb_tran2Packet_t *tp;
+    smb_tran2Packet_t *tp;
     smb_t *smbp;
         
     smbp = (smb_t *) inp->data;
-       for(tp = smb_tran2AssemblyQueuep; tp; tp = (smb_tran2Packet_t *) osi_QNext(&tp->q)) {
-               if (tp->vcp == vcp && tp->mid == smbp->mid && tp->tid == smbp->tid)
+    for (tp = smb_tran2AssemblyQueuep; tp; tp = (smb_tran2Packet_t *) osi_QNext(&tp->q)) {
+        if (tp->vcp == vcp && tp->mid == smbp->mid && tp->tid == smbp->tid)
             return tp;
     }
     return NULL;
@@ -317,11 +1035,11 @@ smb_tran2Packet_t *smb_FindTran2Packet(smb_vc_t *vcp, smb_packet_t *inp)
 smb_tran2Packet_t *smb_NewTran2Packet(smb_vc_t *vcp, smb_packet_t *inp,
        int totalParms, int totalData)
 {
-       smb_tran2Packet_t *tp;
+    smb_tran2Packet_t *tp;
     smb_t *smbp;
         
     smbp = (smb_t *) inp->data;
-       tp = malloc(sizeof(*tp));
+    tp = malloc(sizeof(*tp));
     memset(tp, 0, sizeof(*tp));
     tp->vcp = vcp;
     smb_HoldVC(vcp);
@@ -332,68 +1050,74 @@ smb_tran2Packet_t *smb_NewTran2Packet(smb_vc_t *vcp, smb_packet_t *inp,
     tp->mid = smbp->mid;
     tp->uid = smbp->uid;
     tp->pid = smbp->pid;
-       tp->res[0] = smbp->res[0];
-       osi_QAdd((osi_queue_t **)&smb_tran2AssemblyQueuep, &tp->q);
-    tp->opcode = smb_GetSMBParm(inp, 14);
-       if (totalParms != 0)
+    tp->res[0] = smbp->res[0];
+    osi_QAdd((osi_queue_t **)&smb_tran2AssemblyQueuep, &tp->q);
+    if (totalParms != 0)
         tp->parmsp = malloc(totalParms);
-       if (totalData != 0)
+    if (totalData != 0)
         tp->datap = malloc(totalData);
-       tp->flags |= SMB_TRAN2PFLAG_ALLOC;
+    if (smbp->com == 0x25 || smbp->com == 0x26)
+        tp->com = 0x25;
+    else {
+        tp->opcode = smb_GetSMBParm(inp, 14);
+        tp->com = 0x32;
+    }
+    tp->flags |= SMB_TRAN2PFLAG_ALLOC;
     return tp;
 }
 
 smb_tran2Packet_t *smb_GetTran2ResponsePacket(smb_vc_t *vcp,
-       smb_tran2Packet_t *inp, smb_packet_t *outp,
-       int totalParms, int totalData)
+                                               smb_tran2Packet_t *inp, smb_packet_t *outp,
+                                               int totalParms, int totalData)  
 {
-       smb_tran2Packet_t *tp;
-       unsigned short parmOffset;
-       unsigned short dataOffset;
-       unsigned short dataAlign;
+    smb_tran2Packet_t *tp;
+    unsigned short parmOffset;
+    unsigned short dataOffset;
+    unsigned short dataAlign;
         
-       tp = malloc(sizeof(*tp));
+    tp = malloc(sizeof(*tp));
     memset(tp, 0, sizeof(*tp));
     tp->vcp = NULL;
     tp->curData = tp->curParms = 0;
     tp->totalData = totalData;
     tp->totalParms = totalParms;
-       tp->oldTotalParms = totalParms;
+    tp->oldTotalParms = totalParms;
     tp->tid = inp->tid;
     tp->mid = inp->mid;
     tp->uid = inp->uid;
     tp->pid = inp->pid;
-       tp->res[0] = inp->res[0];
+    tp->res[0] = inp->res[0];
     tp->opcode = inp->opcode;
+    tp->com = inp->com;
 
-       /*
-        * We calculate where the parameters and data will start.
-        * This calculation must parallel the calculation in
-        * smb_SendTran2Packet.
-        */
+    /*
+     * We calculate where the parameters and data will start.
+     * This calculation must parallel the calculation in
+     * smb_SendTran2Packet.
+     */
 
-       parmOffset = 10*2 + 35;
-       parmOffset++;                   /* round to even */
-       tp->parmsp = (unsigned short *) (outp->data + parmOffset);
+    parmOffset = 10*2 + 35;
+    parmOffset++;                      /* round to even */
+    tp->parmsp = (unsigned short *) (outp->data + parmOffset);
 
-       dataOffset = parmOffset + totalParms;
-       dataAlign = dataOffset & 2;     /* quad-align */
-       dataOffset += dataAlign;
-       tp->datap = outp->data + dataOffset;
+    dataOffset = parmOffset + totalParms;
+    dataAlign = dataOffset & 2;        /* quad-align */
+    dataOffset += dataAlign;
+    tp->datap = outp->data + dataOffset;
 
     return tp;
-}
+}       
 
 /* free a tran2 packet; must be called with smb_globalLock held */
 void smb_FreeTran2Packet(smb_tran2Packet_t *t2p)
 {
     if (t2p->vcp) smb_ReleaseVC(t2p->vcp);
-       if (t2p->flags & SMB_TRAN2PFLAG_ALLOC) {
-               if (t2p->parmsp)
-                       free(t2p->parmsp);
-               if (t2p->datap)
-                       free(t2p->datap);
-       }
+    if (t2p->flags & SMB_TRAN2PFLAG_ALLOC) {
+        if (t2p->parmsp)
+            free(t2p->parmsp);
+        if (t2p->datap)
+            free(t2p->datap);
+    }       
     free(t2p);
 }
 
@@ -406,39 +1130,39 @@ void smb_SendTran2Error(smb_vc_t *vcp, smb_tran2Packet_t *t2p,
     smb_t *smbp;
     unsigned short errCode;
     unsigned char errClass;
-       unsigned long NTStatus;
+    unsigned long NTStatus;
 
     if (vcp->flags & SMB_VCFLAG_STATUS32)
-               smb_MapNTError(code, &NTStatus);
-       else
-               smb_MapCoreError(code, vcp, &errCode, &errClass);
+        smb_MapNTError(code, &NTStatus);
+    else
+        smb_MapCoreError(code, vcp, &errCode, &errClass);
 
     smb_FormatResponsePacket(vcp, NULL, tp);
     smbp = (smb_t *) tp;
 
-       /* We can handle long names */
-       if (vcp->flags & SMB_VCFLAG_USENT)
-               smbp->flg2 |= 0x40;     /* IS_LONG_NAME */
+    /* We can handle long names */
+    if (vcp->flags & SMB_VCFLAG_USENT)
+        smbp->flg2 |= SMB_FLAGS2_IS_LONG_NAME;
         
     /* now copy important fields from the tran 2 packet */
-    smbp->com = 0x32;          /* tran 2 response */
+    smbp->com = t2p->com;
     smbp->tid = t2p->tid;
     smbp->mid = t2p->mid;
     smbp->pid = t2p->pid;
     smbp->uid = t2p->uid;
-       smbp->res[0] = t2p->res[0];
-       if (vcp->flags & SMB_VCFLAG_STATUS32) {
-               smbp->rcls = (unsigned char) (NTStatus & 0xff);
-               smbp->reh = (unsigned char) ((NTStatus >> 8) & 0xff);
-               smbp->errLow = (unsigned char) ((NTStatus >> 16) & 0xff);
-               smbp->errHigh = (unsigned char) ((NTStatus >> 24) & 0xff);
-               smbp->flg2 |= 0x4000;
-       }
-       else {
+    smbp->res[0] = t2p->res[0];
+    if (vcp->flags & SMB_VCFLAG_STATUS32) {
+        smbp->rcls = (unsigned char) (NTStatus & 0xff);
+        smbp->reh = (unsigned char) ((NTStatus >> 8) & 0xff);
+        smbp->errLow = (unsigned char) ((NTStatus >> 16) & 0xff);
+        smbp->errHigh = (unsigned char) ((NTStatus >> 24) & 0xff);
+        smbp->flg2 |= SMB_FLAGS2_ERR_STATUS;
+    }
+    else {
         smbp->rcls = errClass;
-               smbp->errLow = (unsigned char) (errCode & 0xff);
-               smbp->errHigh = (unsigned char) ((errCode >> 8) & 0xff);
-       }
+        smbp->errLow = (unsigned char) (errCode & 0xff);
+        smbp->errHigh = (unsigned char) ((errCode >> 8) & 0xff);
+    }
         
     /* send packet */
     smb_SendPacket(vcp, tp);
@@ -448,25 +1172,25 @@ void smb_SendTran2Packet(smb_vc_t *vcp, smb_tran2Packet_t *t2p, smb_packet_t *tp
 {
     smb_t *smbp;
     unsigned short parmOffset;
-       unsigned short dataOffset;
-       unsigned short totalLength;
-       unsigned short dataAlign;
+    unsigned short dataOffset;
+    unsigned short totalLength;
+    unsigned short dataAlign;
     char *datap;
-        
+
     smb_FormatResponsePacket(vcp, NULL, tp);
     smbp = (smb_t *) tp;
 
-       /* We can handle long names */
-       if (vcp->flags & SMB_VCFLAG_USENT)
-               smbp->flg2 |= 0x40;     /* IS_LONG_NAME */
+    /* We can handle long names */
+    if (vcp->flags & SMB_VCFLAG_USENT)
+        smbp->flg2 |= SMB_FLAGS2_IS_LONG_NAME;
 
     /* now copy important fields from the tran 2 packet */
-    smbp->com = 0x32;          /* tran 2 response */
+    smbp->com = t2p->com;
     smbp->tid = t2p->tid;
     smbp->mid = t2p->mid;
     smbp->pid = t2p->pid;
     smbp->uid = t2p->uid;
-       smbp->res[0] = t2p->res[0];
+    smbp->res[0] = t2p->res[0];
 
     totalLength = 1 + t2p->totalData + t2p->totalParms;
 
@@ -475,31 +1199,31 @@ void smb_SendTran2Packet(smb_vc_t *vcp, smb_tran2Packet_t *t2p, smb_packet_t *tp
     smb_SetSMBParm(tp, 1, t2p->totalData);     /* data bytes */
     smb_SetSMBParm(tp, 2, 0);          /* reserved */
     smb_SetSMBParm(tp, 3, t2p->totalParms);    /* parm bytes in this packet */
-       parmOffset = 10*2 + 35;                 /* parm offset in packet */
-       parmOffset++;                           /* round to even */
+    parmOffset = 10*2 + 35;                    /* parm offset in packet */
+    parmOffset++;                              /* round to even */
     smb_SetSMBParm(tp, 4, parmOffset); /* 11 parm words plus *
     * hdr, bcc and wct */
     smb_SetSMBParm(tp, 5, 0);          /* parm displacement */
     smb_SetSMBParm(tp, 6, t2p->totalData);     /* data in this packet */
-       dataOffset = parmOffset + t2p->oldTotalParms;
-       dataAlign = dataOffset & 2;             /* quad-align */
-       dataOffset += dataAlign;
+    dataOffset = parmOffset + t2p->oldTotalParms;
+    dataAlign = dataOffset & 2;                /* quad-align */
+    dataOffset += dataAlign;
     smb_SetSMBParm(tp, 7, dataOffset); /* offset of data */
     smb_SetSMBParm(tp, 8, 0);          /* data displacement */
     smb_SetSMBParm(tp, 9, 0);          /* low: setup word count *
-    * high: resvd */
+                                         * high: resvd */
 
     datap = smb_GetSMBData(tp, NULL);
-       *datap++ = 0;                           /* we rounded to even */
+    *datap++ = 0;                              /* we rounded to even */
 
-       totalLength += dataAlign;
+    totalLength += dataAlign;
     smb_SetSMBDataLength(tp, totalLength);
         
     /* next, send the datagram */
     smb_SendPacket(vcp, tp);
 }   
 
-long smb_ReceiveV3Tran2A(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
+long smb_ReceiveV3Trans(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
     smb_tran2Packet_t *asp;
     int totalParms;
@@ -511,55 +1235,56 @@ long smb_ReceiveV3Tran2A(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     int parmCount;
     int dataCount;
     int firstPacket;
+    int rapOp;
     long code = 0;
 
-       /* We sometimes see 0 word count.  What to do? */
-       if (*inp->wctp == 0) {
+    /* We sometimes see 0 word count.  What to do? */
+    if (*inp->wctp == 0) {
 #ifndef DJGPP
-               HANDLE h;
-               char *ptbuf[1];
+        HANDLE h;
+        char *ptbuf[1];
 
-               osi_Log0(smb_logp, "TRANSACTION2 word count = 0"); 
+        osi_Log0(smb_logp, "TRANSACTION word count = 0"); 
 
-               h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
-               ptbuf[0] = "Transaction2 word count = 0";
-               ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1003, NULL,
-                           1, inp->ncb_length, ptbuf, inp);
-               DeregisterEventSource(h);
+        h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
+        ptbuf[0] = "Transaction2 word count = 0";
+        ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1003, NULL,
+                    1, inp->ncb_length, ptbuf, inp);
+        DeregisterEventSource(h);
 #else /* DJGPP */
-               osi_Log0(smb_logp, "TRANSACTION2 word count = 0"); 
+        osi_Log0(smb_logp, "TRANSACTION word count = 0"); 
 #endif /* !DJGPP */
 
         smb_SetSMBDataLength(outp, 0);
         smb_SendPacket(vcp, outp);
-               return 0;
-       }
+        return 0;
+    }
 
     totalParms = smb_GetSMBParm(inp, 0);
     totalData = smb_GetSMBParm(inp, 1);
         
-    firstPacket = (inp->inCom == 0x32);
+    firstPacket = (inp->inCom == 0x25);
         
-       /* find the packet we're reassembling */
-       lock_ObtainWrite(&smb_globalLock);
+    /* find the packet we're reassembling */
+    lock_ObtainWrite(&smb_globalLock);
     asp = smb_FindTran2Packet(vcp, inp);
     if (!asp) {
         asp = smb_NewTran2Packet(vcp, inp, totalParms, totalData);
-       }
+    }
     lock_ReleaseWrite(&smb_globalLock);
         
     /* now merge in this latest packet; start by looking up offsets */
-       if (firstPacket) {
-               parmDisp = dataDisp = 0;
+    if (firstPacket) {
+        parmDisp = dataDisp = 0;
         parmOffset = smb_GetSMBParm(inp, 10);
         dataOffset = smb_GetSMBParm(inp, 12);
         parmCount = smb_GetSMBParm(inp, 9);
         dataCount = smb_GetSMBParm(inp, 11);
-               asp->maxReturnParms = smb_GetSMBParm(inp, 2);
+        asp->maxReturnParms = smb_GetSMBParm(inp, 2);
         asp->maxReturnData = smb_GetSMBParm(inp, 3);
 
-               osi_Log3(smb_logp, "SMB3 received T2 init packet total data %d, cur data %d, max return data %d",
-                 totalData, dataCount, asp->maxReturnData);
+        osi_Log3(smb_logp, "SMB3 received Trans init packet total data %d, cur data %d, max return data %d",
+                  totalData, dataCount, asp->maxReturnData);
     }
     else {
         parmDisp = smb_GetSMBParm(inp, 4);
@@ -569,16 +1294,16 @@ long smb_ReceiveV3Tran2A(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         parmCount = smb_GetSMBParm(inp, 2);
         dataCount = smb_GetSMBParm(inp, 5);
 
-        osi_Log2(smb_logp, "SMB3 received T2 aux packet parms %d, data %d",
+        osi_Log2(smb_logp, "SMB3 received Trans aux packet parms %d, data %d",
                  parmCount, dataCount);
     }   
 
     /* now copy the parms and data */
-    if ( parmCount != 0 )
+    if ( asp->totalParms > 0 && parmCount != 0 )
     {
         memcpy(((char *)asp->parmsp) + parmDisp, inp->data + parmOffset, parmCount);
     }
-    if ( dataCount != 0 ) {
+    if ( asp->totalData > 0 && dataCount != 0 ) {
         memcpy(asp->datap + dataDisp, inp->data + dataOffset, dataCount);
     }
 
@@ -587,25 +1312,30 @@ long smb_ReceiveV3Tran2A(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     asp->curParms += parmCount;
 
     /* finally, if we're done, remove the packet from the queue and dispatch it */
-    if (asp->totalData <= asp->curData && asp->totalParms <= asp->curParms) {
-               /* we've received it all */
+    if (asp->totalParms > 0 &&
+        asp->curParms > 0 &&
+        asp->totalData <= asp->curData &&
+        asp->totalParms <= asp->curParms) {
+        /* we've received it all */
         lock_ObtainWrite(&smb_globalLock);
-               osi_QRemove((osi_queue_t **) &smb_tran2AssemblyQueuep, &asp->q);
+        osi_QRemove((osi_queue_t **) &smb_tran2AssemblyQueuep, &asp->q);
         lock_ReleaseWrite(&smb_globalLock);
 
         /* now dispatch it */
-        if ( asp->opcode >= 0 && asp->opcode < 20 && smb_tran2DispatchTable[asp->opcode].procp) {
-            osi_LogEvent("AFS-Dispatch-2[%s]",myCrt_2Dispatch(asp->opcode),"vcp[%x] lana[%d] lsn[%d]",(int)vcp,vcp->lana,vcp->lsn);
-            osi_Log4(smb_logp,"AFS Server - Dispatch-2 %s vcp[%x] lana[%d] lsn[%d]",myCrt_2Dispatch(asp->opcode),vcp,vcp->lana,vcp->lsn);
-            code = (*smb_tran2DispatchTable[asp->opcode].procp)(vcp, asp, outp);
+        rapOp = asp->parmsp[0];
+
+        if ( rapOp >= 0 && rapOp < SMB_RAP_NOPCODES && smb_rapDispatchTable[rapOp].procp) {
+            osi_LogEvent("AFS-Dispatch-RAP[%s]",myCrt_RapDispatch(rapOp),"vcp[%x] lana[%d] lsn[%d]",(int)vcp,vcp->lana,vcp->lsn);
+            osi_Log4(smb_logp,"AFS Server - Dispatch-RAP %s vcp[%x] lana[%d] lsn[%d]",myCrt_RapDispatch(rapOp),vcp,vcp->lana,vcp->lsn);
+            code = (*smb_rapDispatchTable[rapOp].procp)(vcp, asp, outp);
         }
         else {
-            osi_LogEvent("AFS-Dispatch-2 [invalid]", NULL, "op[%x] vcp[%x] lana[%d] lsn[%d]", asp->opcode, vcp, vcp->lana, vcp->lsn);
-            osi_Log4(smb_logp,"AFS Server - Dispatch-2 [INVALID] op[%x] vcp[%x] lana[%d] lsn[%d]", asp->opcode, vcp, vcp->lana, vcp->lsn);
+            osi_LogEvent("AFS-Dispatch-RAP [invalid]", NULL, "op[%x] vcp[%x] lana[%d] lsn[%d]", rapOp, vcp, vcp->lana, vcp->lsn);
+            osi_Log4(smb_logp,"AFS Server - Dispatch-RAP [INVALID] op[%x] vcp[%x] lana[%d] lsn[%d]", rapOp, vcp, vcp->lana, vcp->lsn);
             code = CM_ERROR_BADOP;
         }
 
-               /* if an error is returned, we're supposed to send an error packet,
+        /* if an error is returned, we're supposed to send an error packet,
          * otherwise the dispatched function already did the data sending.
          * We give dispatched proc the responsibility since it knows how much
          * space to allocate.
@@ -614,86 +1344,726 @@ long smb_ReceiveV3Tran2A(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
             smb_SendTran2Error(vcp, asp, outp, code);
         }
 
-               /* free the input tran 2 packet */
-               lock_ObtainWrite(&smb_globalLock);
+        /* free the input tran 2 packet */
+        lock_ObtainWrite(&smb_globalLock);
         smb_FreeTran2Packet(asp);
-               lock_ReleaseWrite(&smb_globalLock);
+        lock_ReleaseWrite(&smb_globalLock);
     }
     else if (firstPacket) {
-               /* the first packet in a multi-packet request, we need to send an
+        /* the first packet in a multi-packet request, we need to send an
          * ack to get more data.
          */
         smb_SetSMBDataLength(outp, 0);
         smb_SendPacket(vcp, outp);
     }
 
-       return 0;
+    return 0;
 }
 
-long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
-{
-       char *pathp;
-    smb_tran2Packet_t *outp;
-    long code = 0;
-       cm_space_t *spacep;
-    int excl;
-    cm_user_t *userp;
-    cm_scache_t *dscp;         /* dir we're dealing with */
-    cm_scache_t *scp;          /* file we're creating */
-    cm_attr_t setAttr;
-    int initialModeBits;
-    smb_fid_t *fidp;
-    int attributes;
-    char *lastNamep;
-    long dosTime;
-    int openFun;
-    int trunc;
-    int openMode;
-    int extraInfo;
-    int openAction;
-    int parmSlot;                      /* which parm we're dealing with */
-    long returnEALength;
-       char *tidPathp;
-       cm_req_t req;
+/* ANSI versions.  The unicode versions support arbitrary length
+   share names, but we don't support unicode yet. */
 
-       cm_InitReq(&req);
+typedef struct smb_rap_share_info_0 {
+    char        shi0_netname[13];
+} smb_rap_share_info_0_t;
 
-    scp = NULL;
-        
-       extraInfo = (p->parmsp[0] & 1); /* return extra info */
-    returnEALength = (p->parmsp[0] & 8);       /* return extended attr length */
+typedef struct smb_rap_share_info_1 {
+    char                       shi1_netname[13];
+    char                       shi1_pad;
+    WORD                       shi1_type;
+    DWORD                      shi1_remark; /* char *shi1_remark; data offset */
+} smb_rap_share_info_1_t;
 
-       openFun = p->parmsp[6];         /* open function */
-    excl = ((openFun & 3) == 0);
-    trunc = ((openFun & 3) == 2);      /* truncate it */
-       openMode = (p->parmsp[1] & 0x7);
-    openAction = 0;                    /* tracks what we did */
+typedef struct smb_rap_share_info_2 {
+    char                               shi2_netname[13];
+    char                               shi2_pad;
+    unsigned short             shi2_type;
+    DWORD                              shi2_remark; /* char *shi2_remark; data offset */
+    unsigned short             shi2_permissions;
+    unsigned short             shi2_max_uses;
+    unsigned short             shi2_current_uses;
+    DWORD                              shi2_path;  /* char *shi2_path; data offset */
+    unsigned short             shi2_passwd[9];
+    unsigned short             shi2_pad2;
+} smb_rap_share_info_2_t;
 
-    attributes = p->parmsp[3];
-    dosTime = p->parmsp[4] | (p->parmsp[5] << 16);
-        
-       /* compute initial mode bits based on read-only flag in attributes */
-    initialModeBits = 0666;
-    if (attributes & 1) initialModeBits &= ~0222;
-        
-    pathp = (char *) (&p->parmsp[14]);
+#define SMB_RAP_MAX_SHARES 512
+
+typedef struct smb_rap_share_list {
+    int cShare;
+    int maxShares;
+    smb_rap_share_info_0_t * shares;
+} smb_rap_share_list_t;
+
+int smb_rapCollectSharesProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp) {
+    smb_rap_share_list_t * sp;
+    char * name;
+
+    name = dep->name;
+
+    if (name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2])))
+        return 0; /* skip over '.' and '..' */
+
+    sp = (smb_rap_share_list_t *) vrockp;
+
+    strncpy(sp->shares[sp->cShare].shi0_netname, name, 12);
+    sp->shares[sp->cShare].shi0_netname[12] = 0;
+
+    sp->cShare++;
+
+    if (sp->cShare >= sp->maxShares)
+        return CM_ERROR_STOPNOW;
+    else
+        return 0;
+}       
+
+long smb_ReceiveRAPNetShareEnum(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
+{
+    smb_tran2Packet_t *outp;
+    unsigned short * tp;
+    int len;
+    int infoLevel;
+    int bufsize;
+    int outParmsTotal; /* total parameter bytes */
+    int outDataTotal;  /* total data bytes */
+    int code = 0;
+    DWORD rv;
+    DWORD allSubmount;
+    USHORT nShares;
+    DWORD nRegShares;
+    DWORD nSharesRet;
+    HKEY hkParam;
+    HKEY hkSubmount = NULL;
+    smb_rap_share_info_1_t * shares;
+    USHORT cshare = 0;
+    char * cstrp;
+    char thisShare[256];
+    int i,j;
+    int nonrootShares;
+    smb_rap_share_list_t rootShares;
+    cm_req_t req;
+    cm_user_t * userp;
+    osi_hyper_t thyper;
+
+    tp = p->parmsp + 1; /* skip over function number (always 0) */
+    (void) smb_ParseString((char *) tp, (char **) &tp); /* skip over parm descriptor */
+    (void) smb_ParseString((char *) tp, (char **) &tp); /* skip over data descriptor */
+    infoLevel = tp[0];
+    bufsize = tp[1];
+
+    if (infoLevel != 1) {
+        return CM_ERROR_INVAL;
+    }
+
+    /* first figure out how many shares there are */
+    rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0,
+                      KEY_QUERY_VALUE, &hkParam);
+    if (rv == ERROR_SUCCESS) {
+        len = sizeof(allSubmount);
+        rv = RegQueryValueEx(hkParam, "AllSubmount", NULL, NULL,
+                             (BYTE *) &allSubmount, &len);
+        if (rv != ERROR_SUCCESS || allSubmount != 0) {
+            allSubmount = 1;
+        }
+        RegCloseKey (hkParam);
+    }
+
+    rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\OpenAFS\\Client\\Submounts",
+                      0, KEY_QUERY_VALUE, &hkSubmount);
+    if (rv == ERROR_SUCCESS) {
+        rv = RegQueryInfoKey(hkSubmount, NULL, NULL, NULL, NULL,
+                             NULL, NULL, &nRegShares, NULL, NULL, NULL, NULL);
+        if (rv != ERROR_SUCCESS)
+            nRegShares = 0;
+    } else {
+        hkSubmount = NULL;
+    }
+
+    /* fetch the root shares */
+    rootShares.maxShares = SMB_RAP_MAX_SHARES;
+    rootShares.cShare = 0;
+    rootShares.shares = malloc( sizeof(smb_rap_share_info_0_t) * SMB_RAP_MAX_SHARES );
+
+    cm_InitReq(&req);
+
+    userp = smb_GetTran2User(vcp,p);
+
+    thyper.HighPart = 0;
+    thyper.LowPart = 0;
+
+    cm_HoldSCache(cm_rootSCachep);
+    cm_ApplyDir(cm_rootSCachep, smb_rapCollectSharesProc, &rootShares, &thyper, userp, &req, NULL);
+    cm_ReleaseSCache(cm_rootSCachep);
+
+    cm_ReleaseUser(userp);
+
+    nShares = rootShares.cShare + nRegShares + allSubmount;
+
+#define REMARK_LEN 1
+    outParmsTotal = 8; /* 4 dwords */
+    outDataTotal = (sizeof(smb_rap_share_info_1_t) + REMARK_LEN) * nShares ;
+    if(outDataTotal > bufsize) {
+        nSharesRet = bufsize / (sizeof(smb_rap_share_info_1_t) + REMARK_LEN);
+        outDataTotal = (sizeof(smb_rap_share_info_1_t) + REMARK_LEN) * nSharesRet;
+    }
+    else {
+        nSharesRet = nShares;
+    }
+    
+    outp = smb_GetTran2ResponsePacket(vcp, p, op, outParmsTotal, outDataTotal);
+
+    /* now for the submounts */
+    shares = (smb_rap_share_info_1_t *) outp->datap;
+    cstrp = outp->datap + sizeof(smb_rap_share_info_1_t) * nSharesRet;
+
+    memset(outp->datap, 0, (sizeof(smb_rap_share_info_1_t) + REMARK_LEN) * nSharesRet);
+
+    if (allSubmount) {
+        strcpy( shares[cshare].shi1_netname, "all" );
+        shares[cshare].shi1_remark = cstrp - outp->datap;
+        /* type and pad are zero already */
+        cshare++;
+        cstrp+=REMARK_LEN;
+    }
+
+    if (hkSubmount) {
+        for (i=0; i < nRegShares && cshare < nSharesRet; i++) {
+            len = sizeof(thisShare);
+            rv = RegEnumValue(hkSubmount, i, thisShare, &len, NULL, NULL, NULL, NULL);
+            if (rv == ERROR_SUCCESS && strlen(thisShare) && (!allSubmount || stricmp(thisShare,"all"))) {
+                strncpy(shares[cshare].shi1_netname, thisShare, sizeof(shares->shi1_netname)-1);
+                shares[cshare].shi1_netname[sizeof(shares->shi1_netname)-1] = 0; /* unfortunate truncation */
+                shares[cshare].shi1_remark = cstrp - outp->datap;
+                cshare++;
+                cstrp+=REMARK_LEN;
+            }
+            else
+                nShares--; /* uncount key */
+        }
+
+        RegCloseKey(hkSubmount);
+    }
+
+    nonrootShares = cshare;
+
+    for (i=0; i < rootShares.cShare && cshare < nSharesRet; i++) {
+        /* in case there are collisions with submounts, submounts have higher priority */              
+        for (j=0; j < nonrootShares; j++)
+            if (!stricmp(shares[j].shi1_netname, rootShares.shares[i].shi0_netname))
+                break;
+               
+        if (j < nonrootShares) {
+            nShares--; /* uncount */
+            continue;
+        }
+
+        strcpy(shares[cshare].shi1_netname, rootShares.shares[i].shi0_netname);
+        shares[cshare].shi1_remark = cstrp - outp->datap;
+        cshare++;
+        cstrp+=REMARK_LEN;
+    }
+
+    outp->parmsp[0] = ((cshare == nShares)? ERROR_SUCCESS : ERROR_MORE_DATA);
+    outp->parmsp[1] = 0;
+    outp->parmsp[2] = cshare;
+    outp->parmsp[3] = nShares;
+
+    outp->totalData = cstrp - outp->datap;
+    outp->totalParms = outParmsTotal;
+
+    smb_SendTran2Packet(vcp, outp, op);
+    smb_FreeTran2Packet(outp);
+
+    free(rootShares.shares);
+
+    return code;
+}
+
+long smb_ReceiveRAPNetShareGetInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
+{
+    smb_tran2Packet_t *outp;
+    unsigned short * tp;
+    char * shareName;
+    BOOL shareFound = FALSE;
+    unsigned short infoLevel;
+    unsigned short bufsize;
+    int totalData;
+    int totalParam;
+    DWORD len;
+    HKEY hkParam;
+    HKEY hkSubmount;
+    DWORD allSubmount;
+    LONG rv;
+    long code = 0;
+
+    tp = p->parmsp + 1; /* skip over function number (always 1) */
+    (void) smb_ParseString( (char *) tp, (char **) &tp); /* skip over param descriptor */
+    (void) smb_ParseString( (char *) tp, (char **) &tp); /* skip over data descriptor */
+    shareName = smb_ParseString( (char *) tp, (char **) &tp);
+    infoLevel = *tp++;
+    bufsize = *tp++;
+    
+    totalParam = 6;
+
+    if (infoLevel == 0)
+        totalData = sizeof(smb_rap_share_info_0_t);
+    else if(infoLevel == SMB_INFO_STANDARD)
+        totalData = sizeof(smb_rap_share_info_1_t) + 1; /* + empty string */
+    else if(infoLevel == SMB_INFO_QUERY_EA_SIZE)
+        totalData = sizeof(smb_rap_share_info_2_t) + 2; /* + two empty strings */
+    else
+        return CM_ERROR_INVAL;
+
+    outp = smb_GetTran2ResponsePacket(vcp, p, op, totalParam, totalData);
+
+    if(!stricmp(shareName,"all")) {
+        rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0,
+                          KEY_QUERY_VALUE, &hkParam);
+        if (rv == ERROR_SUCCESS) {
+            len = sizeof(allSubmount);
+            rv = RegQueryValueEx(hkParam, "AllSubmount", NULL, NULL,
+                                  (BYTE *) &allSubmount, &len);
+            if (rv != ERROR_SUCCESS || allSubmount != 0) {
+                allSubmount = 1;
+            }
+            RegCloseKey (hkParam);
+        }
+
+        if (allSubmount)
+            shareFound = TRUE;
+
+    } else {
+        rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\OpenAFS\\Client\\Submounts", 0,
+                          KEY_QUERY_VALUE, &hkSubmount);
+        if (rv == ERROR_SUCCESS) {
+            rv = RegQueryValueEx(hkSubmount, shareName, NULL, NULL, NULL, NULL);
+            if (rv == ERROR_SUCCESS) {
+                shareFound = TRUE;
+            }
+            RegCloseKey(hkSubmount);
+        }
+    }
+
+    if (!shareFound) {
+        smb_FreeTran2Packet(outp);
+        return CM_ERROR_BADSHARENAME;
+    }
+
+    memset(outp->datap, 0, totalData);
+
+    outp->parmsp[0] = 0;
+    outp->parmsp[1] = 0;
+    outp->parmsp[2] = totalData;
+
+    if (infoLevel == 0) {
+        smb_rap_share_info_0_t * info = (smb_rap_share_info_0_t *) outp->datap;
+        strncpy(info->shi0_netname, shareName, sizeof(info->shi0_netname)-1);
+        info->shi0_netname[sizeof(info->shi0_netname)-1] = 0;
+    } else if(infoLevel == SMB_INFO_STANDARD) {
+        smb_rap_share_info_1_t * info = (smb_rap_share_info_1_t *) outp->datap;
+        strncpy(info->shi1_netname, shareName, sizeof(info->shi1_netname)-1);
+        info->shi1_netname[sizeof(info->shi1_netname)-1] = 0;
+        info->shi1_remark = ((unsigned char *) (info + 1)) - outp->datap;
+        /* type and pad are already zero */
+    } else { /* infoLevel==2 */
+        smb_rap_share_info_2_t * info = (smb_rap_share_info_2_t *) outp->datap;
+        strncpy(info->shi2_netname, shareName, sizeof(info->shi2_netname)-1);
+        info->shi2_netname[sizeof(info->shi2_netname)-1] = 0;
+        info->shi2_remark = ((unsigned char *) (info + 1)) - outp->datap;
+        info->shi2_permissions = ACCESS_ALL;
+        info->shi2_max_uses = (unsigned short) -1;
+        info->shi2_path = 1 + (((unsigned char *) (info + 1)) - outp->datap);
+    }
+
+    outp->totalData = totalData;
+    outp->totalParms = totalParam;
+
+    smb_SendTran2Packet(vcp, outp, op);
+    smb_FreeTran2Packet(outp);
+
+    return code;
+}
+
+typedef struct smb_rap_wksta_info_10 {
+    DWORD      wki10_computername;     /*char *wki10_computername;*/
+    DWORD      wki10_username; /* char *wki10_username; */
+    DWORD      wki10_langroup; /* char *wki10_langroup;*/
+    unsigned char      wki10_ver_major;
+    unsigned char      wki10_ver_minor;
+    DWORD      wki10_logon_domain;     /*char *wki10_logon_domain;*/
+    DWORD      wki10_oth_domains; /* char *wki10_oth_domains;*/
+} smb_rap_wksta_info_10_t;
+
+
+long smb_ReceiveRAPNetWkstaGetInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
+{
+    smb_tran2Packet_t *outp;
+    long code = 0;
+    int infoLevel;
+    int bufsize;
+    unsigned short * tp;
+    int totalData;
+    int totalParams;
+    smb_rap_wksta_info_10_t * info;
+    char * cstrp;
+    smb_user_t *uidp;
+
+    tp = p->parmsp + 1; /* Skip over function number */
+    (void) smb_ParseString((unsigned char*) tp, (char **) &tp); /* skip over param descriptor */
+    (void) smb_ParseString((unsigned char*) tp, (char **) &tp); /* skip over data descriptor */
+    infoLevel = *tp++;
+    bufsize = *tp++;
+
+    if (infoLevel != 10) {
+        return CM_ERROR_INVAL;
+    }
+
+    totalParams = 6;
+       
+    /* infolevel 10 */
+    totalData = sizeof(*info) +                /* info */
+        MAX_COMPUTERNAME_LENGTH +      /* wki10_computername */
+        SMB_MAX_USERNAME_LENGTH +      /* wki10_username */
+        MAX_COMPUTERNAME_LENGTH +      /* wki10_langroup */
+        MAX_COMPUTERNAME_LENGTH +      /* wki10_logon_domain */
+        1;                             /* wki10_oth_domains (null)*/
+
+    outp = smb_GetTran2ResponsePacket(vcp, p, op, totalParams, totalData);
+
+    memset(outp->parmsp,0,totalParams);
+    memset(outp->datap,0,totalData);
+
+    info = (smb_rap_wksta_info_10_t *) outp->datap;
+    cstrp = (char *) (info + 1);
+
+    info->wki10_computername = (DWORD) (cstrp - outp->datap);
+    strcpy(cstrp, smb_localNamep);
+    cstrp += strlen(cstrp) + 1;
+
+    info->wki10_username = (DWORD) (cstrp - outp->datap);
+    uidp = smb_FindUID(vcp, p->uid, 0);
+    if (uidp) {
+        lock_ObtainMutex(&uidp->mx);
+        if(uidp->unp && uidp->unp->name)
+            strcpy(cstrp, uidp->unp->name);
+        lock_ReleaseMutex(&uidp->mx);
+        smb_ReleaseUID(uidp);
+    }
+    cstrp += strlen(cstrp) + 1;
+
+    info->wki10_langroup = (DWORD) (cstrp - outp->datap);
+    strcpy(cstrp, "WORKGROUP");
+    cstrp += strlen(cstrp) + 1;
+
+    /* TODO: Not sure what values these should take, but these work */
+    info->wki10_ver_major = 5;
+    info->wki10_ver_minor = 1;
+
+    info->wki10_logon_domain = (DWORD) (cstrp - outp->datap);
+    strcpy(cstrp, smb_ServerDomainName);
+    cstrp += strlen(cstrp) + 1;
+
+    info->wki10_oth_domains = (DWORD) (cstrp - outp->datap);
+    cstrp ++; /* no other domains */
+
+    outp->totalData = (unsigned short) (cstrp - outp->datap); /* actual data size */
+    outp->parmsp[2] = outp->totalData;
+    outp->totalParms = totalParams;
+
+    smb_SendTran2Packet(vcp,outp,op);
+    smb_FreeTran2Packet(outp);
+
+    return code;
+}
+
+typedef struct smb_rap_server_info_0 {
+    char    sv0_name[16];
+} smb_rap_server_info_0_t;
+
+typedef struct smb_rap_server_info_1 {
+    char            sv1_name[16];
+    char            sv1_version_major;
+    char            sv1_version_minor;
+    unsigned long   sv1_type;
+    DWORD           *sv1_comment_or_master_browser; /* char *sv1_comment_or_master_browser;*/
+} smb_rap_server_info_1_t;
+
+char smb_ServerComment[] = "OpenAFS Client";
+int smb_ServerCommentLen = sizeof(smb_ServerComment);
+
+#define SMB_SV_TYPE_SERVER             0x00000002L
+#define SMB_SV_TYPE_NT              0x00001000L
+#define SMB_SV_TYPE_SERVER_NT       0x00008000L
+
+long smb_ReceiveRAPNetServerGetInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
+{
+    smb_tran2Packet_t *outp;
+    long code = 0;
+    int infoLevel;
+    int bufsize;
+    unsigned short * tp;
+    int totalData;
+    int totalParams;
+    smb_rap_server_info_0_t * info0;
+    smb_rap_server_info_1_t * info1;
+    char * cstrp;
+
+    tp = p->parmsp + 1; /* Skip over function number */
+    (void) smb_ParseString((unsigned char*) tp, (char **) &tp); /* skip over param descriptor */
+    (void) smb_ParseString((unsigned char*) tp, (char **) &tp); /* skip over data descriptor */
+    infoLevel = *tp++;
+    bufsize = *tp++;
+
+    if (infoLevel != 0 && infoLevel != 1) {
+        return CM_ERROR_INVAL;
+    }
+
+    totalParams = 6;
+
+    totalData = 
+        (infoLevel == 0) ? sizeof(smb_rap_server_info_0_t)
+        : (sizeof(smb_rap_server_info_1_t) + smb_ServerCommentLen);
+
+    outp = smb_GetTran2ResponsePacket(vcp, p, op, totalParams, totalData);
+
+    memset(outp->parmsp,0,totalParams);
+    memset(outp->datap,0,totalData);
+
+    if (infoLevel == 0) {
+        info0 = (smb_rap_server_info_0_t *) outp->datap;
+        cstrp = (char *) (info0 + 1);
+        strcpy(info0->sv0_name, "AFS");
+    } else { /* infoLevel == SMB_INFO_STANDARD */
+        info1 = (smb_rap_server_info_1_t *) outp->datap;
+        cstrp = (char *) (info1 + 1);
+        strcpy(info1->sv1_name, "AFS");
+
+        info1->sv1_type = 
+            SMB_SV_TYPE_SERVER |
+            SMB_SV_TYPE_NT |
+            SMB_SV_TYPE_SERVER_NT;
+
+        info1->sv1_version_major = 5;
+        info1->sv1_version_minor = 1;
+        info1->sv1_comment_or_master_browser = (DWORD *) (cstrp - outp->datap);
+
+        strcpy(cstrp, smb_ServerComment);
+
+        cstrp += smb_ServerCommentLen;
+    }
+
+    totalData = cstrp - outp->datap;
+    outp->totalData = min(bufsize,totalData); /* actual data size */
+    outp->parmsp[0] = (outp->totalData == totalData)? 0 : ERROR_MORE_DATA;
+    outp->parmsp[2] = totalData;
+    outp->totalParms = totalParams;
+
+    smb_SendTran2Packet(vcp,outp,op);
+    smb_FreeTran2Packet(outp);
+
+    return code;
+}
+
+long smb_ReceiveV3Tran2A(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
+{
+    smb_tran2Packet_t *asp;
+    int totalParms;
+    int totalData;
+    int parmDisp;
+    int dataDisp;
+    int parmOffset;
+    int dataOffset;
+    int parmCount;
+    int dataCount;
+    int firstPacket;
+    long code = 0;
+
+    /* We sometimes see 0 word count.  What to do? */
+    if (*inp->wctp == 0) {
+#ifndef DJGPP
+        HANDLE h;
+        char *ptbuf[1];
+
+        osi_Log0(smb_logp, "TRANSACTION2 word count = 0"); 
+
+        h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
+        ptbuf[0] = "Transaction2 word count = 0";
+        ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1003, NULL,
+                    1, inp->ncb_length, ptbuf, inp);
+        DeregisterEventSource(h);
+#else /* DJGPP */
+        osi_Log0(smb_logp, "TRANSACTION2 word count = 0"); 
+#endif /* !DJGPP */
+
+        smb_SetSMBDataLength(outp, 0);
+        smb_SendPacket(vcp, outp);
+        return 0;
+    }
+
+    totalParms = smb_GetSMBParm(inp, 0);
+    totalData = smb_GetSMBParm(inp, 1);
+        
+    firstPacket = (inp->inCom == 0x32);
+        
+    /* find the packet we're reassembling */
+    lock_ObtainWrite(&smb_globalLock);
+    asp = smb_FindTran2Packet(vcp, inp);
+    if (!asp) {
+        asp = smb_NewTran2Packet(vcp, inp, totalParms, totalData);
+    }
+    lock_ReleaseWrite(&smb_globalLock);
+        
+    /* now merge in this latest packet; start by looking up offsets */
+    if (firstPacket) {
+        parmDisp = dataDisp = 0;
+        parmOffset = smb_GetSMBParm(inp, 10);
+        dataOffset = smb_GetSMBParm(inp, 12);
+        parmCount = smb_GetSMBParm(inp, 9);
+        dataCount = smb_GetSMBParm(inp, 11);
+        asp->maxReturnParms = smb_GetSMBParm(inp, 2);
+        asp->maxReturnData = smb_GetSMBParm(inp, 3);
+
+        osi_Log3(smb_logp, "SMB3 received T2 init packet total data %d, cur data %d, max return data %d",
+                 totalData, dataCount, asp->maxReturnData);
+    }
+    else {
+        parmDisp = smb_GetSMBParm(inp, 4);
+        parmOffset = smb_GetSMBParm(inp, 3);
+        dataDisp = smb_GetSMBParm(inp, 7);
+        dataOffset = smb_GetSMBParm(inp, 6);
+        parmCount = smb_GetSMBParm(inp, 2);
+        dataCount = smb_GetSMBParm(inp, 5);
+
+        osi_Log2(smb_logp, "SMB3 received T2 aux packet parms %d, data %d",
+                 parmCount, dataCount);
+    }   
+
+    /* now copy the parms and data */
+    if ( asp->totalParms > 0 && parmCount != 0 )
+    {
+        memcpy(((char *)asp->parmsp) + parmDisp, inp->data + parmOffset, parmCount);
+    }
+    if ( asp->totalData > 0 && dataCount != 0 ) {
+        memcpy(asp->datap + dataDisp, inp->data + dataOffset, dataCount);
+    }
+
+    /* account for new bytes */
+    asp->curData += dataCount;
+    asp->curParms += parmCount;
+
+    /* finally, if we're done, remove the packet from the queue and dispatch it */
+    if (asp->totalParms > 0 &&
+        asp->curParms > 0 &&
+        asp->totalData <= asp->curData &&
+        asp->totalParms <= asp->curParms) {
+        /* we've received it all */
+        lock_ObtainWrite(&smb_globalLock);
+        osi_QRemove((osi_queue_t **) &smb_tran2AssemblyQueuep, &asp->q);
+        lock_ReleaseWrite(&smb_globalLock);
+
+        /* now dispatch it */
+        if ( asp->opcode >= 0 && asp->opcode < 20 && smb_tran2DispatchTable[asp->opcode].procp) {
+            osi_LogEvent("AFS-Dispatch-2[%s]",myCrt_2Dispatch(asp->opcode),"vcp[%x] lana[%d] lsn[%d]",(int)vcp,vcp->lana,vcp->lsn);
+            osi_Log4(smb_logp,"AFS Server - Dispatch-2 %s vcp[%x] lana[%d] lsn[%d]",myCrt_2Dispatch(asp->opcode),vcp,vcp->lana,vcp->lsn);
+            code = (*smb_tran2DispatchTable[asp->opcode].procp)(vcp, asp, outp);
+        }
+        else {
+            osi_LogEvent("AFS-Dispatch-2 [invalid]", NULL, "op[%x] vcp[%x] lana[%d] lsn[%d]", asp->opcode, vcp, vcp->lana, vcp->lsn);
+            osi_Log4(smb_logp,"AFS Server - Dispatch-2 [INVALID] op[%x] vcp[%x] lana[%d] lsn[%d]", asp->opcode, vcp, vcp->lana, vcp->lsn);
+            code = CM_ERROR_BADOP;
+        }
+
+        /* if an error is returned, we're supposed to send an error packet,
+         * otherwise the dispatched function already did the data sending.
+         * We give dispatched proc the responsibility since it knows how much
+         * space to allocate.
+         */
+        if (code != 0) {
+            smb_SendTran2Error(vcp, asp, outp, code);
+        }
+
+        /* free the input tran 2 packet */
+        lock_ObtainWrite(&smb_globalLock);
+        smb_FreeTran2Packet(asp);
+        lock_ReleaseWrite(&smb_globalLock);
+    }
+    else if (firstPacket) {
+        /* the first packet in a multi-packet request, we need to send an
+         * ack to get more data.
+         */
+        smb_SetSMBDataLength(outp, 0);
+        smb_SendPacket(vcp, outp);
+    }
+
+    return 0;
+}
+
+long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
+{
+    char *pathp;
+    smb_tran2Packet_t *outp;
+    long code = 0;
+    cm_space_t *spacep;
+    int excl;
+    cm_user_t *userp;
+    cm_scache_t *dscp;         /* dir we're dealing with */
+    cm_scache_t *scp;          /* file we're creating */
+    cm_attr_t setAttr;
+    int initialModeBits;
+    smb_fid_t *fidp;
+    int attributes;
+    char *lastNamep;
+    time_t dosTime;
+    int openFun;
+    int trunc;
+    int openMode;
+    int extraInfo;
+    int openAction;
+    int parmSlot;                      /* which parm we're dealing with */
+    long returnEALength;
+    char *tidPathp;
+    cm_req_t req;
+
+    cm_InitReq(&req);
+
+    scp = NULL;
+        
+    extraInfo = (p->parmsp[0] & 1);    /* return extra info */
+    returnEALength = (p->parmsp[0] & 8);       /* return extended attr length */
+
+    openFun = p->parmsp[6];            /* open function */
+    excl = ((openFun & 3) == 0);
+    trunc = ((openFun & 3) == 2);      /* truncate it */
+    openMode = (p->parmsp[1] & 0x7);
+    openAction = 0;                    /* tracks what we did */
+
+    attributes = p->parmsp[3];
+    dosTime = p->parmsp[4] | (p->parmsp[5] << 16);
+        
+    /* compute initial mode bits based on read-only flag in attributes */
+    initialModeBits = 0666;
+    if (attributes & 1) initialModeBits &= ~0222;
         
+    pathp = (char *) (&p->parmsp[14]);
+    if (smb_StoreAnsiFilenames)
+        OemToChar(pathp,pathp);
+    
     outp = smb_GetTran2ResponsePacket(vcp, p, op, 40, 0);
 
-       spacep = cm_GetSpace();
+    spacep = cm_GetSpace();
     smb_StripLastComponent(spacep->data, &lastNamep, pathp);
 
-       if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
-               /* special case magic file name for receiving IOCTL requests
+    if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
+        /* special case magic file name for receiving IOCTL requests
          * (since IOCTL calls themselves aren't getting through).
          */
         fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
         smb_SetupIoctlFid(fidp, spacep);
 
         /* copy out remainder of the parms */
-               parmSlot = 0;
-               outp->parmsp[parmSlot] = fidp->fid; parmSlot++;
-               if (extraInfo) {
+        parmSlot = 0;
+        outp->parmsp[parmSlot] = fidp->fid; parmSlot++;
+        if (extraInfo) {
             outp->parmsp[parmSlot] = /* attrs */ 0; parmSlot++;
             outp->parmsp[parmSlot] = 0; parmSlot++;    /* mod time */
             outp->parmsp[parmSlot] = 0; parmSlot++;
@@ -702,17 +2072,17 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
             outp->parmsp[parmSlot] = openMode; parmSlot++;
             outp->parmsp[parmSlot] = 0; parmSlot++; /* file type 0 ==> normal file or dir */
             outp->parmsp[parmSlot] = 0; parmSlot++; /* IPC junk */
-               }   
-               /* and the final "always present" stuff */
+        }   
+        /* and the final "always present" stuff */
         outp->parmsp[parmSlot] = /* openAction found existing file */ 1; parmSlot++;
-               /* next write out the "unique" ID */
-               outp->parmsp[parmSlot] = 0x1234; parmSlot++;
-               outp->parmsp[parmSlot] = 0x5678; parmSlot++;
+        /* next write out the "unique" ID */
+        outp->parmsp[parmSlot] = 0x1234; parmSlot++;
+        outp->parmsp[parmSlot] = 0x5678; parmSlot++;
         outp->parmsp[parmSlot] = 0; parmSlot++;
-               if (returnEALength) {
-                       outp->parmsp[parmSlot] = 0; parmSlot++;
-                       outp->parmsp[parmSlot] = 0; parmSlot++;
-        }
+        if (returnEALength) {
+            outp->parmsp[parmSlot] = 0; parmSlot++;
+            outp->parmsp[parmSlot] = 0; parmSlot++;
+        }       
                 
         outp->totalData = 0;
         outp->totalParms = parmSlot * 2;
@@ -721,179 +2091,208 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
                 
         smb_FreeTran2Packet(outp);
 
-               /* and clean up fid reference */
+        /* and clean up fid reference */
         smb_ReleaseFID(fidp);
         return 0;
     }
 
 #ifdef DEBUG_VERBOSE
-       {
-               char *hexp, *asciip;
-               asciip = (lastNamep ? lastNamep : pathp);
-               hexp = osi_HexifyString( asciip );
-               DEBUG_EVENT2("AFS","T2Open H[%s] A[%s]", hexp, asciip);
-               free(hexp);
-       }
+    {
+        char *hexp, *asciip;
+        asciip = (lastNamep ? lastNamep : pathp);
+        hexp = osi_HexifyString( asciip );
+        DEBUG_EVENT2("AFS","T2Open H[%s] A[%s]", hexp, asciip);
+        free(hexp);
+    }       
 #endif
 
-       userp = smb_GetTran2User(vcp, p);
+    userp = smb_GetTran2User(vcp, p);
     /* In the off chance that userp is NULL, we log and abandon */
-    if(!userp) {
+    if (!userp) {
         osi_Log1(smb_logp, "ReceiveTran2Open user [%d] not resolvable", p->uid);
         smb_FreeTran2Packet(outp);
         return CM_ERROR_BADSMB;
     }
 
-       tidPathp = smb_GetTIDPath(vcp, p->tid);
+    code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
+    if (code == CM_ERROR_TIDIPC) {
+        /* Attempt to use TID allocated for IPC.  The client is
+           probably trying to locate DCE RPC end points, which
+           we don't support. */
+        osi_Log0(smb_logp, "Tran2Open received IPC TID");
+        cm_ReleaseUser(userp);
+        smb_FreeTran2Packet(outp);
+        return CM_ERROR_NOSUCHPATH;
+    }
 
-       dscp = NULL;
-       code = cm_NameI(cm_rootSCachep, pathp,
-                    CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
-                    userp, tidPathp, &req, &scp);
-       if (code != 0) {
-               code = cm_NameI(cm_rootSCachep, spacep->data,
-                        CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
-                        userp, tidPathp, &req, &dscp);
-               cm_FreeSpace(spacep);
+    dscp = NULL;
+    code = cm_NameI(cm_rootSCachep, pathp,
+                     CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                     userp, tidPathp, &req, &scp);
+    if (code != 0) {
+        code = cm_NameI(cm_rootSCachep, spacep->data,
+                         CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                         userp, tidPathp, &req, &dscp);
+        cm_FreeSpace(spacep);
 
         if (code) {
             cm_ReleaseUser(userp);
-                       smb_FreeTran2Packet(outp);
+            smb_FreeTran2Packet(outp);
             return code;
         }
         
         /* otherwise, scp points to the parent directory.  Do a lookup,
-                * and truncate the file if we find it, otherwise we create the
-                * file.
+         * and truncate the file if we find it, otherwise we create the
+         * file.
          */
-        if (!lastNamep) lastNamep = pathp;
-        else lastNamep++;
+        if (!lastNamep) 
+            lastNamep = pathp;
+        else 
+            lastNamep++;
         code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD, userp,
                          &req, &scp);
         if (code && code != CM_ERROR_NOSUCHFILE) {
-                       cm_ReleaseSCache(dscp);
+            cm_ReleaseSCache(dscp);
             cm_ReleaseUser(userp);
-                       smb_FreeTran2Packet(outp);
+            smb_FreeTran2Packet(outp);
             return code;
         }
-       }
+    }
     else {
         cm_FreeSpace(spacep);
-       }
+    }
         
     /* if we get here, if code is 0, the file exists and is represented by
      * scp.  Otherwise, we have to create it.
      */
-       if (code == 0) {
+    if (code == 0) {
         code = cm_CheckOpen(scp, openMode, trunc, userp, &req);
         if (code) {
             if (dscp) cm_ReleaseSCache(dscp);
             cm_ReleaseSCache(scp);
             cm_ReleaseUser(userp);
-                       smb_FreeTran2Packet(outp);
+            smb_FreeTran2Packet(outp);
             return code;
         }
 
-               if (excl) {
-                       /* oops, file shouldn't be there */
+        if (excl) {
+            /* oops, file shouldn't be there */
             if (dscp) cm_ReleaseSCache(dscp);
             cm_ReleaseSCache(scp);
             cm_ReleaseUser(userp);
-                       smb_FreeTran2Packet(outp);
+            smb_FreeTran2Packet(outp);
             return CM_ERROR_EXISTS;
         }
 
-               if (trunc) {
-                       setAttr.mask = CM_ATTRMASK_LENGTH;
+        if (trunc) {
+            setAttr.mask = CM_ATTRMASK_LENGTH;
             setAttr.length.LowPart = 0;
             setAttr.length.HighPart = 0;
-                       code = cm_SetAttr(scp, &setAttr, userp, &req);
+            code = cm_SetAttr(scp, &setAttr, userp, &req);
             openAction = 3;    /* truncated existing file */
-               }   
-        else openAction = 1;   /* found existing file */
+        }   
+        else 
+            openAction = 1;    /* found existing file */
     }
-       else if (!(openFun & SMB_ATTR_DIRECTORY)) {
-               /* don't create if not found */
+    else if (!(openFun & SMB_ATTR_DIRECTORY)) {
+        /* don't create if not found */
         if (dscp) cm_ReleaseSCache(dscp);
         osi_assert(scp == NULL);
         cm_ReleaseUser(userp);
-               smb_FreeTran2Packet(outp);
+        smb_FreeTran2Packet(outp);
         return CM_ERROR_NOSUCHFILE;
     }
     else {
-               osi_assert(dscp != NULL && scp == NULL);
-               openAction = 2; /* created file */
-               setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
-               smb_UnixTimeFromSearchTime(&setAttr.clientModTime, dosTime);
+        osi_assert(dscp != NULL && scp == NULL);
+        openAction = 2;        /* created file */
+        setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
+        smb_UnixTimeFromSearchTime(&setAttr.clientModTime, dosTime);
         code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
-                         &req);
-               if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-                       smb_NotifyChange(FILE_ACTION_ADDED,
+                          &req);
+        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
+            smb_NotifyChange(FILE_ACTION_ADDED,
                              FILE_NOTIFY_CHANGE_FILE_NAME,  
                              dscp, lastNamep, NULL, TRUE);
         if (!excl && code == CM_ERROR_EXISTS) {
-                       /* not an exclusive create, and someone else tried
-                        * creating it already, then we open it anyway.  We
-                        * don't bother retrying after this, since if this next
-                        * fails, that means that the file was deleted after we
-                        * started this call.
+            /* not an exclusive create, and someone else tried
+             * creating it already, then we open it anyway.  We
+             * don't bother retrying after this, since if this next
+             * fails, that means that the file was deleted after we
+             * started this call.
              */
             code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
-                             userp, &req, &scp);
+                              userp, &req, &scp);
             if (code == 0) {
                 if (trunc) {
-                                       setAttr.mask = CM_ATTRMASK_LENGTH;
+                    setAttr.mask = CM_ATTRMASK_LENGTH;
                     setAttr.length.LowPart = 0;
                     setAttr.length.HighPart = 0;
                     code = cm_SetAttr(scp, &setAttr, userp,
-                                      &req);
+                                       &req);
                 }   
-                       }       /* lookup succeeded */
+            }  /* lookup succeeded */
         }
     }
         
-       /* we don't need this any longer */
-       if (dscp) cm_ReleaseSCache(dscp);
+    /* we don't need this any longer */
+    if (dscp) cm_ReleaseSCache(dscp);
 
     if (code) {
-               /* something went wrong creating or truncating the file */
+        /* something went wrong creating or truncating the file */
         if (scp) cm_ReleaseSCache(scp);
         cm_ReleaseUser(userp);
-               smb_FreeTran2Packet(outp);
+        smb_FreeTran2Packet(outp);
         return code;
     }
         
-       /* make sure we're about to open a file */
-       if (scp->fileType != CM_SCACHETYPE_FILE) {
-               cm_ReleaseSCache(scp);
-               cm_ReleaseUser(userp);
-               smb_FreeTran2Packet(outp);
-               return CM_ERROR_ISDIR;
-       }
+    /* make sure we're about to open a file */
+    if (scp->fileType != CM_SCACHETYPE_FILE) {
+        code = 0;
+        while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
+            cm_scache_t * targetScp = 0;
+            code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
+            if (code == 0) {
+                /* we have a more accurate file to use (the
+                * target of the symbolic link).  Otherwise,
+                * we'll just use the symlink anyway.
+                */
+                osi_Log2(smb_logp, "symlink vp %x to vp %x",
+                          scp, targetScp);
+                cm_ReleaseSCache(scp);
+                scp = targetScp;
+            }
+        }
+        if (scp->fileType != CM_SCACHETYPE_FILE) {
+            cm_ReleaseSCache(scp);
+            cm_ReleaseUser(userp);
+            smb_FreeTran2Packet(outp);
+            return CM_ERROR_ISDIR;
+        }
+    }
 
     /* now all we have to do is open the file itself */
     fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
     osi_assert(fidp);
        
-       /* save a pointer to the vnode */
+    /* save a pointer to the vnode */
     fidp->scp = scp;
         
-       /* compute open mode */
+    /* compute open mode */
     if (openMode != 1) fidp->flags |= SMB_FID_OPENREAD;
     if (openMode == 1 || openMode == 2)
         fidp->flags |= SMB_FID_OPENWRITE;
 
-       smb_ReleaseFID(fidp);
+    smb_ReleaseFID(fidp);
         
-       cm_Open(scp, 0, userp);
+    cm_Open(scp, 0, userp);
 
     /* copy out remainder of the parms */
-       parmSlot = 0;
-       outp->parmsp[parmSlot] = fidp->fid; parmSlot++;
-       lock_ObtainMutex(&scp->mx);
-       if (extraInfo) {
+    parmSlot = 0;
+    outp->parmsp[parmSlot] = fidp->fid; parmSlot++;
+    lock_ObtainMutex(&scp->mx);
+    if (extraInfo) {
         outp->parmsp[parmSlot] = smb_Attributes(scp); parmSlot++;
-               smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
+        smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
         outp->parmsp[parmSlot] = (unsigned short)(dosTime & 0xffff); parmSlot++;
         outp->parmsp[parmSlot] = (unsigned short)((dosTime>>16) & 0xffff); parmSlot++;
         outp->parmsp[parmSlot] = (unsigned short) (scp->length.LowPart & 0xffff);
@@ -903,23 +2302,23 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
         outp->parmsp[parmSlot] = openMode; parmSlot++;
         outp->parmsp[parmSlot] = 0; parmSlot++; /* file type 0 ==> normal file or dir */
         outp->parmsp[parmSlot] = 0; parmSlot++; /* IPC junk */
-       }   
-       /* and the final "always present" stuff */
+    }   
+    /* and the final "always present" stuff */
     outp->parmsp[parmSlot] = openAction; parmSlot++;
-       /* next write out the "unique" ID */
-       outp->parmsp[parmSlot] = (unsigned short) (scp->fid.vnode & 0xffff); parmSlot++;
-       outp->parmsp[parmSlot] = (unsigned short) (scp->fid.volume & 0xffff); parmSlot++;
+    /* next write out the "unique" ID */
+    outp->parmsp[parmSlot] = (unsigned short) (scp->fid.vnode & 0xffff); parmSlot++;
+    outp->parmsp[parmSlot] = (unsigned short) (scp->fid.volume & 0xffff); parmSlot++;
     outp->parmsp[parmSlot] = 0; parmSlot++;
     if (returnEALength) {
-               outp->parmsp[parmSlot] = 0; parmSlot++;
-               outp->parmsp[parmSlot] = 0; parmSlot++;
-    }
-       lock_ReleaseMutex(&scp->mx);
-       outp->totalData = 0;            /* total # of data bytes */
+        outp->parmsp[parmSlot] = 0; parmSlot++;
+        outp->parmsp[parmSlot] = 0; parmSlot++;
+    }   
+    lock_ReleaseMutex(&scp->mx);
+    outp->totalData = 0;               /* total # of data bytes */
     outp->totalParms = parmSlot * 2;   /* shorts are two bytes */
 
-       smb_SendTran2Packet(vcp, outp, op);
-        
+    smb_SendTran2Packet(vcp, outp, op);
+
     smb_FreeTran2Packet(outp);
 
     cm_ReleaseUser(userp);
@@ -929,103 +2328,107 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
 
 long smb_ReceiveTran2FindFirst(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    osi_Log0(smb_logp,"ReceiveTran2FindFirst - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
 long smb_ReceiveTran2FindNext(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    osi_Log0(smb_logp,"ReceiveTran2FindNext - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
 long smb_ReceiveTran2QFSInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
 {
-       smb_tran2Packet_t *outp;
+    smb_tran2Packet_t *outp;
     smb_tran2QFSInfo_t qi;
-       int responseSize;
-       osi_hyper_t temp;
-       static char FSname[6] = {'A', 0, 'F', 0, 'S', 0};
+    int responseSize;
+    osi_hyper_t temp;
+    static char FSname[6] = {'A', 0, 'F', 0, 'S', 0};
         
-       osi_Log1(smb_logp, "T2 QFSInfo type 0x%x", p->parmsp[0]);
-
-       switch (p->parmsp[0]) {
-       case 1: responseSize = sizeof(qi.u.allocInfo); break;
-       case 2: responseSize = sizeof(qi.u.volumeInfo); break;
-       case 0x102: responseSize = sizeof(qi.u.FSvolumeInfo); break;
-       case 0x103: responseSize = sizeof(qi.u.FSsizeInfo); break;
-       case 0x104: responseSize = sizeof(qi.u.FSdeviceInfo); break;
-       case 0x105: responseSize = sizeof(qi.u.FSattributeInfo); break;
-       default: return CM_ERROR_INVAL;
-       }
+    osi_Log1(smb_logp, "T2 QFSInfo type 0x%x", p->parmsp[0]);
+
+    switch (p->parmsp[0]) {
+    case 1: responseSize = sizeof(qi.u.allocInfo); break;
+    case 2: responseSize = sizeof(qi.u.volumeInfo); break;
+    case 0x102: responseSize = sizeof(qi.u.FSvolumeInfo); break;
+    case 0x103: responseSize = sizeof(qi.u.FSsizeInfo); break;
+    case 0x104: responseSize = sizeof(qi.u.FSdeviceInfo); break;
+    case 0x105: responseSize = sizeof(qi.u.FSattributeInfo); break;
+    case 0x200: /* CIFS Unix Info */
+    case 0x301: /* Mac FS Info */
+    default: return CM_ERROR_INVAL;
+    }
 
     outp = smb_GetTran2ResponsePacket(vcp, p, op, 0, responseSize);
-       switch (p->parmsp[0]) {
-       case 1:
-               /* alloc info */
+    switch (p->parmsp[0]) {
+    case 1:
+        /* alloc info */
         qi.u.allocInfo.FSID = 0;
         qi.u.allocInfo.sectorsPerAllocUnit = 1;
         qi.u.allocInfo.totalAllocUnits = 0x7fffffff;
         qi.u.allocInfo.availAllocUnits = 0x3fffffff;
         qi.u.allocInfo.bytesPerSector = 1024;
-               break;
+        break;
 
     case 2:
-               /* volume info */
+        /* volume info */
         qi.u.volumeInfo.vsn = 1234;
         qi.u.volumeInfo.vnCount = 4;
-               /* we're supposed to pad it out with zeroes to the end */
-               memset(&qi.u.volumeInfo.label, 0, sizeof(qi.u.volumeInfo.label));
+        /* we're supposed to pad it out with zeroes to the end */
+        memset(&qi.u.volumeInfo.label, 0, sizeof(qi.u.volumeInfo.label));
         memcpy(qi.u.volumeInfo.label, "AFS", 4);
-               break;
-
-       case 0x102:
-               /* FS volume info */
-               memset((char *)&qi.u.FSvolumeInfo.vct, 0, sizeof(FILETIME));
-               qi.u.FSvolumeInfo.vsn = 1234;
-               qi.u.FSvolumeInfo.vnCount = 8;
-               memcpy(qi.u.FSvolumeInfo.label, "A\0F\0S\0\0", 8);
-               break;
-
-       case 0x103:
-               /* FS size info */
-               temp.HighPart = 0;
-               temp.LowPart = 0x7fffffff;
-               qi.u.FSsizeInfo.totalAllocUnits = temp;
-               temp.LowPart = 0x3fffffff;
-               qi.u.FSsizeInfo.availAllocUnits = temp;
-               qi.u.FSsizeInfo.sectorsPerAllocUnit = 1;
-               qi.u.FSsizeInfo.bytesPerSector = 1024;
-               break;
-
-       case 0x104:
-               /* FS device info */
-               qi.u.FSdeviceInfo.devType = 0;  /* don't have a number */
-               qi.u.FSdeviceInfo.characteristics = 0x50; /* remote, virtual */
-               break;
+        break;
+
+    case 0x102:
+        /* FS volume info */
+        memset((char *)&qi.u.FSvolumeInfo.vct, 0, sizeof(FILETIME));
+        qi.u.FSvolumeInfo.vsn = 1234;
+        qi.u.FSvolumeInfo.vnCount = 8;
+        memcpy(qi.u.FSvolumeInfo.label, "A\0F\0S\0\0", 8);
+        break;
+
+    case 0x103:
+        /* FS size info */
+        temp.HighPart = 0;
+        temp.LowPart = 0x7fffffff;
+        qi.u.FSsizeInfo.totalAllocUnits = temp;
+        temp.LowPart = 0x3fffffff;
+        qi.u.FSsizeInfo.availAllocUnits = temp;
+        qi.u.FSsizeInfo.sectorsPerAllocUnit = 1;
+        qi.u.FSsizeInfo.bytesPerSector = 1024;
+        break;
+
+    case 0x104:
+        /* FS device info */
+        qi.u.FSdeviceInfo.devType = 0; /* don't have a number */
+        qi.u.FSdeviceInfo.characteristics = 0x50; /* remote, virtual */
+        break;
 
        case 0x105:
-               /* FS attribute info */
-               /* attributes, defined in WINNT.H:
-                *      FILE_CASE_SENSITIVE_SEARCH      0x1
-                *      FILE_CASE_PRESERVED_NAMES       0x2
-                *      <no name defined>               0x4000
-                *         If bit 0x4000 is not set, Windows 95 thinks
-                *         we can't handle long (non-8.3) names,
-                *         despite our protestations to the contrary.
-                */
-               qi.u.FSattributeInfo.attributes = 0x4003;
-               qi.u.FSattributeInfo.maxCompLength = 255;
-               qi.u.FSattributeInfo.FSnameLength = 6;
-               memcpy(qi.u.FSattributeInfo.FSname, FSname, 6);
-               break;
-    }
+        /* FS attribute info */
+        /* attributes, defined in WINNT.H:
+         *     FILE_CASE_SENSITIVE_SEARCH      0x1
+         *     FILE_CASE_PRESERVED_NAMES       0x2
+         *     <no name defined>               0x4000
+         *        If bit 0x4000 is not set, Windows 95 thinks
+         *        we can't handle long (non-8.3) names,
+         *        despite our protestations to the contrary.
+         */
+        qi.u.FSattributeInfo.attributes = 0x4003;
+        qi.u.FSattributeInfo.maxCompLength = 255;
+        qi.u.FSattributeInfo.FSnameLength = 6;
+        memcpy(qi.u.FSattributeInfo.FSname, FSname, 6);
+        break;
+    }   
         
-       /* copy out return data, and set corresponding sizes */
-       outp->totalParms = 0;
+    /* copy out return data, and set corresponding sizes */
+    outp->totalParms = 0;
     outp->totalData = responseSize;
     memcpy(outp->datap, &qi, responseSize);
 
-       /* send and free the packets */
-       smb_SendTran2Packet(vcp, outp, op);
+    /* send and free the packets */
+    smb_SendTran2Packet(vcp, outp, op);
     smb_FreeTran2Packet(outp);
 
     return 0;
@@ -1033,168 +2436,182 @@ long smb_ReceiveTran2QFSInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *
 
 long smb_ReceiveTran2SetFSInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    osi_Log0(smb_logp,"ReceiveTran2SetFSInfo - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
 struct smb_ShortNameRock {
-       char *maskp;
-       unsigned int vnode;
-       char *shortName;
-       size_t shortNameLen;
-};
+    char *maskp;
+    unsigned int vnode;
+    char *shortName;
+    size_t shortNameLen;
+};      
 
 int cm_GetShortNameProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *vrockp,
-       osi_hyper_t *offp)
-{
-       struct smb_ShortNameRock *rockp;
-       char *shortNameEnd;
-
-       rockp = vrockp;
-       /* compare both names and vnodes, though probably just comparing vnodes
-        * would be safe enough.
-        */
-       if (stricmp(dep->name, rockp->maskp) != 0)
-               return 0;
-       if (ntohl(dep->fid.vnode) != rockp->vnode)
-               return 0;
-       /* This is the entry */
-       cm_Gen8Dot3Name(dep, rockp->shortName, &shortNameEnd);
-       rockp->shortNameLen = shortNameEnd - rockp->shortName;
-       return CM_ERROR_STOPNOW;
-}
+                         osi_hyper_t *offp)
+{       
+    struct smb_ShortNameRock *rockp;
+    char *shortNameEnd;
+
+    rockp = vrockp;
+    /* compare both names and vnodes, though probably just comparing vnodes
+     * would be safe enough.
+     */
+    if (cm_stricmp(dep->name, rockp->maskp) != 0)
+        return 0;
+    if (ntohl(dep->fid.vnode) != rockp->vnode)
+        return 0;
+    /* This is the entry */
+    cm_Gen8Dot3Name(dep, rockp->shortName, &shortNameEnd);
+    rockp->shortNameLen = shortNameEnd - rockp->shortName;
+    return CM_ERROR_STOPNOW;
+}       
 
 long cm_GetShortName(char *pathp, cm_user_t *userp, cm_req_t *reqp,
        char *tidPathp, int vnode, char *shortName, size_t *shortNameLenp)
 {
-       struct smb_ShortNameRock rock;
-       char *lastNamep;
-       cm_space_t *spacep;
-       cm_scache_t *dscp;
-       int caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
-       long code = 0;
-       osi_hyper_t thyper;
-
-       spacep = cm_GetSpace();
-       smb_StripLastComponent(spacep->data, &lastNamep, pathp);
-
-       code = cm_NameI(cm_rootSCachep, spacep->data, caseFold, userp, tidPathp,
+    struct smb_ShortNameRock rock;
+    char *lastNamep;
+    cm_space_t *spacep;
+    cm_scache_t *dscp;
+    int caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
+    long code = 0;
+    osi_hyper_t thyper;
+
+    spacep = cm_GetSpace();
+    smb_StripLastComponent(spacep->data, &lastNamep, pathp);
+
+    code = cm_NameI(cm_rootSCachep, spacep->data, caseFold, userp, tidPathp,
                      reqp, &dscp);
-       cm_FreeSpace(spacep);
-       if (code) return code;
-
-       if (!lastNamep) lastNamep = pathp;
-       else lastNamep++;
-       thyper.LowPart = 0;
-       thyper.HighPart = 0;
-       rock.shortName = shortName;
-       rock.vnode = vnode;
-       rock.maskp = lastNamep;
-       code = cm_ApplyDir(dscp, cm_GetShortNameProc, &rock, &thyper, userp,
-                        reqp, NULL);
-
-       cm_ReleaseSCache(dscp);
-
-       if (code == 0)
-               return CM_ERROR_NOSUCHFILE;
-       if (code == CM_ERROR_STOPNOW) {
-               *shortNameLenp = rock.shortNameLen;
-               return 0;
-       }
-       return code;
+    cm_FreeSpace(spacep);
+    if (code) return code;
+
+    if (!lastNamep) lastNamep = pathp;
+    else lastNamep++;
+    thyper.LowPart = 0;
+    thyper.HighPart = 0;
+    rock.shortName = shortName;
+    rock.vnode = vnode;
+    rock.maskp = lastNamep;
+    code = cm_ApplyDir(dscp, cm_GetShortNameProc, &rock, &thyper, userp, reqp, NULL);
+
+    cm_ReleaseSCache(dscp);
+
+    if (code == 0)
+        return CM_ERROR_NOSUCHFILE;
+    if (code == CM_ERROR_STOPNOW) {
+        *shortNameLenp = rock.shortNameLen;
+        return 0;
+    }
+    return code;
 }
 
 long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
 {
-       smb_tran2Packet_t *outp;
-    unsigned long dosTime;
-       FILETIME ft;
+    smb_tran2Packet_t *outp;
+    time_t dosTime;
+    FILETIME ft;
     unsigned short infoLevel;
     int nbytesRequired;
     unsigned short attributes;
-       unsigned long extAttributes;
-       char shortName[13];
-       unsigned int len;
+    unsigned long extAttributes;
+    char shortName[13];
+    unsigned int len;
     cm_user_t *userp;
-       cm_space_t *spacep;
+    cm_space_t *spacep;
     cm_scache_t *scp, *dscp;
     long code = 0;
     char *op;
-       char *tidPathp;
-       char *lastComp;
-       cm_req_t req;
-
-       cm_InitReq(&req);
-
-       infoLevel = p->parmsp[0];
-    if (infoLevel == 6) nbytesRequired = 0;
-    else if (infoLevel == 1) nbytesRequired = 22;
-    else if (infoLevel == 2) nbytesRequired = 26;
-       else if (infoLevel == 0x101) nbytesRequired = 40;
-       else if (infoLevel == 0x102) nbytesRequired = 24;
-       else if (infoLevel == 0x103) nbytesRequired = 4;
-       else if (infoLevel == 0x108) nbytesRequired = 30;
+    char *tidPathp;
+    char *lastComp;
+    cm_req_t req;
+
+    cm_InitReq(&req);
+
+    infoLevel = p->parmsp[0];
+    if (infoLevel == SMB_INFO_IS_NAME_VALID) 
+        nbytesRequired = 0;
+    else if (infoLevel == SMB_INFO_STANDARD) 
+        nbytesRequired = 22;
+    else if (infoLevel == SMB_INFO_QUERY_EA_SIZE) 
+        nbytesRequired = 26;
+    else if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) 
+        nbytesRequired = 40;
+    else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO) 
+        nbytesRequired = 24;
+    else if (infoLevel == SMB_QUERY_FILE_EA_INFO) 
+        nbytesRequired = 4;
+    else if (infoLevel == SMB_QUERY_FILE_ALT_NAME_INFO) 
+        nbytesRequired = 30;
     else {
-               osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
+        osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
                   p->opcode, infoLevel);
-               smb_SendTran2Error(vcp, p, opx, CM_ERROR_INVAL);
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_INVAL);
         return 0;
     }
-       osi_Log2(smb_logp, "T2 QPathInfo type 0x%x path %s", infoLevel,
-             osi_LogSaveString(smb_logp, (char *)(&p->parmsp[3])));
+    osi_Log2(smb_logp, "T2 QPathInfo type 0x%x path %s", infoLevel,
+              osi_LogSaveString(smb_logp, (char *)(&p->parmsp[3])));
 
     outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, nbytesRequired);
 
-       if (infoLevel > 0x100)
-               outp->totalParms = 2;
-       else
-               outp->totalParms = 0;
-        outp->totalData = nbytesRequired;
+    if (infoLevel > 0x100)
+        outp->totalParms = 2;
+    else
+        outp->totalParms = 0;
+    outp->totalData = nbytesRequired;
         
     /* now, if we're at infoLevel 6, we're only being asked to check
      * the syntax, so we just OK things now.  In particular, we're *not*
      * being asked to verify anything about the state of any parent dirs.
      */
-       if (infoLevel == 6) {
-               smb_SendTran2Packet(vcp, outp, opx);
+    if (infoLevel == SMB_INFO_IS_NAME_VALID) {
+        smb_SendTran2Packet(vcp, outp, opx);
         smb_FreeTran2Packet(outp);
-               return 0;
-    }
+        return 0;
+    }   
         
     userp = smb_GetTran2User(vcp, p);
-    if(!userp) {
+    if (!userp) {
         osi_Log1(smb_logp, "ReceiveTran2QPathInfo unable to resolve user [%d]", p->uid);
         smb_FreeTran2Packet(outp);
         return CM_ERROR_BADSMB;
     }
 
-       tidPathp = smb_GetTIDPath(vcp, p->tid);
-
-       /*
-        * XXX Strange hack XXX
-        *
-        * As of Patch 7 (13 January 98), we are having the following problem:
-        * In NT Explorer 4.0, whenever we click on a directory, AFS gets
-        * requests to look up "desktop.ini" in all the subdirectories.
-        * This can cause zillions of timeouts looking up non-existent cells
-        * and volumes, especially in the top-level directory.
-        *
-        * We have not found any way to avoid this or work around it except
-        * to explicitly ignore the requests for mount points that haven't
-        * yet been evaluated and for directories that haven't yet been
-        * fetched.
-        */
-       if (infoLevel == 0x101) {
-               spacep = cm_GetSpace();
-               smb_StripLastComponent(spacep->data, &lastComp,
-                                       (char *)(&p->parmsp[3]));
-               /* Make sure that lastComp is not NULL */
-               if (lastComp) {
-                   if (strcmp(lastComp, "\\desktop.ini") == 0) {
+    code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
+    if(code) {
+        cm_ReleaseUser(userp);
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOSUCHPATH);
+        smb_FreeTran2Packet(outp);
+        return 0;
+    }
+
+    /*
+     * XXX Strange hack XXX
+     *
+     * As of Patch 7 (13 January 98), we are having the following problem:
+     * In NT Explorer 4.0, whenever we click on a directory, AFS gets
+     * requests to look up "desktop.ini" in all the subdirectories.
+     * This can cause zillions of timeouts looking up non-existent cells
+     * and volumes, especially in the top-level directory.
+     *
+     * We have not found any way to avoid this or work around it except
+     * to explicitly ignore the requests for mount points that haven't
+     * yet been evaluated and for directories that haven't yet been
+     * fetched.
+     */
+    if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) {
+        spacep = cm_GetSpace();
+        smb_StripLastComponent(spacep->data, &lastComp,
+                                (char *)(&p->parmsp[3]));
+#ifndef SPECIAL_FOLDERS
+        /* Make sure that lastComp is not NULL */
+        if (lastComp) {
+            if (stricmp(lastComp, "\\desktop.ini") == 0) {
                 code = cm_NameI(cm_rootSCachep, spacep->data,
-                                CM_FLAG_CASEFOLD
-                                | CM_FLAG_DIRSEARCH
-                                | CM_FLAG_FOLLOW,
-                                userp, tidPathp, &req, &dscp);
+                                 CM_FLAG_CASEFOLD
+                                 | CM_FLAG_DIRSEARCH
+                                 | CM_FLAG_FOLLOW,
+                                 userp, tidPathp, &req, &dscp);
                 if (code == 0) {
                     if (dscp->fileType == CM_SCACHETYPE_MOUNTPOINT
                          && !dscp->mountRootFidp)
@@ -1217,90 +2634,92 @@ long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
                 }
             }
         }
-               cm_FreeSpace(spacep);
-       }
+#endif /* SPECIAL_FOLDERS */
+
+        cm_FreeSpace(spacep);
+    }
 
-       /* now do namei and stat, and copy out the info */
+    /* now do namei and stat, and copy out the info */
     code = cm_NameI(cm_rootSCachep, (char *)(&p->parmsp[3]),
-                    CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, tidPathp, &req, &scp);
+                     CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, tidPathp, &req, &scp);
 
-       if (code) {
-               cm_ReleaseUser(userp);
+    if (code) {
+        cm_ReleaseUser(userp);
         smb_SendTran2Error(vcp, p, opx, code);
         smb_FreeTran2Packet(outp);
         return 0;
     }
 
     lock_ObtainMutex(&scp->mx);
-        code = cm_SyncOp(scp, NULL, userp, &req, 0,
-                         CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-       if (code) goto done;
+    code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) goto done;
         
     /* now we have the status in the cache entry, and everything is locked.
-        * Marshall the output data.
+     * Marshall the output data.
      */
-       op = outp->datap;
-       /* for info level 108, figure out short name */
-       if (infoLevel == 0x108) {
-               code = cm_GetShortName((char *)(&p->parmsp[3]), userp, &req,
+    op = outp->datap;
+    /* for info level 108, figure out short name */
+    if (infoLevel == 0x108) {
+        code = cm_GetShortName((char *)(&p->parmsp[3]), userp, &req,
                                 tidPathp, scp->fid.vnode, shortName,
                                 (size_t *) &len);
-               if (code) {
-                       goto done;
-               }
+        if (code) {
+            goto done;
+        }
 
-               op = outp->datap;
-               *((u_long *)op) = len * 2; op += 4;
-               mbstowcs((unsigned short *)op, shortName, len);
-               op += (len * 2);
+        op = outp->datap;
+        *((u_long *)op) = len * 2; op += 4;
+        mbstowcs((unsigned short *)op, shortName, len);
+        op += (len * 2);
 
-               goto done;
-       }
-       if (infoLevel == 1 || infoLevel == 2) {
-               smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
+        goto done;
+    }
+    if (infoLevel == SMB_INFO_STANDARD || infoLevel == SMB_INFO_QUERY_EA_SIZE) {
+        smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
         *((u_long *)op) = dosTime; op += 4;    /* creation time */
         *((u_long *)op) = dosTime; op += 4;    /* access time */
         *((u_long *)op) = dosTime; op += 4;    /* write time */
         *((u_long *)op) = scp->length.LowPart; op += 4;        /* length */
-        *((u_long *)op) = scp->length.LowPart; op += 4;        /* alloc size */
-               attributes = smb_Attributes(scp);
-               *((u_short *)op) = attributes; op += 2; /* attributes */
-       }
-       else if (infoLevel == 0x101) {
-               smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
-               *((FILETIME *)op) = ft; op += 8;        /* creation time */
-               *((FILETIME *)op) = ft; op += 8;        /* last access time */
-               *((FILETIME *)op) = ft; op += 8;        /* last write time */
-               *((FILETIME *)op) = ft; op += 8;        /* last change time */
-               extAttributes = smb_ExtAttributes(scp);
-               *((u_long *)op) = extAttributes; op += 4; /* extended attribs */
-               *((u_long *)op) = 0; op += 4;   /* don't know what this is */
-       }
-       else if (infoLevel == 0x102) {
-               *((LARGE_INTEGER *)op) = scp->length; op += 8;  /* alloc size */
-               *((LARGE_INTEGER *)op) = scp->length; op += 8;  /* EOF */
-               *((u_long *)op) = scp->linkCount; op += 4;
-               *op++ = 0;
-               *op++ = 0;
-               *op++ = (scp->fileType == CM_SCACHETYPE_DIRECTORY ? 1 : 0);
-               *op++ = 0;
-       }
-       else if (infoLevel == 0x103) {
-               memset(op, 0, 4); op += 4;      /* EA size */
-       }
+        *((u_long *)op) = scp->length.LowPart; op += 4;        /* alloc size */
+        attributes = smb_Attributes(scp);
+        *((u_short *)op) = attributes; op += 2;        /* attributes */
+    }
+    else if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) {
+        smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
+        *((FILETIME *)op) = ft; op += 8;       /* creation time */
+        *((FILETIME *)op) = ft; op += 8;       /* last access time */
+        *((FILETIME *)op) = ft; op += 8;       /* last write time */
+        *((FILETIME *)op) = ft; op += 8;       /* last change time */
+        extAttributes = smb_ExtAttributes(scp);
+        *((u_long *)op) = extAttributes; op += 4; /* extended attribs */
+        *((u_long *)op) = 0; op += 4;  /* don't know what this is */
+    }
+    else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO) {
+        *((LARGE_INTEGER *)op) = scp->length; op += 8; /* alloc size */
+        *((LARGE_INTEGER *)op) = scp->length; op += 8; /* EOF */
+        *((u_long *)op) = scp->linkCount; op += 4;
+        *op++ = 0;
+        *op++ = 0;
+        *op++ = (scp->fileType == CM_SCACHETYPE_DIRECTORY ? 1 : 0);
+        *op++ = 0;
+    }
+    else if (infoLevel == SMB_QUERY_FILE_EA_INFO) {
+        memset(op, 0, 4); op += 4;     /* EA size */
+    }
+
+    /* now, if we are being asked about extended attrs, return a 0 size */
+    if (infoLevel == SMB_INFO_QUERY_EA_SIZE) {
+        *((u_long *)op) = 0; op += 4;
+    }
 
-        /* now, if we are being asked about extended attrs, return a 0 size */
-       if (infoLevel == 2) {
-               *((u_long *)op) = 0; op += 4;
-       }
-        
 
-       /* send and free the packets */
+    /* send and free the packets */
   done:
-       lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseMutex(&scp->mx);
     cm_ReleaseSCache(scp);
     cm_ReleaseUser(userp);
-       if (code == 0) 
+    if (code == 0) 
         smb_SendTran2Packet(vcp, outp, opx);
     else 
         smb_SendTran2Error(vcp, p, opx, code);
@@ -1311,304 +2730,367 @@ long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
 
 long smb_ReceiveTran2SetPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    osi_Log0(smb_logp,"ReceiveTran2SetPathInfo - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
 long smb_ReceiveTran2QFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
 {
-       smb_tran2Packet_t *outp;
-       FILETIME ft;
-       unsigned long attributes;
-       unsigned short infoLevel;
-       int nbytesRequired;
-       unsigned short fid;
-       cm_user_t *userp;
+    smb_tran2Packet_t *outp;
+    FILETIME ft;
+    unsigned long attributes;
+    unsigned short infoLevel;
+    int nbytesRequired;
+    unsigned short fid;
+    cm_user_t *userp;
     smb_fid_t *fidp;
-       cm_scache_t *scp;
-       char *op;
-       long code = 0;
-       cm_req_t req;
+    cm_scache_t *scp;
+    char *op;
+    long code = 0;
+    cm_req_t req;
 
-       cm_InitReq(&req);
+    cm_InitReq(&req);
 
     fid = p->parmsp[0];
     fidp = smb_FindFID(vcp, fid, 0);
 
-       if (fidp == NULL) {
-               smb_SendTran2Error(vcp, p, opx, CM_ERROR_BADFD);
-               return 0;
-       }
+    if (fidp == NULL) {
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_BADFD);
+        return 0;
+    }
 
-       infoLevel = p->parmsp[1];
-       if (infoLevel == 0x101) nbytesRequired = 40;
-       else if (infoLevel == 0x102) nbytesRequired = 24;
-       else if (infoLevel == 0x103) nbytesRequired = 4;
-       else if (infoLevel == 0x104) nbytesRequired = 6;
-       else {
-               osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
-                 p->opcode, infoLevel);
-               smb_SendTran2Error(vcp, p, opx, CM_ERROR_INVAL);
+    infoLevel = p->parmsp[1];
+    if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) 
+        nbytesRequired = 40;
+    else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO) 
+        nbytesRequired = 24;
+    else if (infoLevel == SMB_QUERY_FILE_EA_INFO)
+        nbytesRequired = 4;
+    else if (infoLevel == SMB_QUERY_FILE_NAME_INFO) 
+        nbytesRequired = 6;
+    else {
+        osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
+                  p->opcode, infoLevel);
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_INVAL);
         smb_ReleaseFID(fidp);
-               return 0;
-       }
-       osi_Log2(smb_logp, "T2 QFileInfo type 0x%x fid %d", infoLevel, fid);
+        return 0;
+    }
+    osi_Log2(smb_logp, "T2 QFileInfo type 0x%x fid %d", infoLevel, fid);
 
-       outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, nbytesRequired);
+    outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, nbytesRequired);
 
-       if (infoLevel > 0x100)
-               outp->totalParms = 2;
-       else
-               outp->totalParms = 0;
-       outp->totalData = nbytesRequired;
+    if (infoLevel > 0x100)
+        outp->totalParms = 2;
+    else
+        outp->totalParms = 0;
+    outp->totalData = nbytesRequired;
 
-       userp = smb_GetTran2User(vcp, p);
-    if(!userp) {
+    userp = smb_GetTran2User(vcp, p);
+    if (!userp) {
        osi_Log1(smb_logp, "ReceiveTran2QFileInfo unable to resolve user [%d]", p->uid);
        code = CM_ERROR_BADSMB;
        goto done;
-    }
+    }   
 
-       scp = fidp->scp;
-       lock_ObtainMutex(&scp->mx);
-       code = cm_SyncOp(scp, NULL, userp, &req, 0,
-                     CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-       if (code) goto done;
+    scp = fidp->scp;
+    lock_ObtainMutex(&scp->mx);
+    code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+    if (code) 
+        goto done;
 
-       /* now we have the status in the cache entry, and everything is locked.
-        * Marshall the output data.
-        */
-       op = outp->datap;
-       if (infoLevel == 0x101) {
-               smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
-               *((FILETIME *)op) = ft; op += 8;        /* creation time */
-               *((FILETIME *)op) = ft; op += 8;        /* last access time */
-               *((FILETIME *)op) = ft; op += 8;        /* last write time */
-               *((FILETIME *)op) = ft; op += 8;        /* last change time */
-               attributes = smb_ExtAttributes(scp);
-               *((u_long *)op) = attributes; op += 4;
-               *((u_long *)op) = 0; op += 4;
-       }
-       else if (infoLevel == 0x102) {
-               *((LARGE_INTEGER *)op) = scp->length; op += 8;  /* alloc size */
-               *((LARGE_INTEGER *)op) = scp->length; op += 8;  /* EOF */
-               *((u_long *)op) = scp->linkCount; op += 4;
-               *op++ = ((fidp->flags & SMB_FID_DELONCLOSE) ? 1 : 0);
-               *op++ = (scp->fileType == CM_SCACHETYPE_DIRECTORY ? 1 : 0);
-               *op++ = 0;
-               *op++ = 0;
-       }
-       else if (infoLevel == 0x103) {
-               *((u_long *)op) = 0; op += 4;
-       }
-       else if (infoLevel == 0x104) {
-               unsigned long len;
-               char *name;
-
-               if (fidp->NTopen_wholepathp)
-                       name = fidp->NTopen_wholepathp;
-               else
-                       name = "\\";    /* probably can't happen */
-               len = strlen(name);
-               outp->totalData = (len*2) + 4;  /* this is actually what we want to return */
-               *((u_long *)op) = len * 2; op += 4;
-               mbstowcs((unsigned short *)op, name, len); op += (len * 2);
-       }
+    /* now we have the status in the cache entry, and everything is locked.
+     * Marshall the output data.
+     */
+    op = outp->datap;
+    if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) {
+        smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
+        *((FILETIME *)op) = ft; op += 8;       /* creation time */
+        *((FILETIME *)op) = ft; op += 8;       /* last access time */
+        *((FILETIME *)op) = ft; op += 8;       /* last write time */
+        *((FILETIME *)op) = ft; op += 8;       /* last change time */
+        attributes = smb_ExtAttributes(scp);
+        *((u_long *)op) = attributes; op += 4;
+        *((u_long *)op) = 0; op += 4;
+    }
+    else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO) {
+        *((LARGE_INTEGER *)op) = scp->length; op += 8; /* alloc size */
+        *((LARGE_INTEGER *)op) = scp->length; op += 8; /* EOF */
+        *((u_long *)op) = scp->linkCount; op += 4;
+        *op++ = ((fidp->flags & SMB_FID_DELONCLOSE) ? 1 : 0);
+        *op++ = (scp->fileType == CM_SCACHETYPE_DIRECTORY ? 1 : 0);
+        *op++ = 0;
+        *op++ = 0;
+    }
+    else if (infoLevel == SMB_QUERY_FILE_EA_INFO) {
+        *((u_long *)op) = 0; op += 4;
+    }
+    else if (infoLevel == SMB_QUERY_FILE_NAME_INFO) {
+        unsigned long len;
+        char *name;
+
+        if (fidp->NTopen_wholepathp)
+            name = fidp->NTopen_wholepathp;
+        else
+            name = "\\";       /* probably can't happen */
+        len = strlen(name);
+        outp->totalData = (len*2) + 4; /* this is actually what we want to return */
+        *((u_long *)op) = len * 2; op += 4;
+        mbstowcs((unsigned short *)op, name, len); op += (len * 2);
+    }
 
-       /* send and free the packets */
+    /* send and free the packets */
   done:
-       lock_ReleaseMutex(&scp->mx);
-       cm_ReleaseUser(userp);
-       smb_ReleaseFID(fidp);
-       if (code == 0) smb_SendTran2Packet(vcp, outp, opx);
-       else smb_SendTran2Error(vcp, p, opx, code);
-       smb_FreeTran2Packet(outp);
-
-       return 0;
-}
+    lock_ReleaseMutex(&scp->mx);
+    cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
+    if (code == 0) 
+        smb_SendTran2Packet(vcp, outp, opx);
+    else 
+        smb_SendTran2Error(vcp, p, opx, code);
+    smb_FreeTran2Packet(outp);
+
+    return 0;
+}       
 
 long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
 {
-       long code = 0;
-       unsigned short fid;
-       smb_fid_t *fidp;
-       unsigned short infoLevel;
-       smb_tran2Packet_t *outp;
-       cm_user_t *userp;
-       cm_scache_t *scp;
-       cm_req_t req;
+    long code = 0;
+    unsigned short fid;
+    smb_fid_t *fidp;
+    unsigned short infoLevel;
+    smb_tran2Packet_t *outp;
+    cm_user_t *userp;
+    cm_scache_t *scp;
+    cm_req_t req;
 
-       cm_InitReq(&req);
+    cm_InitReq(&req);
 
     fid = p->parmsp[0];
-       fidp = smb_FindFID(vcp, fid, 0);
+    fidp = smb_FindFID(vcp, fid, 0);
 
-       if (fidp == NULL) {
-               smb_SendTran2Error(vcp, p, op, CM_ERROR_BADFD);
-               return 0;
-       }
+    if (fidp == NULL) {
+        smb_SendTran2Error(vcp, p, op, CM_ERROR_BADFD);
+        return 0;
+    }
 
-       infoLevel = p->parmsp[1];
-       if (infoLevel > 0x104 || infoLevel < 0x101) {
-               osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
-                        p->opcode, infoLevel);
-               smb_SendTran2Error(vcp, p, op, CM_ERROR_INVAL);
+    infoLevel = p->parmsp[1];
+    osi_Log2(smb_logp,"ReceiveTran2SetFileInfo type=[%x] fid=[%x]", infoLevel, fid);
+    if (infoLevel > 0x104 || infoLevel < 0x101) {
+        osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
+                  p->opcode, infoLevel);
+        smb_SendTran2Error(vcp, p, op, CM_ERROR_INVAL);
         smb_ReleaseFID(fidp);
-               return 0;
-       }
+        return 0;
+    }
 
-       if (infoLevel == 0x102 && !(fidp->flags & SMB_FID_OPENDELETE)) {
-               smb_SendTran2Error(vcp, p, op, CM_ERROR_NOACCESS);
+    if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO && !(fidp->flags & SMB_FID_OPENDELETE)) {
+        smb_SendTran2Error(vcp, p, op, CM_ERROR_NOACCESS);
         smb_ReleaseFID(fidp);
-               return 0;
-       }
-       if ((infoLevel == 0x103 || infoLevel == 0x104)
-           && !(fidp->flags & SMB_FID_OPENWRITE)) {
-               smb_SendTran2Error(vcp, p, op, CM_ERROR_NOACCESS);
+        return 0;
+    }
+    if ((infoLevel == SMB_QUERY_FILE_EA_INFO || infoLevel == SMB_QUERY_FILE_NAME_INFO)
+         && !(fidp->flags & SMB_FID_OPENWRITE)) {
+        smb_SendTran2Error(vcp, p, op, CM_ERROR_NOACCESS);
         smb_ReleaseFID(fidp);
-               return 0;
-       }
+        return 0;
+    }
 
-       osi_Log1(smb_logp, "T2 SFileInfo type 0x%x", infoLevel);
+    osi_Log1(smb_logp, "T2 SFileInfo type 0x%x", infoLevel);
 
-       outp = smb_GetTran2ResponsePacket(vcp, p, op, 2, 0);
+    outp = smb_GetTran2ResponsePacket(vcp, p, op, 2, 0);
 
-       outp->totalParms = 2;
-       outp->totalData = 0;
+    outp->totalParms = 2;
+    outp->totalData = 0;
 
-       userp = smb_GetTran2User(vcp, p);
-    if(!userp) {
+    userp = smb_GetTran2User(vcp, p);
+    if (!userp) {
        osi_Log1(smb_logp,"ReceiveTran2SetFileInfo unable to resolve user [%d]", p->uid);
        code = CM_ERROR_BADSMB;
        goto done;
-    }
+    }   
 
-       scp = fidp->scp;
+    scp = fidp->scp;
 
-       if (infoLevel == 0x101) {
-               FILETIME lastMod;
-               unsigned int attribute;
-               cm_attr_t attr;
+    if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) {
+        FILETIME lastMod;
+        unsigned int attribute;
+        cm_attr_t attr;
 
-               /* lock the vnode with a callback; we need the current status
-                * to determine what the new status is, in some cases.
-                */
-               lock_ObtainMutex(&scp->mx);
-               code = cm_SyncOp(scp, NULL, userp, &req, 0,
-                         CM_SCACHESYNC_GETSTATUS
+        /* lock the vnode with a callback; we need the current status
+         * to determine what the new status is, in some cases.
+         */
+        lock_ObtainMutex(&scp->mx);
+        code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                          CM_SCACHESYNC_GETSTATUS
                          | CM_SCACHESYNC_NEEDCALLBACK);
-               if (code) {
-                       lock_ReleaseMutex(&scp->mx);
-                       goto done;
-               }
+        if (code) {
+            lock_ReleaseMutex(&scp->mx);
+            goto done;
+        }
 
-               /* prepare for setattr call */
-               attr.mask = 0;
-               
-               lastMod = *((FILETIME *)(p->datap + 16));
-               /* when called as result of move a b, lastMod is (-1, -1). 
+        /* prepare for setattr call */
+        attr.mask = 0;
+
+        lastMod = *((FILETIME *)(p->datap + 16));
+        /* when called as result of move a b, lastMod is (-1, -1). 
          * If the check for -1 is not present, timestamp
-                * of the resulting file will be 1969 (-1)
-                */
-               if (LargeIntegerNotEqualToZero(*((LARGE_INTEGER *)&lastMod)) && 
-            lastMod.dwLowDateTime != -1 && lastMod.dwHighDateTime != -1) {
-                       attr.mask |= CM_ATTRMASK_CLIENTMODTIME;
-                       smb_UnixTimeFromLargeSearchTime(&attr.clientModTime,
-                                                       &lastMod);
-                       fidp->flags |= SMB_FID_MTIMESETDONE;
-               }
+         * of the resulting file will be 1969 (-1)
+         */
+        if (LargeIntegerNotEqualToZero(*((LARGE_INTEGER *)&lastMod)) && 
+             lastMod.dwLowDateTime != -1 && lastMod.dwHighDateTime != -1) {
+            attr.mask |= CM_ATTRMASK_CLIENTMODTIME;
+            smb_UnixTimeFromLargeSearchTime(&attr.clientModTime,
+                                             &lastMod);
+            fidp->flags |= SMB_FID_MTIMESETDONE;
+        }
                
-               attribute = *((u_long *)(p->datap + 32));
-               if (attribute != 0) {
-                       if ((scp->unixModeBits & 0222)
-                           && (attribute & 1) != 0) {
-                               /* make a writable file read-only */
-                               attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
-                               attr.unixModeBits = scp->unixModeBits & ~0222;
-                       }
-                       else if ((scp->unixModeBits & 0222) == 0
-                                && (attribute & 1) == 0) {
-                               /* make a read-only file writable */
-                               attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
-                               attr.unixModeBits = scp->unixModeBits | 0222;
-                       }
-               }
-               lock_ReleaseMutex(&scp->mx);
-
-               /* call setattr */
-               if (attr.mask)
-                       code = cm_SetAttr(scp, &attr, userp, &req);
-               else
-                       code = 0;
-       }
-       else if (infoLevel == 0x103 || infoLevel == 0x104) {
-               LARGE_INTEGER size = *((LARGE_INTEGER *)(p->datap));
-               cm_attr_t attr;
-
-               attr.mask = CM_ATTRMASK_LENGTH;
-               attr.length.LowPart = size.LowPart;
-               attr.length.HighPart = size.HighPart;
-               code = cm_SetAttr(scp, &attr, userp, &req);
-       }
-       else if (infoLevel == 0x102) {
-               if (*((char *)(p->datap))) {
-                       code = cm_CheckNTDelete(fidp->NTopen_dscp, scp, userp,
-                                               &req);
-                       if (code == 0)
-                               fidp->flags |= SMB_FID_DELONCLOSE;
-               }
-               else {
-                       code = 0;
-                       fidp->flags &= ~SMB_FID_DELONCLOSE;
-               }
-       }
+        attribute = *((u_long *)(p->datap + 32));
+        if (attribute != 0) {
+            if ((scp->unixModeBits & 0222)
+                 && (attribute & 1) != 0) {
+                /* make a writable file read-only */
+                attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
+                attr.unixModeBits = scp->unixModeBits & ~0222;
+            }
+            else if ((scp->unixModeBits & 0222) == 0
+                      && (attribute & 1) == 0) {
+                /* make a read-only file writable */
+                attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
+                attr.unixModeBits = scp->unixModeBits | 0222;
+            }
+        }
+        lock_ReleaseMutex(&scp->mx);
+
+        /* call setattr */
+        if (attr.mask)
+            code = cm_SetAttr(scp, &attr, userp, &req);
+        else
+            code = 0;
+    }               
+    else if (infoLevel == SMB_QUERY_FILE_EA_INFO || infoLevel == SMB_QUERY_FILE_NAME_INFO) {
+        LARGE_INTEGER size = *((LARGE_INTEGER *)(p->datap));
+        cm_attr_t attr;
+
+        attr.mask = CM_ATTRMASK_LENGTH;
+        attr.length.LowPart = size.LowPart;
+        attr.length.HighPart = size.HighPart;
+        code = cm_SetAttr(scp, &attr, userp, &req);
+    }       
+    else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO) {
+        if (*((char *)(p->datap))) {
+            code = cm_CheckNTDelete(fidp->NTopen_dscp, scp, userp,
+                                     &req);
+            if (code == 0)          
+                fidp->flags |= SMB_FID_DELONCLOSE;
+        }               
+        else {  
+            code = 0;
+            fidp->flags &= ~SMB_FID_DELONCLOSE;
+        }
+    }       
+
   done:
-       cm_ReleaseUser(userp);
-       smb_ReleaseFID(fidp);
-       if (code == 0) smb_SendTran2Packet(vcp, outp, op);
-       else smb_SendTran2Error(vcp, p, op, code);
-       smb_FreeTran2Packet(outp);
+    cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
+    if (code == 0) 
+        smb_SendTran2Packet(vcp, outp, op);
+    else 
+        smb_SendTran2Error(vcp, p, op, code);
+    smb_FreeTran2Packet(outp);
+
+    return 0;
+}
+
+long 
+smb_ReceiveTran2FSCTL(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
+{
+    osi_Log0(smb_logp,"ReceiveTran2FSCTL - NOT_SUPPORTED");
+    return CM_ERROR_BADOP;
+}
+
+long 
+smb_ReceiveTran2IOCTL(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
+{
+    osi_Log0(smb_logp,"ReceiveTran2IOCTL - NOT_SUPPORTED");
+    return CM_ERROR_BADOP;
+}
 
-       return 0;
+long 
+smb_ReceiveTran2FindNotifyFirst(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
+{
+    osi_Log0(smb_logp,"ReceiveTran2FindNotifyFirst - NOT_SUPPORTED");
+    return CM_ERROR_BADOP;
 }
 
-long smb_ReceiveTran2FSCTL(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
+long 
+smb_ReceiveTran2FindNotifyNext(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    osi_Log0(smb_logp,"ReceiveTran2FindNotifyNext - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
-long smb_ReceiveTran2IOCTL(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
+long 
+smb_ReceiveTran2CreateDirectory(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    osi_Log0(smb_logp,"ReceiveTran2CreateDirectory - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
-long smb_ReceiveTran2FindNotifyFirst(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
+long 
+smb_ReceiveTran2SessionSetup(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    osi_Log0(smb_logp,"ReceiveTran2SessionSetup - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
-long smb_ReceiveTran2FindNotifyNext(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
+long 
+smb_ReceiveTran2GetDFSReferral(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    /* This is a UNICODE only request (bit15 of Flags2) */
+    /* The TID must be IPC$ */
+
+    /* The documentation for the Flags response field is contradictory */
+
+    /* Use Version 1 Referral Element Format */
+    /* ServerType = 0; indicates the next server should be queried for the file */
+    /* ReferralFlags = 0x01; PathConsumed characters should be stripped */
+    /* Node = UnicodeString of UNC path of the next share name */
+
+    osi_Log0(smb_logp,"ReceiveTran2GetDFSReferral - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
-long smb_ReceiveTran2MKDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
+long 
+smb_ReceiveTran2ReportDFSInconsistency(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
 {
+    /* This is a UNICODE only request (bit15 of Flags2) */
+
+    /* There is nothing we can do about this operation.  The client is going to
+     * tell us that there is a Version 1 Referral Element for which there is a DFS Error.
+     * Unfortunately, there is really nothing we can do about it other then log it 
+     * somewhere.  Even then I don't think there is anything for us to do.
+     * So let's return an error value.
+     */
+
+    osi_Log0(smb_logp,"ReceiveTran2ReportDFSInconsistency - NOT_SUPPORTED");
     return CM_ERROR_BADOP;
 }
 
-long smb_ApplyV3DirListPatches(cm_scache_t *dscp,
+long 
+smb_ApplyV3DirListPatches(cm_scache_t *dscp,
        smb_dirListPatch_t **dirPatchespp, int infoLevel, cm_user_t *userp,
        cm_req_t *reqp)
 {
-       long code = 0;
+    long code = 0;
     cm_scache_t *scp;
     cm_scache_t *targetScp;                    /* target if scp is a symlink */
     char *dptr;
-    long dosTime;
-       FILETIME ft;
+    time_t dosTime;
+    FILETIME ft;
     int shortTemp;
     unsigned short attr;
-       unsigned long lattr;
+    unsigned long lattr;
     smb_dirListPatch_t *patchp;
     smb_dirListPatch_t *npatchp;
         
@@ -1619,125 +3101,198 @@ long smb_ApplyV3DirListPatches(cm_scache_t *dscp,
         lock_ObtainMutex(&scp->mx);
         code = cm_SyncOp(scp, NULL, userp, reqp, 0,
                           CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-               if (code) { 
-                       lock_ReleaseMutex(&scp->mx);
-                       cm_ReleaseSCache(scp);
-                       continue;
+        if (code) { 
+            lock_ReleaseMutex(&scp->mx);
+            cm_ReleaseSCache(scp);
+
+            dptr = patchp->dptr;
+
+            /* Plug in fake timestamps. A time stamp of 0 causes 'invalid parameter'
+               errors in the client. */
+            if (infoLevel >= 0x101) {
+                /* 1969-12-31 23:59:59 +00 */
+                ft.dwHighDateTime = 0x19DB200;
+                ft.dwLowDateTime = 0x5BB78980;
+
+                /* copy to Creation Time */
+                *((FILETIME *)dptr) = ft;
+                dptr += 8;
+
+                /* copy to Last Access Time */
+                *((FILETIME *)dptr) = ft;
+                dptr += 8;
+
+                /* copy to Last Write Time */
+                *((FILETIME *)dptr) = ft;
+                dptr += 8;
+
+                /* copy to Change Time */
+                *((FILETIME *)dptr) = ft;
+                dptr += 24;
+
+                /* merge in hidden attribute */
+                if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE ) {
+                    *((u_long *)dptr) = SMB_ATTR_HIDDEN;
+                }
+                dptr += 4;
+            } else {
+                /* 1969-12-31 23:59:58 +00*/
+                dosTime = 0xEBBFBF7D;
+
+                /* and copy out date */
+                shortTemp = (dosTime>>16) & 0xffff;
+                *((u_short *)dptr) = shortTemp;
+                dptr += 2;
+
+                /* copy out creation time */
+                shortTemp = dosTime & 0xffff;
+                *((u_short *)dptr) = shortTemp;
+                dptr += 2;
+
+                /* and copy out date */
+                shortTemp = (dosTime>>16) & 0xffff;
+                *((u_short *)dptr) = shortTemp;
+                dptr += 2;
+                       
+                /* copy out access time */
+                shortTemp = dosTime & 0xffff;
+                *((u_short *)dptr) = shortTemp;
+                dptr += 2;
+
+                /* and copy out date */
+                shortTemp = (dosTime>>16) & 0xffff;
+                *((u_short *)dptr) = shortTemp;
+                dptr += 2;
+                       
+                /* copy out mod time */
+                shortTemp = dosTime & 0xffff;
+                *((u_short *)dptr) = shortTemp;
+                dptr += 10;
+
+                /* merge in hidden (dot file) attribute */
+                if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE ) {
+                    attr = SMB_ATTR_HIDDEN;
+                    *dptr++ = attr & 0xff;
+                    *dptr++ = (attr >> 8) & 0xff;
+                }       
+            }
+            continue;
         }
                 
         /* now watch for a symlink */
-        if (scp->fileType == CM_SCACHETYPE_SYMLINK) {
-                       lock_ReleaseMutex(&scp->mx);
+        code = 0;
+        while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
+            lock_ReleaseMutex(&scp->mx);
             code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, reqp);
             if (code == 0) {
-                               /* we have a more accurate file to use (the
-                                * target of the symbolic link).  Otherwise,
-                                * we'll just use the symlink anyway.
+                /* we have a more accurate file to use (the
+                 * target of the symbolic link).  Otherwise,
+                 * we'll just use the symlink anyway.
                  */
-                               osi_Log2(smb_logp, "symlink vp %x to vp %x",
-                         scp, targetScp);
-                               cm_ReleaseSCache(scp);
+                osi_Log2(smb_logp, "symlink vp %x to vp %x",
+                          scp, targetScp);
+                cm_ReleaseSCache(scp);
                 scp = targetScp;
             }
             lock_ObtainMutex(&scp->mx);
         }
 
-               dptr = patchp->dptr;
+        dptr = patchp->dptr;
 
-               if (infoLevel >= 0x101) {
-                       /* get filetime */
-                       smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
+        if (infoLevel >= 0x101) {
+            /* get filetime */
+            smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
 
-                       /* copy to Creation Time */
-                       *((FILETIME *)dptr) = ft;
-                       dptr += 8;
+            /* copy to Creation Time */
+            *((FILETIME *)dptr) = ft;
+            dptr += 8;
 
-                       /* copy to Last Access Time */
-                       *((FILETIME *)dptr) = ft;
-                       dptr += 8;
+            /* copy to Last Access Time */
+            *((FILETIME *)dptr) = ft;
+            dptr += 8;
 
-                       /* copy to Last Write Time */
-                       *((FILETIME *)dptr) = ft;
-                       dptr += 8;
+            /* copy to Last Write Time */
+            *((FILETIME *)dptr) = ft;
+            dptr += 8;
 
-                       /* copy to Change Time */
-                       *((FILETIME *)dptr) = ft;
-                       dptr += 8;
+            /* copy to Change Time */
+            *((FILETIME *)dptr) = ft;
+            dptr += 8;
 
-                       /* Use length for both file length and alloc length */
-                       *((LARGE_INTEGER *)dptr) = scp->length;
-                       dptr += 8;
-                       *((LARGE_INTEGER *)dptr) = scp->length;
-                       dptr += 8;
+            /* Use length for both file length and alloc length */
+            *((LARGE_INTEGER *)dptr) = scp->length;
+            dptr += 8;
+            *((LARGE_INTEGER *)dptr) = scp->length;
+            dptr += 8;
 
-                       /* Copy attributes */
-                       lattr = smb_ExtAttributes(scp);
+            /* Copy attributes */
+            lattr = smb_ExtAttributes(scp);
             /* merge in hidden (dot file) attribute */
-                       if( patchp->flags & SMB_DIRLISTPATCH_DOTFILE )
-                               lattr |= SMB_ATTR_HIDDEN;
-                       *((u_long *)dptr) = lattr;
-                       dptr += 4;
-               }
-               else {
-                       /* get dos time */
-                       smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
-
-                       /* and copy out date */
-                       shortTemp = (dosTime>>16) & 0xffff;
-                       *((u_short *)dptr) = shortTemp;
-                       dptr += 2;
-
-                       /* copy out creation time */
-                       shortTemp = dosTime & 0xffff;
-                       *((u_short *)dptr) = shortTemp;
-                       dptr += 2;
-
-                       /* and copy out date */
-                       shortTemp = (dosTime>>16) & 0xffff;
-                       *((u_short *)dptr) = shortTemp;
-                       dptr += 2;
-                       
-                       /* copy out access time */
-                       shortTemp = dosTime & 0xffff;
-                       *((u_short *)dptr) = shortTemp;
-                       dptr += 2;
-
-                       /* and copy out date */
-                       shortTemp = (dosTime>>16) & 0xffff;
-                       *((u_short *)dptr) = shortTemp;
-                       dptr += 2;
-                       
-                       /* copy out mod time */
-                       shortTemp = dosTime & 0xffff;
-                       *((u_short *)dptr) = shortTemp;
-                       dptr += 2;
-
-                       /* copy out file length and alloc length,
-                        * using the same for both
-                        */
-                       *((u_long *)dptr) = scp->length.LowPart;
-                       dptr += 4;
-                       *((u_long *)dptr) = scp->length.LowPart;
-                       dptr += 4;
-
-                       /* finally copy out attributes as short */
-                       attr = smb_Attributes(scp);
+            if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE )
+                lattr |= SMB_ATTR_HIDDEN;
+            *((u_long *)dptr) = lattr;
+            dptr += 4;
+        }
+        else {
+            /* get dos time */
+            smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
+
+            /* and copy out date */
+            shortTemp = (dosTime>>16) & 0xffff;
+            *((u_short *)dptr) = shortTemp;
+            dptr += 2;
+
+            /* copy out creation time */
+            shortTemp = dosTime & 0xffff;
+            *((u_short *)dptr) = shortTemp;
+            dptr += 2;
+
+            /* and copy out date */
+            shortTemp = (dosTime>>16) & 0xffff;
+            *((u_short *)dptr) = shortTemp;
+            dptr += 2;
+
+            /* copy out access time */
+            shortTemp = dosTime & 0xffff;
+            *((u_short *)dptr) = shortTemp;
+            dptr += 2;
+
+            /* and copy out date */
+            shortTemp = (dosTime>>16) & 0xffff;
+            *((u_short *)dptr) = shortTemp;
+            dptr += 2;
+
+            /* copy out mod time */
+            shortTemp = dosTime & 0xffff;
+            *((u_short *)dptr) = shortTemp;
+            dptr += 2;
+
+            /* copy out file length and alloc length,
+             * using the same for both
+             */
+            *((u_long *)dptr) = scp->length.LowPart;
+            dptr += 4;
+            *((u_long *)dptr) = scp->length.LowPart;
+            dptr += 4;
+
+            /* finally copy out attributes as short */
+            attr = smb_Attributes(scp);
             /* merge in hidden (dot file) attribute */
-            if( patchp->flags & SMB_DIRLISTPATCH_DOTFILE )
+            if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE )
                 attr |= SMB_ATTR_HIDDEN;
-                       *dptr++ = attr & 0xff;
-                       *dptr++ = (attr >> 8) & 0xff;
-               }
+            *dptr++ = attr & 0xff;
+            *dptr++ = (attr >> 8) & 0xff;
+        }
 
         lock_ReleaseMutex(&scp->mx);
         cm_ReleaseSCache(scp);
-       }
+    }
         
     /* now free the patches */
-    for(patchp = *dirPatchespp; patchp; patchp = npatchp) {
-               npatchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q);
+    for (patchp = *dirPatchespp; patchp; patchp = npatchp) {
+        npatchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q);
         free(patchp);
-       }
+    }
         
     /* and mark the list as empty */
     *dirPatchespp = NULL;
@@ -1745,144 +3300,266 @@ long smb_ApplyV3DirListPatches(cm_scache_t *dscp,
     return code;
 }
 
+#ifndef USE_OLD_MATCHING
+// char table for case insensitive comparison
+char mapCaseTable[256];
+
+VOID initUpperCaseTable(VOID) 
+{
+    int i;
+    for (i = 0; i < 256; ++i) 
+       mapCaseTable[i] = toupper(i);
+    // make '"' match '.' 
+    mapCaseTable[(int)'"'] = toupper('.');
+    // make '<' match '*' 
+    mapCaseTable[(int)'<'] = toupper('*');
+    // make '>' match '?' 
+    mapCaseTable[(int)'>'] = toupper('?');    
+}
+
+// Compare 'pattern' (containing metacharacters '*' and '?') with the file
+// name 'name'.
+// Note : this procedure works recursively calling itself.
+// Parameters
+// PSZ pattern    : string containing metacharacters.
+// PSZ name       : file name to be compared with 'pattern'.
+// Return value
+// BOOL : TRUE/FALSE (match/mistmatch)
+
+BOOL 
+szWildCardMatchFileName(PSZ pattern, PSZ name) 
+{
+    PSZ pename;         // points to the last 'name' character
+    PSZ p;
+    pename = name + strlen(name) - 1;
+    while (*name) {
+        switch (*pattern) {
+        case '?':
+            if (*name == '.') 
+                return FALSE;
+            ++pattern, ++name;
+            break;
+         case '*':
+            ++pattern;
+            if (*pattern == '\0')
+                return TRUE;
+            for (p = pename; p >= name; --p) {
+                if ((mapCaseTable[*p] == mapCaseTable[*pattern]) &&
+                     szWildCardMatchFileName(pattern + 1, p + 1))
+                    return TRUE;
+            } /* endfor */
+            return FALSE;
+        default:
+            if (mapCaseTable[*name] != mapCaseTable[*pattern]) 
+                return FALSE;
+            ++pattern, ++name;
+            break;
+        } /* endswitch */
+    } /* endwhile */ 
+
+    if (*pattern == '\0' || *pattern == '*' && *(pattern+1) == '\0')
+        return TRUE;
+    else 
+        return FALSE;
+}
+
+/* do a case-folding search of the star name mask with the name in namep.
+ * Return 1 if we match, otherwise 0.
+ */
+int smb_V3MatchMask(char *namep, char *maskp, int flags) 
+{
+    char * newmask;
+    int    i, j, star, qmark, retval;
+
+    /* make sure we only match 8.3 names, if requested */
+    if ((flags & CM_FLAG_8DOT3) && !cm_Is8Dot3(namep)) 
+        return 0;
+    
+    /* optimize the pattern:
+     * if there is a mixture of '?' and '*',
+     * for example  the sequence "*?*?*?*"
+     * must be turned into the form "*"
+     */
+    newmask = (char *)malloc(strlen(maskp)+1);
+    for ( i=0, j=0, star=0, qmark=0; maskp[i]; i++) {
+        switch ( maskp[i] ) {
+        case '?':
+        case '>':
+            qmark++;
+            break;
+        case '<':
+        case '*':
+            star++;
+            break;
+        default:
+            if ( star ) {
+                newmask[j++] = '*';
+            } else if ( qmark ) {
+                while ( qmark-- )
+                    newmask[j++] = '?';
+            }
+            newmask[j++] = maskp[i];
+            star = 0;
+            qmark = 0;
+        }
+    }
+    if ( star ) {
+        newmask[j++] = '*';
+    } else if ( qmark ) {
+        while ( qmark-- )
+            newmask[j++] = '?';
+    }
+    newmask[j++] = '\0';
+
+    retval = szWildCardMatchFileName(newmask, namep) ? 1:0;
+
+    free(newmask);
+    return retval;
+}
+
+#else /* USE_OLD_MATCHING */
 /* do a case-folding search of the star name mask with the name in namep.
  * Return 1 if we match, otherwise 0.
  */
 int smb_V3MatchMask(char *namep, char *maskp, int flags)
 {
-       unsigned char tcp1, tcp2;       /* Pattern characters */
+    unsigned char tcp1, tcp2;  /* Pattern characters */
     unsigned char tcn1;                /* Name characters */
-       int sawDot = 0, sawStar = 0, req8dot3 = 0;
-       char *starNamep, *starMaskp;
-       static char nullCharp[] = {0};
+    int sawDot = 0, sawStar = 0, req8dot3 = 0;
+    char *starNamep, *starMaskp;
+    static char nullCharp[] = {0};
+    int casefold = flags & CM_FLAG_CASEFOLD;
 
-       /* make sure we only match 8.3 names, if requested */
+    /* make sure we only match 8.3 names, if requested */
     req8dot3 = (flags & CM_FLAG_8DOT3);
-       if (req8dot3 && !cm_Is8Dot3(namep)) 
+    if (req8dot3 && !cm_Is8Dot3(namep)) 
         return 0;
 
-       /* loop */
-       while (1) {
-               /* Next pattern character */
-               tcp1 = *maskp++;
-
-               /* Next name character */
-               tcn1 = *namep;
-
-               if (tcp1 == 0) {
-                       /* 0 - end of pattern */
-                       if (tcn1 == 0)
-                               return 1;
-                       else
-                               return 0;
-               }
-               else if (tcp1 == '.' || tcp1 == '"') {
-                       if (sawDot) {
-                               if (tcn1 == '.') {
-                                       namep++;
-                                       continue;
-                               } else
-                                       return 0;
-                       }
-                       else {
-                               /*
-                                * first dot in pattern;
-                                * must match dot or end of name
-                                */
-                               sawDot = 1;
-                               if (tcn1 == 0)
-                                       continue;
-                               else if (tcn1 == '.') {
-                                       sawStar = 0;
-                                       namep++;
-                                       continue;
-                               }
-                               else
-                                       return 0;
-                       }
-               }
-               else if (tcp1 == '?') {
-                       if (tcn1 == 0 || tcn1 == '.')
-                               return 0;
-                       namep++;
-                       continue;
-               }
-               else if (tcp1 == '>') {
-                       if (tcn1 != 0 && tcn1 != '.')
-                               namep++;
-                       continue;
-               }
-               else if (tcp1 == '*' || tcp1 == '<') {
-                       tcp2 = *maskp++;
-                       if (tcp2 == 0)
-                               return 1;
-                       else if ((req8dot3 && tcp2 == '.') || tcp2 == '"') {
-                               while (req8dot3 && tcn1 != '.' && tcn1 != 0)
-                                       tcn1 = *++namep;
-                               if (tcn1 == 0) {
-                                       if (sawDot)
-                                               return 0;
-                                       else
-                                               continue;
-                               }
-                               else {
-                                       namep++;
-                                       continue;
-                               }
-                       }
-                       else {
-                               /*
-                                * pattern character after '*' is not null or
-                                * period.  If it is '?' or '>', we are not
-                                * going to understand it.  If it is '*' or
-                                * '<', we are going to skip over it.  None of
-                                * these are likely, I hope.
-                                */
-                               /* skip over '*' and '<' */
-                               while (tcp2 == '*' || tcp2 == '<')
-                                       tcp2 = *maskp++;
-
-                               /* skip over characters that don't match tcp2 */
-                               while (req8dot3 && tcn1 != '.' && tcn1 != 0
-                                       && cm_foldUpper[tcn1] != cm_foldUpper[tcp2])
-                                       tcn1 = *++namep;
-
-                               /* No match */
-                               if ((req8dot3 && tcn1 == '.') || tcn1 == 0)
-                                       return 0;
-
-                               /* Remember where we are */
-                               sawStar = 1;
-                               starMaskp = maskp;
-                               starNamep = namep;
-
-                               namep++;
-                               continue;
-                       }
-               }
-               else {
-                       /* tcp1 is not a wildcard */
-                       if (cm_foldUpper[tcn1] == cm_foldUpper[tcp1]) {
-                               /* they match */
-                               namep++;
-                               continue;
-                       }
-                       /* if trying to match a star pattern, go back */
-                       if (sawStar) {
-                               maskp = starMaskp - 2;
-                               namep = starNamep + 1;
-                               sawStar = 0;
-                               continue;
-                       }
-                       /* that's all */
-                       return 0;
-               }
-       }
+    /* loop */
+    while (1) {
+        /* Next pattern character */
+        tcp1 = *maskp++;
+
+        /* Next name character */
+        tcn1 = *namep;
+
+        if (tcp1 == 0) {
+            /* 0 - end of pattern */
+            if (tcn1 == 0)
+                return 1;
+            else
+                return 0;
+        }
+        else if (tcp1 == '.' || tcp1 == '"') {
+            if (sawDot) {
+                if (tcn1 == '.') {
+                    namep++;
+                    continue;
+                } else
+                    return 0;
+            }
+            else {
+                /*
+                 * first dot in pattern;
+                 * must match dot or end of name
+                 */
+                sawDot = 1;
+                if (tcn1 == 0)
+                    continue;
+                else if (tcn1 == '.') {
+                    sawStar = 0;
+                    namep++;
+                    continue;
+                }
+                else
+                    return 0;
+            }
+        }
+        else if (tcp1 == '?') {
+            if (tcn1 == 0 || tcn1 == '.')
+                return 0;
+            namep++;
+            continue;
+        }
+        else if (tcp1 == '>') {
+            if (tcn1 != 0 && tcn1 != '.')
+                namep++;
+            continue;
+        }
+        else if (tcp1 == '*' || tcp1 == '<') {
+            tcp2 = *maskp++;
+            if (tcp2 == 0)
+                return 1;
+            else if ((req8dot3 && tcp2 == '.') || tcp2 == '"') {
+                while (req8dot3 && tcn1 != '.' && tcn1 != 0)
+                    tcn1 = *++namep;
+                if (tcn1 == 0) {
+                    if (sawDot)
+                        return 0;
+                    else
+                        continue;
+                }
+                else {
+                    namep++;
+                    continue;
+                }
+            }
+            else {
+                /*
+                 * pattern character after '*' is not null or
+                 * period.  If it is '?' or '>', we are not
+                 * going to understand it.  If it is '*' or
+                 * '<', we are going to skip over it.  None of
+                 * these are likely, I hope.
+                 */
+                /* skip over '*' and '<' */
+                while (tcp2 == '*' || tcp2 == '<')
+                    tcp2 = *maskp++;
+
+                /* skip over characters that don't match tcp2 */
+                while (req8dot3 && tcn1 != '.' && tcn1 != 0 && 
+                        ((casefold && cm_foldUpper[tcn1] != cm_foldUpper[tcp2]) || 
+                          (!casefold && tcn1 != tcp2)))
+                    tcn1 = *++namep;
+
+                /* No match */
+                if ((req8dot3 && tcn1 == '.') || tcn1 == 0)
+                    return 0;
+
+                /* Remember where we are */
+                sawStar = 1;
+                starMaskp = maskp;
+                starNamep = namep;
+
+                namep++;
+                continue;
+            }
+        }
+        else {
+            /* tcp1 is not a wildcard */
+            if ((casefold && cm_foldUpper[tcn1] == cm_foldUpper[tcp1]) || 
+                 (!casefold && tcn1 == tcp1)) {
+                /* they match */
+                namep++;
+                continue;
+            }
+            /* if trying to match a star pattern, go back */
+            if (sawStar) {
+                maskp = starMaskp - 2;
+                namep = starNamep + 1;
+                sawStar = 0;
+                continue;
+            }
+            /* that's all */
+            return 0;
+        }
+    }
 }
+#endif /* USE_OLD_MATCHING */
 
 long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
 {
-       int attribute;
+    int attribute;
     long nextCookie;
     char *tp;
     long code = 0;
@@ -1904,14 +3581,14 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     cm_scache_t *scp;
     long entryInDir;
     long entryInBuffer;
-       cm_pageHeader_t *pageHeaderp;
+    cm_pageHeader_t *pageHeaderp;
     cm_user_t *userp = NULL;
     int slotInPage;
     int returnedNames;
     long nextEntryCookie;
     int numDirChunks;          /* # of 32 byte dir chunks in this entry */
     char *op;                  /* output data ptr */
-       char *origOp;                   /* original value of op */
+    char *origOp;                      /* original value of op */
     cm_space_t *spacep;                /* for pathname buffer */
     long maxReturnData;                /* max # of return data */
     long maxReturnParms;               /* max # of return parms */
@@ -1922,21 +3599,21 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     int searchFlags;
     int eos;
     smb_tran2Packet_t *outp;   /* response packet */
-       char *tidPathp;
-       int align;
-       char shortName[13];             /* 8.3 name if needed */
-       int NeedShortName;
-       char *shortNameEnd;
+    char *tidPathp;
+    int align;
+    char shortName[13];                /* 8.3 name if needed */
+    int NeedShortName;
+    int foundInexact;
+    char *shortNameEnd;
     int fileType;
     cm_fid_t fid;
-
     cm_req_t req;
 
-       cm_InitReq(&req);
+    cm_InitReq(&req);
 
-       eos = 0;
-       if (p->opcode == 1) {
-               /* find first; obtain basic parameters from request */
+    eos = 0;
+    if (p->opcode == 1) {
+        /* find first; obtain basic parameters from request */
         attribute = p->parmsp[0];
         maxCount = p->parmsp[1];
         infoLevel = p->parmsp[3];
@@ -1944,19 +3621,24 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
         dsp = smb_NewDirSearch(1);
         dsp->attribute = attribute;
         pathp = ((char *) p->parmsp) + 12;     /* points to path */
+        if (smb_StoreAnsiFilenames)
+            OemToChar(pathp,pathp);
         nextCookie = 0;
         maskp = strrchr(pathp, '\\');
-        if (maskp == NULL) maskp = pathp;
-               else maskp++;   /* skip over backslash */
+        if (maskp == NULL) 
+            maskp = pathp;
+        else 
+            maskp++;   /* skip over backslash */
         strcpy(dsp->mask, maskp);      /* and save mask */
-               /* track if this is likely to match a lot of entries */
+        /* track if this is likely to match a lot of entries */
         starPattern = smb_V3IsStarMask(maskp);
-        }
+    }
     else {
-               osi_assert(p->opcode == 2);
+        osi_assert(p->opcode == 2);
         /* find next; obtain basic parameters from request or open dir file */
         dsp = smb_FindDirSearch(p->parmsp[0]);
-        if (!dsp) return CM_ERROR_BADFD;
+        if (!dsp) 
+            return CM_ERROR_BADFD;
         attribute = dsp->attribute;
         maxCount = p->parmsp[1];
         infoLevel = p->parmsp[2];
@@ -1964,25 +3646,25 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
         pathp = NULL;
         nextCookie = p->parmsp[3] | (p->parmsp[4] << 16);
         maskp = dsp->mask;
-               starPattern = 1;        /* assume, since required a Find Next */
+        starPattern = 1;       /* assume, since required a Find Next */
     }
 
-       osi_Log4(smb_logp,
+    osi_Log4(smb_logp,
               "T2 search dir attr 0x%x, info level %d, max count %d, flags 0x%x",
               attribute, infoLevel, maxCount, searchFlags);
 
-       osi_Log2(smb_logp, "...T2 search op %d, nextCookie 0x%x",
+    osi_Log2(smb_logp, "...T2 search op %d, nextCookie 0x%x",
               p->opcode, nextCookie);
 
-       if (infoLevel >= 0x101)
-               searchFlags &= ~4;      /* no resume keys */
+    if (infoLevel >= 0x101)
+        searchFlags &= ~4;     /* no resume keys */
 
     dirListPatchesp = NULL;
 
-       maxReturnData = p->maxReturnData;
+    maxReturnData = p->maxReturnData;
     if (p->opcode == 1)        /* find first */
         maxReturnParms = 10;   /* bytes */
-       else    
+    else    
         maxReturnParms = 8;    /* bytes */
 
 #ifndef CM_CONFIG_MULTITRAN2RESPONSES
@@ -1990,7 +3672,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
         maxReturnData = 6000;
 #endif /* CM_CONFIG_MULTITRAN2RESPONSES */
 
-       outp = smb_GetTran2ResponsePacket(vcp, p, opx, maxReturnParms,
+    outp = smb_GetTran2ResponsePacket(vcp, p, opx, maxReturnParms,
                                       maxReturnData);
 
     osi_Log1(smb_logp, "T2 receive search dir %s",
@@ -2003,10 +3685,10 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
         return CM_ERROR_BADSMB;
     }
         
-       osi_Log2(smb_logp, "T2 dir search cookie 0x%x, connection %d",
+    osi_Log2(smb_logp, "T2 dir search cookie 0x%x, connection %d",
              nextCookie, dsp->cookie);
 
-       userp = smb_GetTran2User(vcp, p);
+    userp = smb_GetTran2User(vcp, p);
     if (!userp) {
        osi_Log1(smb_logp, "T2 dir search unable to resolve user [%d]", p->uid);
        smb_ReleaseDirSearch(dsp);
@@ -2014,96 +3696,105 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
        return CM_ERROR_BADSMB;
     }
 
-       /* try to get the vnode for the path name next */
-       lock_ObtainMutex(&dsp->mx);
-       if (dsp->scp) {
-               scp = dsp->scp;
+    /* try to get the vnode for the path name next */
+    lock_ObtainMutex(&dsp->mx);
+    if (dsp->scp) {
+        scp = dsp->scp;
         cm_HoldSCache(scp);
         code = 0;
     }
     else {
-               spacep = cm_GetSpace();
+        spacep = cm_GetSpace();
         smb_StripLastComponent(spacep->data, NULL, pathp);
-        lock_ReleaseMutex(&dsp->mx);
-
-               tidPathp = smb_GetTIDPath(vcp, p->tid);
+        code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
+        if (code) {
+            lock_ReleaseMutex(&dsp->mx);
+            cm_ReleaseUser(userp);
+            smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOFILES);
+            smb_FreeTran2Packet(outp);
+            smb_DeleteDirSearch(dsp);
+            smb_ReleaseDirSearch(dsp);
+            return 0;
+        }
         code = cm_NameI(cm_rootSCachep, spacep->data,
                         CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
                         userp, tidPathp, &req, &scp);
         cm_FreeSpace(spacep);
 
-        lock_ObtainMutex(&dsp->mx);
-               if (code == 0) {
-            if (dsp->scp != 0) cm_ReleaseSCache(dsp->scp);
-                       dsp->scp = scp;
-                       /* we need one hold for the entry we just stored into,
+        if (code == 0) {
+            if (dsp->scp != 0) 
+                cm_ReleaseSCache(dsp->scp);
+            dsp->scp = scp;
+            /* we need one hold for the entry we just stored into,
              * and one for our own processing.  When we're done
-                        * with this function, we'll drop the one for our own
-                        * processing.  We held it once from the namei call,
-                        * and so we do another hold now.
+             * with this function, we'll drop the one for our own
+             * processing.  We held it once from the namei call,
+             * and so we do another hold now.
              */
             cm_HoldSCache(scp);
-                       lock_ObtainMutex(&scp->mx);
-                       if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
-                           && LargeIntegerGreaterOrEqualToZero(scp->bulkStatProgress)) {
+            lock_ObtainMutex(&scp->mx);
+            if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0 &&
+                 LargeIntegerGreaterOrEqualToZero(scp->bulkStatProgress)) {
                 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
-                               dsp->flags |= SMB_DIRSEARCH_BULKST;
-                       }
-                       lock_ReleaseMutex(&scp->mx);
-        }
+                dsp->flags |= SMB_DIRSEARCH_BULKST;
+            }
+            lock_ReleaseMutex(&scp->mx);
+        }       
     }
-       lock_ReleaseMutex(&dsp->mx);
+    lock_ReleaseMutex(&dsp->mx);
     if (code) {
-               cm_ReleaseUser(userp);
+        cm_ReleaseUser(userp);
         smb_FreeTran2Packet(outp);
-               smb_DeleteDirSearch(dsp);
-               smb_ReleaseDirSearch(dsp);
+        smb_DeleteDirSearch(dsp);
+        smb_ReleaseDirSearch(dsp);
         return code;
-        }
+    }
 
     /* get the directory size */
-       lock_ObtainMutex(&scp->mx);
+    lock_ObtainMutex(&scp->mx);
     code = cm_SyncOp(scp, NULL, userp, &req, 0,
                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-       if (code) {
-               lock_ReleaseMutex(&scp->mx);
+    if (code) {
+        lock_ReleaseMutex(&scp->mx);
         cm_ReleaseSCache(scp);
         cm_ReleaseUser(userp);
         smb_FreeTran2Packet(outp);
-               smb_DeleteDirSearch(dsp);
-               smb_ReleaseDirSearch(dsp);
+        smb_DeleteDirSearch(dsp);
+        smb_ReleaseDirSearch(dsp);
         return code;
     }
 
+  startsearch:
     dirLength = scp->length;
     bufferp = NULL;
     bufferOffset.LowPart = bufferOffset.HighPart = 0;
     curOffset.HighPart = 0;
     curOffset.LowPart = nextCookie;
-       origOp = outp->datap;
+    origOp = outp->datap;
 
+    foundInexact = 0;
     code = 0;
     returnedNames = 0;
     bytesInBuffer = 0;
     while (1) {
-               op = origOp;
-               if (searchFlags & 4)
-                       /* skip over resume key */
-                       op += 4;
+        op = origOp;
+        if (searchFlags & 4)
+            /* skip over resume key */
+            op += 4;
 
-               /* make sure that curOffset.LowPart doesn't point to the first
+        /* make sure that curOffset.LowPart doesn't point to the first
          * 32 bytes in the 2nd through last dir page, and that it doesn't
          * point at the first 13 32-byte chunks in the first dir page,
          * since those are dir and page headers, and don't contain useful
          * information.
          */
-               temp = curOffset.LowPart & (2048-1);
+        temp = curOffset.LowPart & (2048-1);
         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
-                       /* we're in the first page */
+            /* we're in the first page */
             if (temp < 13*32) temp = 13*32;
-               }
-               else {
-                       /* we're in a later dir page */
+        }
+        else {
+            /* we're in a later dir page */
             if (temp < 32) temp = 32;
         }
                
@@ -2136,47 +3827,52 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
         thyper.HighPart = curOffset.HighPart;
         thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
-                       /* wrong buffer */
+            /* wrong buffer */
             if (bufferp) {
                 buf_Release(bufferp);
                 bufferp = NULL;
-                       }       
-                       lock_ReleaseMutex(&scp->mx);
-                       lock_ObtainRead(&scp->bufCreateLock);
+            }       
+            lock_ReleaseMutex(&scp->mx);
+            lock_ObtainRead(&scp->bufCreateLock);
             code = buf_Get(scp, &thyper, &bufferp);
-                       lock_ReleaseRead(&scp->bufCreateLock);
+            lock_ReleaseRead(&scp->bufCreateLock);
+            lock_ObtainMutex(&dsp->mx);
 
-                       /* now, if we're doing a star match, do bulk fetching
-                        * of all of the status info for files in the dir.
+            /* now, if we're doing a star match, do bulk fetching
+             * of all of the status info for files in the dir.
              */
             if (starPattern) {
-                               smb_ApplyV3DirListPatches(scp, &dirListPatchesp,
-                                          infoLevel, userp,
-                                          &req);
-                               if ((dsp->flags & SMB_DIRSEARCH_BULKST)
-                    && LargeIntegerGreaterThanOrEqualTo(thyper, scp->bulkStatProgress)) {
-                                       /* Don't bulk stat if risking timeout */
-                                       int now = GetCurrentTime();
-                                       if (now - req.startTime > 5000) {
-                                               scp->bulkStatProgress = thyper;
-                                               scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
-                                               dsp->flags &= ~SMB_DIRSEARCH_BULKST;
-                                       } else
+                smb_ApplyV3DirListPatches(scp, &dirListPatchesp,
+                                           infoLevel, userp,
+                                           &req);
+                lock_ObtainMutex(&scp->mx);
+                if ((dsp->flags & SMB_DIRSEARCH_BULKST) &&
+                    LargeIntegerGreaterThanOrEqualTo(thyper, scp->bulkStatProgress)) {
+                    /* Don't bulk stat if risking timeout */
+                    int now = GetCurrentTime();
+                    if (now - req.startTime > 5000) {
+                        scp->bulkStatProgress = thyper;
+                        scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
+                        dsp->flags &= ~SMB_DIRSEARCH_BULKST;
+                    } else
                         cm_TryBulkStat(scp, &thyper, userp, &req);
-                               }
-                       }
+                }
+            } else {
+                lock_ObtainMutex(&scp->mx);
+            }
+            lock_ReleaseMutex(&dsp->mx);
+            if (code) 
+                break;
 
-            lock_ObtainMutex(&scp->mx);
-            if (code) break;
             bufferOffset = thyper;
 
             /* now get the data in the cache */
             while (1) {
-                               code = cm_SyncOp(scp, bufferp, userp, &req,
+                code = cm_SyncOp(scp, bufferp, userp, &req,
                                  PRSFS_LOOKUP,
                                  CM_SCACHESYNC_NEEDCALLBACK
                                  | CM_SCACHESYNC_READ);
-                               if (code) break;
+                if (code) break;
                                 
                 if (cm_HaveBuffer(scp, bufferp, 0)) break;
 
@@ -2186,68 +3882,70 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
                 if (code) break;
             }
             if (code) {
-                               buf_Release(bufferp);
+                buf_Release(bufferp);
                 bufferp = NULL;
                 break;
-                       }
+            }
         }      /* if (wrong buffer) ... */
                 
         /* now we have the buffer containing the entry we're interested
          * in; copy it out if it represents a non-deleted entry.
          */
-               entryInDir = curOffset.LowPart & (2048-1);
+        entryInDir = curOffset.LowPart & (2048-1);
         entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
 
-               /* page header will help tell us which entries are free.  Page
-                * header can change more often than once per buffer, since
-                * AFS 3 dir page size may be less than (but not more than)
-                * a buffer package buffer.
+        /* page header will help tell us which entries are free.  Page
+         * header can change more often than once per buffer, since
+         * AFS 3 dir page size may be less than (but not more than)
+         * a buffer package buffer.
          */
-               /* only look intra-buffer */
-               temp = curOffset.LowPart & (buf_bufferSize - 1);
+        /* only look intra-buffer */
+        temp = curOffset.LowPart & (buf_bufferSize - 1);
         temp &= ~(2048 - 1);   /* turn off intra-page bits */
-               pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
+        pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
 
-               /* now determine which entry we're looking at in the page.
-                * If it is free (there's a free bitmap at the start of the
-                * dir), we should skip these 32 bytes.
+        /* now determine which entry we're looking at in the page.
+         * If it is free (there's a free bitmap at the start of the
+         * dir), we should skip these 32 bytes.
          */
         slotInPage = (entryInDir & 0x7e0) >> 5;
-        if (!(pageHeaderp->freeBitmap[slotInPage>>3]
-               & (1 << (slotInPage & 0x7)))) {
-                       /* this entry is free */
+        if (!(pageHeaderp->freeBitmap[slotInPage>>3] &
+            (1 << (slotInPage & 0x7)))) {
+            /* this entry is free */
             numDirChunks = 1;  /* only skip this guy */
             goto nextEntry;
         }
 
-               tp = bufferp->datap + entryInBuffer;
+        tp = bufferp->datap + entryInBuffer;
         dep = (cm_dirEntry_t *) tp;    /* now points to AFS3 dir entry */
 
         /* while we're here, compute the next entry's location, too,
-                * since we'll need it when writing out the cookie into the dir
-                * listing stream.
+         * since we'll need it when writing out the cookie into the dir
+         * listing stream.
          *
          * XXXX Probably should do more sanity checking.
          */
-               numDirChunks = cm_NameEntries(dep->name, &onbytes);
+        numDirChunks = cm_NameEntries(dep->name, &onbytes);
                
         /* compute offset of cookie representing next entry */
         nextEntryCookie = curOffset.LowPart + (CM_DIR_CHUNKSIZE * numDirChunks);
 
-               /* Need 8.3 name? */
-               NeedShortName = 0;
-               if (infoLevel == 0x104
-                   && dep->fid.vnode != 0
-                   && !cm_Is8Dot3(dep->name)) {
-                       cm_Gen8Dot3Name(dep, shortName, &shortNameEnd);
-                       NeedShortName = 1;
-               }
-
-        if (dep->fid.vnode != 0
-             && (smb_V3MatchMask(dep->name, maskp, CM_FLAG_CASEFOLD)
-                 || (NeedShortName
-                      && smb_V3MatchMask(shortName, maskp,
-                                          CM_FLAG_CASEFOLD)))) {
+        /* Need 8.3 name? */
+        NeedShortName = 0;
+        if (infoLevel == SMB_QUERY_FILE_NAME_INFO
+             && dep->fid.vnode != 0
+             && !cm_Is8Dot3(dep->name)) {
+            cm_Gen8Dot3Name(dep, shortName, &shortNameEnd);
+            NeedShortName = 1;
+        }
+
+        /* When matching, we are using doing a case fold if we have a wildcard mask.
+         * If we get a non-wildcard match, it's a lookup for a specific file. 
+         */
+        if (dep->fid.vnode != 0 && 
+            (smb_V3MatchMask(dep->name, maskp, (starPattern? CM_FLAG_CASEFOLD : 0)) ||
+             (NeedShortName &&
+              smb_V3MatchMask(shortName, maskp, CM_FLAG_CASEFOLD)))) {
 
             /* Eliminate entries that don't match requested attributes */
             if (smb_hideDotFiles && !(dsp->attribute & SMB_ATTR_HIDDEN) && 
@@ -2269,150 +3967,172 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
                     goto nextEntry;
             }
 
-                       /* finally check if this name will fit */
+            /* finally check if this name will fit */
 
-                       /* standard dir entry stuff */
-                       if (infoLevel < 0x101)
-                               ohbytes = 23;   /* pre-NT */
-                       else if (infoLevel == 0x103)
-                               ohbytes = 12;   /* NT names only */
-                       else
-                               ohbytes = 64;   /* NT */
+            /* standard dir entry stuff */
+            if (infoLevel < 0x101)
+                ohbytes = 23;  /* pre-NT */
+            else if (infoLevel == SMB_QUERY_FILE_EA_INFO)
+                ohbytes = 12;  /* NT names only */
+            else
+                ohbytes = 64;  /* NT */
 
-                       if (infoLevel == 0x104)
-                               ohbytes += 26;  /* Short name & length */
+            if (infoLevel == SMB_QUERY_FILE_NAME_INFO)
+                ohbytes += 26; /* Short name & length */
 
             if (searchFlags & 4) {
                 ohbytes += 4;  /* if resume key required */
-                       }   
+            }   
 
             if (infoLevel != 1
                  && infoLevel != 0x101
                  && infoLevel != 0x103)
-                               ohbytes += 4;   /* EASIZE */
+                ohbytes += 4;  /* EASIZE */
 
-                       /* add header to name & term. null */
-                       orbytes = onbytes + ohbytes + 1;
+            /* add header to name & term. null */
+            orbytes = onbytes + ohbytes + 1;
 
-                       /* now, we round up the record to a 4 byte alignment,
-                        * and we make sure that we have enough room here for
-                        * even the aligned version (so we don't have to worry
-                        * about an * overflow when we pad things out below).
-                        * That's the reason for the alignment arithmetic below.
+            /* now, we round up the record to a 4 byte alignment,
+             * and we make sure that we have enough room here for
+             * even the aligned version (so we don't have to worry
+             * about an * overflow when we pad things out below).
+             * That's the reason for the alignment arithmetic below.
              */
-                       if (infoLevel >= 0x101)
-                               align = (4 - (orbytes & 3)) & 3;
-                       else
-                               align = 0;
-                       if (orbytes + bytesInBuffer + align > maxReturnData)
+            if (infoLevel >= 0x101)
+                align = (4 - (orbytes & 3)) & 3;
+            else
+                align = 0;
+            if (orbytes + bytesInBuffer + align > maxReturnData)
                 break;
 
-                       /* this is one of the entries to use: it is not deleted
-                        * and it matches the star pattern we're looking for.
-                        * Put out the name, preceded by its length.
+            /* this is one of the entries to use: it is not deleted
+             * and it matches the star pattern we're looking for.
+             * Put out the name, preceded by its length.
              */
-                       /* First zero everything else */
-                       memset(origOp, 0, ohbytes);
+            /* First zero everything else */
+            memset(origOp, 0, ohbytes);
 
-                       if (infoLevel <= 0x101)
+            if (infoLevel <= 0x101)
                 *(origOp + ohbytes - 1) = (unsigned char) onbytes;
-                       else if (infoLevel == 0x103)
-                               *((u_long *)(op + 8)) = onbytes;
-                       else
-                               *((u_long *)(op + 60)) = onbytes;
+            else if (infoLevel == SMB_QUERY_FILE_EA_INFO)
+                *((u_long *)(op + 8)) = onbytes;
+            else
+                *((u_long *)(op + 60)) = onbytes;
             strcpy(origOp+ohbytes, dep->name);
-
-                       /* Short name if requested and needed */
-            if (infoLevel == 0x104) {
-                               if (NeedShortName) {
-                                       strcpy(op + 70, shortName);
-                                       *(op + 68) = shortNameEnd - shortName;
-                               }
-                       }
+            if (smb_StoreAnsiFilenames)
+                CharToOem(origOp+ohbytes, origOp+ohbytes);
+
+            /* Short name if requested and needed */
+            if (infoLevel == SMB_QUERY_FILE_NAME_INFO) {
+                if (NeedShortName) {
+                    strcpy(op + 70, shortName);
+                    if (smb_StoreAnsiFilenames)
+                        CharToOem(op + 70, op + 70);
+                    *(op + 68) = shortNameEnd - shortName;
+                }
+            }
 
             /* now, adjust the # of entries copied */
             returnedNames++;
 
-                       /* NextEntryOffset and FileIndex */
-                       if (infoLevel >= 101) {
-                               int entryOffset = orbytes + align;
-                               *((u_long *)op) = entryOffset;
-                               *((u_long *)(op+4)) = nextEntryCookie;
-                       }
+            /* NextEntryOffset and FileIndex */
+            if (infoLevel >= 101) {
+                int entryOffset = orbytes + align;
+                *((u_long *)op) = entryOffset;
+                *((u_long *)(op+4)) = nextEntryCookie;
+            }
 
             /* now we emit the attribute.  This is tricky, since
              * we need to really stat the file to find out what
-                        * type of entry we've got.  Right now, we're copying
-                        * out data from * a buffer, while holding the scp
-                        * locked, so it isn't really convenient to stat
-                        * something now.  We'll put in a place holder
+             * type of entry we've got.  Right now, we're copying
+             * out data from a buffer, while holding the scp
+             * locked, so it isn't really convenient to stat
+             * something now.  We'll put in a place holder
              * now, and make a second pass before returning this
-                        * to get the real attributes.  So, we just skip the
-                        * data for now, and adjust it later.  We allocate a
-                        * patch record to make it easy to find this point
-                        * later.  The replay will happen at a time when it is
-                        * safe to unlock the directory.
+             * to get the real attributes.  So, we just skip the
+             * data for now, and adjust it later.  We allocate a
+             * patch record to make it easy to find this point
+             * later.  The replay will happen at a time when it is
+             * safe to unlock the directory.
              */
-                       if (infoLevel != 0x103) {
-                               curPatchp = malloc(sizeof(*curPatchp));
+            if (infoLevel != 0x103) {
+                curPatchp = malloc(sizeof(*curPatchp));
                 osi_QAdd((osi_queue_t **) &dirListPatchesp,
                           &curPatchp->q);
-                               curPatchp->dptr = op;
-                               if (infoLevel >= 0x101)
-                                       curPatchp->dptr += 8;
+                curPatchp->dptr = op;
+                if (infoLevel >= 0x101)
+                    curPatchp->dptr += 8;
 
                 if (smb_hideDotFiles && smb_IsDotFile(dep->name)) {
                     curPatchp->flags = SMB_DIRLISTPATCH_DOTFILE;
-                }
-                else
+                }       
+                else    
                     curPatchp->flags = 0;
 
-                               curPatchp->fid.cell = scp->fid.cell;
-                               curPatchp->fid.volume = scp->fid.volume;
-                               curPatchp->fid.vnode = ntohl(dep->fid.vnode);
-                               curPatchp->fid.unique = ntohl(dep->fid.unique);
+                curPatchp->fid.cell = scp->fid.cell;
+                curPatchp->fid.volume = scp->fid.volume;
+                curPatchp->fid.vnode = ntohl(dep->fid.vnode);
+                curPatchp->fid.unique = ntohl(dep->fid.unique);
 
                 /* temp */
                 curPatchp->dep = dep;
-                       }   
+            }   
 
-                       if (searchFlags & 4)
-                               /* put out resume key */
-                               *((u_long *)origOp) = nextEntryCookie;
+            if (searchFlags & 4)
+                /* put out resume key */
+                *((u_long *)origOp) = nextEntryCookie;
 
-                       /* Adjust byte ptr and count */
-                       origOp += orbytes;      /* skip entire record */
+            /* Adjust byte ptr and count */
+            origOp += orbytes; /* skip entire record */
             bytesInBuffer += orbytes;
 
-                       /* and pad the record out */
+            /* and pad the record out */
             while (--align >= 0) {
-                               *origOp++ = 0;
+                *origOp++ = 0;
                 bytesInBuffer++;
             }
-
-               }       /* if we're including this name */
+        }      /* if we're including this name */
+        else if (!NeedShortName &&
+                 !starPattern &&
+                 !foundInexact &&
+                 dep->fid.vnode != 0 &&
+                 smb_V3MatchMask(dep->name, maskp, CM_FLAG_CASEFOLD)) {
+            /* We were looking for exact matches, but here's an inexact one*/
+            foundInexact = 1;
+        }
                 
       nextEntry:
         /* and adjust curOffset to be where the new cookie is */
-               thyper.HighPart = 0;
+        thyper.HighPart = 0;
         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
         curOffset = LargeIntegerAdd(thyper, curOffset);
-    }          /* while copying data for dir listing */
+    } /* while copying data for dir listing */
 
-       /* release the mutex */
-       lock_ReleaseMutex(&scp->mx);
+    /* If we didn't get a star pattern, we did an exact match during the first pass. 
+     * If there were no exact matches found, we fail over to inexact matches by
+     * marking the query as a star pattern (matches all case permutations), and
+     * re-running the query. 
+     */
+    if (returnedNames == 0 && !starPattern && foundInexact) {
+        osi_Log0(afsd_logp,"T2 Search: No exact matches. Re-running for inexact matches");
+        starPattern = 1;
+        goto startsearch;
+    }
+
+    /* release the mutex */
+    lock_ReleaseMutex(&scp->mx);
     if (bufferp) buf_Release(bufferp);
 
-       /* apply and free last set of patches; if not doing a star match, this
-        * will be empty, but better safe (and freeing everything) than sorry.
+    /* apply and free last set of patches; if not doing a star match, this
+     * will be empty, but better safe (and freeing everything) than sorry.
      */
     smb_ApplyV3DirListPatches(scp, &dirListPatchesp, infoLevel, userp,
                               &req);
         
     /* now put out the final parameters */
-       if (returnedNames == 0) eos = 1;
+    if (returnedNames == 0) eos = 1;
     if (p->opcode == 1) {
-               /* find first */
+        /* find first */
         outp->parmsp[0] = (unsigned short) dsp->cookie;
         outp->parmsp[1] = returnedNames;
         outp->parmsp[2] = eos;
@@ -2434,30 +4154,30 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
         outp->totalParms = 8;  /* in bytes */
     }   
 
-       /* return # of bytes in the buffer */
+    /* return # of bytes in the buffer */
     outp->totalData = bytesInBuffer;
 
-       osi_Log2(smb_logp, "T2 search dir done, %d names, code %d",
-              returnedNames, code);
+    osi_Log2(smb_logp, "T2 search dir done, %d names, code %d",
+             returnedNames, code);
 
-       /* Return error code if unsuccessful on first request */
-       if (code == 0 && p->opcode == 1 && returnedNames == 0)
-               code = CM_ERROR_NOSUCHFILE;
+    /* Return error code if unsuccessful on first request */
+    if (code == 0 && p->opcode == 1 && returnedNames == 0)
+        code = CM_ERROR_NOSUCHFILE;
 
-       /* if we're supposed to close the search after this request, or if
+    /* if we're supposed to close the search after this request, or if
      * we're supposed to close the search if we're done, and we're done,
      * or if something went wrong, close the search.
      */
     /* ((searchFlags & 1) || ((searchFlags & 2) && eos) */
-       if ((searchFlags & 1) || (returnedNames == 0) || 
+    if ((searchFlags & 1) || (returnedNames == 0) || 
          ((searchFlags & 2) && eos) || code != 0)
-           smb_DeleteDirSearch(dsp);
-       if (code)
+        smb_DeleteDirSearch(dsp);
+    if (code)
         smb_SendTran2Error(vcp, p, opx, code);
-       else {
+    else {
         smb_SendTran2Packet(vcp, outp, opx);
-       }
-       smb_FreeTran2Packet(outp);
+    }
+    smb_FreeTran2Packet(outp);
     smb_ReleaseDirSearch(dsp);
     cm_ReleaseSCache(scp);
     cm_ReleaseUser(userp);
@@ -2476,29 +4196,29 @@ long smb_ReceiveV3FindClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp
     dsp = smb_FindDirSearch(dirHandle);
         
     if (!dsp)
-               return CM_ERROR_BADFD;
+        return CM_ERROR_BADFD;
        
     /* otherwise, we have an FD to destroy */
     smb_DeleteDirSearch(dsp);
     smb_ReleaseDirSearch(dsp);
         
-       /* and return results */
-       smb_SetSMBDataLength(outp, 0);
+    /* and return results */
+    smb_SetSMBDataLength(outp, 0);
 
     return 0;
 }
 
 long smb_ReceiveV3FindNotifyClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       smb_SetSMBDataLength(outp, 0);
+    smb_SetSMBDataLength(outp, 0);
     return 0;
 }
 
 long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       char *pathp;
+    char *pathp;
     long code = 0;
-       cm_space_t *spacep;
+    cm_space_t *spacep;
     int excl;
     cm_user_t *userp;
     cm_scache_t *dscp;         /* dir we're dealing with */
@@ -2508,25 +4228,25 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     smb_fid_t *fidp;
     int attributes;
     char *lastNamep;
-    long dosTime;
+    time_t dosTime;
     int openFun;
     int trunc;
     int openMode;
     int extraInfo;
     int openAction;
     int parmSlot;                      /* which parm we're dealing with */
-       char *tidPathp;
-       cm_req_t req;
+    char *tidPathp;
+    cm_req_t req;
 
-       cm_InitReq(&req);
+    cm_InitReq(&req);
 
     scp = NULL;
         
-       extraInfo = (smb_GetSMBParm(inp, 2) & 1);       /* return extra info */
-       openFun = smb_GetSMBParm(inp, 8);       /* open function */
+    extraInfo = (smb_GetSMBParm(inp, 2) & 1);  /* return extra info */
+    openFun = smb_GetSMBParm(inp, 8);  /* open function */
     excl = ((openFun & 3) == 0);
     trunc = ((openFun & 3) == 2);              /* truncate it */
-       openMode = (smb_GetSMBParm(inp, 3) & 0x7);
+    openMode = (smb_GetSMBParm(inp, 3) & 0x7);
     openAction = 0;                    /* tracks what we did */
 
     attributes = smb_GetSMBParm(inp, 5);
@@ -2537,12 +4257,14 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     if (attributes & 1) initialModeBits &= ~0222;
         
     pathp = smb_GetSMBData(inp, NULL);
+    if (smb_StoreAnsiFilenames)
+        OemToChar(pathp,pathp);
 
-       spacep = inp->spacep;
+    spacep = inp->spacep;
     smb_StripLastComponent(spacep->data, &lastNamep, pathp);
 
-       if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
-               /* special case magic file name for receiving IOCTL requests
+    if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
+        /* special case magic file name for receiving IOCTL requests
          * (since IOCTL calls themselves aren't getting through).
          */
 #ifdef NOTSERVICE
@@ -2552,13 +4274,13 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
         smb_SetupIoctlFid(fidp, spacep);
 
-               /* set inp->fid so that later read calls in same msg can find fid */
+        /* set inp->fid so that later read calls in same msg can find fid */
         inp->fid = fidp->fid;
         
         /* copy out remainder of the parms */
-               parmSlot = 2;
-               smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
-               if (extraInfo) {
+        parmSlot = 2;
+        smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
+        if (extraInfo) {
             smb_SetSMBParm(outp, parmSlot, /* attrs */ 0); parmSlot++;
             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;     /* mod time */
             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
@@ -2567,16 +4289,16 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
             smb_SetSMBParm(outp, parmSlot, openMode); parmSlot++;
             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* file type 0 ==> normal file or dir */
             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* IPC junk */
-               }   
-               /* and the final "always present" stuff */
+        }   
+        /* and the final "always present" stuff */
         smb_SetSMBParm(outp, parmSlot, /* openAction found existing file */ 1); parmSlot++;
-               /* next write out the "unique" ID */
-               smb_SetSMBParm(outp, parmSlot, 0x1234); parmSlot++;
-               smb_SetSMBParm(outp, parmSlot, 0x5678); parmSlot++;
+        /* next write out the "unique" ID */
+        smb_SetSMBParm(outp, parmSlot, 0x1234); parmSlot++;
+        smb_SetSMBParm(outp, parmSlot, 0x5678); parmSlot++;
         smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
         smb_SetSMBDataLength(outp, 0);
 
-               /* and clean up fid reference */
+        /* and clean up fid reference */
         smb_ReleaseFID(fidp);
         return 0;
     }
@@ -2592,13 +4314,17 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 #endif
     userp = smb_GetUser(vcp, inp);
 
-       dscp = NULL;
-       tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
-       code = cm_NameI(cm_rootSCachep, pathp,
+    dscp = NULL;
+    code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
+    if (code) {
+        cm_ReleaseUser(userp);
+        return CM_ERROR_NOSUCHPATH;
+    }
+    code = cm_NameI(cm_rootSCachep, pathp,
                     CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
                     userp, tidPathp, &req, &scp);
-       if (code != 0) {
-               code = cm_NameI(cm_rootSCachep, spacep->data,
+    if (code != 0) {
+        code = cm_NameI(cm_rootSCachep, spacep->data,
                         CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
                         userp, tidPathp, &req, &dscp);
 
@@ -2620,14 +4346,14 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
             cm_ReleaseUser(userp);
             return code;
         }
-       }
+    }
         
     /* if we get here, if code is 0, the file exists and is represented by
      * scp.  Otherwise, we have to create it.  The dir may be represented
      * by dscp, or we may have found the file directly.  If code is non-zero,
      * scp is NULL.
      */
-       if (code == 0) {
+    if (code == 0) {
         code = cm_CheckOpen(scp, openMode, trunc, userp, &req);
         if (code) {
             if (dscp) cm_ReleaseSCache(dscp);
@@ -2636,105 +4362,105 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
             return code;
         }
 
-               if (excl) {
-                       /* oops, file shouldn't be there */
+        if (excl) {
+            /* oops, file shouldn't be there */
             if (dscp) cm_ReleaseSCache(dscp);
             cm_ReleaseSCache(scp);
             cm_ReleaseUser(userp);
             return CM_ERROR_EXISTS;
         }
 
-               if (trunc) {
-                       setAttr.mask = CM_ATTRMASK_LENGTH;
+        if (trunc) {
+            setAttr.mask = CM_ATTRMASK_LENGTH;
             setAttr.length.LowPart = 0;
             setAttr.length.HighPart = 0;
-                       code = cm_SetAttr(scp, &setAttr, userp, &req);
+            code = cm_SetAttr(scp, &setAttr, userp, &req);
             openAction = 3;    /* truncated existing file */
-               }
+        }
         else openAction = 1;   /* found existing file */
     }
-       else if (!(openFun & 0x10)) {
-               /* don't create if not found */
+    else if (!(openFun & 0x10)) {
+        /* don't create if not found */
         if (dscp) cm_ReleaseSCache(dscp);
         cm_ReleaseUser(userp);
         return CM_ERROR_NOSUCHFILE;
     }
     else {
-               osi_assert(dscp != NULL);
-               osi_Log1(smb_logp, "smb_ReceiveV3OpenX creating file %s",
+        osi_assert(dscp != NULL);
+        osi_Log1(smb_logp, "smb_ReceiveV3OpenX creating file %s",
                  osi_LogSaveString(smb_logp, lastNamep));
-               openAction = 2; /* created file */
-               setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
-               smb_UnixTimeFromDosUTime(&setAttr.clientModTime, dosTime);
+        openAction = 2;        /* created file */
+        setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
+        smb_UnixTimeFromDosUTime(&setAttr.clientModTime, dosTime);
         code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
                          &req);
-               if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-                       smb_NotifyChange(FILE_ACTION_ADDED,
+        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
+            smb_NotifyChange(FILE_ACTION_ADDED,
                              FILE_NOTIFY_CHANGE_FILE_NAME,
                              dscp, lastNamep, NULL, TRUE);
         if (!excl && code == CM_ERROR_EXISTS) {
-                       /* not an exclusive create, and someone else tried
-                        * creating it already, then we open it anyway.  We
-                        * don't bother retrying after this, since if this next
-                        * fails, that means that the file was deleted after we
-                        * started this call.
+            /* not an exclusive create, and someone else tried
+             * creating it already, then we open it anyway.  We
+             * don't bother retrying after this, since if this next
+             * fails, that means that the file was deleted after we
+             * started this call.
              */
             code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
                              userp, &req, &scp);
             if (code == 0) {
                 if (trunc) {
-                                       setAttr.mask = CM_ATTRMASK_LENGTH;
+                    setAttr.mask = CM_ATTRMASK_LENGTH;
                     setAttr.length.LowPart = 0;
                     setAttr.length.HighPart = 0;
                     code = cm_SetAttr(scp, &setAttr, userp, &req);
                 }   
-                       }       /* lookup succeeded */
+            }  /* lookup succeeded */
         }
     }
         
-       /* we don't need this any longer */
-       if (dscp) cm_ReleaseSCache(dscp);
+    /* we don't need this any longer */
+    if (dscp) cm_ReleaseSCache(dscp);
 
     if (code) {
-               /* something went wrong creating or truncating the file */
+        /* something went wrong creating or truncating the file */
         if (scp) cm_ReleaseSCache(scp);
         cm_ReleaseUser(userp);
         return code;
     }
         
-       /* make sure we're about to open a file */
-       if (scp->fileType != CM_SCACHETYPE_FILE) {
-               cm_ReleaseSCache(scp);
-               cm_ReleaseUser(userp);
-               return CM_ERROR_ISDIR;
-       }
+    /* make sure we're about to open a file */
+    if (scp->fileType != CM_SCACHETYPE_FILE) {
+        cm_ReleaseSCache(scp);
+        cm_ReleaseUser(userp);
+        return CM_ERROR_ISDIR;
+    }
 
     /* now all we have to do is open the file itself */
     fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
     osi_assert(fidp);
        
-       /* save a pointer to the vnode */
+    /* save a pointer to the vnode */
     fidp->scp = scp;
         
-       /* compute open mode */
+    /* compute open mode */
     if (openMode != 1) fidp->flags |= SMB_FID_OPENREAD;
     if (openMode == 1 || openMode == 2)
         fidp->flags |= SMB_FID_OPENWRITE;
 
-       smb_ReleaseFID(fidp);
+    smb_ReleaseFID(fidp);
         
-       cm_Open(scp, 0, userp);
+    cm_Open(scp, 0, userp);
 
-       /* set inp->fid so that later read calls in same msg can find fid */
+    /* set inp->fid so that later read calls in same msg can find fid */
     inp->fid = fidp->fid;
         
     /* copy out remainder of the parms */
-       parmSlot = 2;
-       smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
-       lock_ObtainMutex(&scp->mx);
-       if (extraInfo) {
+    parmSlot = 2;
+    smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
+    lock_ObtainMutex(&scp->mx);
+    if (extraInfo) {
         smb_SetSMBParm(outp, parmSlot, smb_Attributes(scp)); parmSlot++;
-               smb_DosUTimeFromUnixTime(&dosTime, scp->clientModTime);
+        smb_DosUTimeFromUnixTime(&dosTime, scp->clientModTime);
         smb_SetSMBParm(outp, parmSlot, dosTime & 0xffff); parmSlot++;
         smb_SetSMBParm(outp, parmSlot, (dosTime>>16) & 0xffff); parmSlot++;
         smb_SetSMBParm(outp, parmSlot, scp->length.LowPart & 0xffff); parmSlot++;
@@ -2742,59 +4468,59 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         smb_SetSMBParm(outp, parmSlot, openMode); parmSlot++;
         smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* file type 0 ==> normal file or dir */
         smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* IPC junk */
-       }
-       /* and the final "always present" stuff */
+    }
+    /* and the final "always present" stuff */
     smb_SetSMBParm(outp, parmSlot, openAction); parmSlot++;
-       /* next write out the "unique" ID */
-       smb_SetSMBParm(outp, parmSlot, scp->fid.vnode & 0xffff); parmSlot++;
-       smb_SetSMBParm(outp, parmSlot, scp->fid.volume & 0xffff); parmSlot++;
+    /* next write out the "unique" ID */
+    smb_SetSMBParm(outp, parmSlot, scp->fid.vnode & 0xffff); parmSlot++;
+    smb_SetSMBParm(outp, parmSlot, scp->fid.volume & 0xffff); parmSlot++;
     smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
-       lock_ReleaseMutex(&scp->mx);
+    lock_ReleaseMutex(&scp->mx);
     smb_SetSMBDataLength(outp, 0);
 
-       osi_Log1(smb_logp, "SMB OpenX opening fid %d", fidp->fid);
+    osi_Log1(smb_logp, "SMB OpenX opening fid %d", fidp->fid);
 
     cm_ReleaseUser(userp);
     /* leave scp held since we put it in fidp->scp */
     return 0;
-}   
+}       
 
 long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       cm_req_t req;
-       cm_user_t *userp;
-       unsigned short fid;
-       smb_fid_t *fidp;
-       cm_scache_t *scp;
-       unsigned char LockType;
-       unsigned short NumberOfUnlocks, NumberOfLocks;
-       unsigned long Timeout;
-       char *op;
-       LARGE_INTEGER LOffset, LLength;
-       smb_waitingLock_t *waitingLock;
-       void *lockp;
-       long code = 0;
-       int i;
-
-       cm_InitReq(&req);
-
-       fid = smb_GetSMBParm(inp, 2);
-       fid = smb_ChainFID(fid, inp);
-
-       fidp = smb_FindFID(vcp, fid, 0);
-       if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
-               return CM_ERROR_BADFD;
-       }
-       /* set inp->fid so that later read calls in same msg can find fid */
+    cm_req_t req;
+    cm_user_t *userp;
+    unsigned short fid;
+    smb_fid_t *fidp;
+    cm_scache_t *scp;
+    unsigned char LockType;
+    unsigned short NumberOfUnlocks, NumberOfLocks;
+    unsigned long Timeout;
+    char *op;
+    LARGE_INTEGER LOffset, LLength;
+    smb_waitingLock_t *waitingLock;
+    void *lockp;
+    long code = 0;
+    int i;
+
+    cm_InitReq(&req);
+
+    fid = smb_GetSMBParm(inp, 2);
+    fid = smb_ChainFID(fid, inp);
+
+    fidp = smb_FindFID(vcp, fid, 0);
+    if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
+        return CM_ERROR_BADFD;
+    }
+    /* set inp->fid so that later read calls in same msg can find fid */
     inp->fid = fid;
 
-       userp = smb_GetUser(vcp, inp);
+    userp = smb_GetUser(vcp, inp);
 
-       scp = fidp->scp;
+    scp = fidp->scp;
 
-       lock_ObtainMutex(&scp->mx);
-       code = cm_SyncOp(scp, NULL, userp, &req, 0,
-                        CM_SCACHESYNC_NEEDCALLBACK
+    lock_ObtainMutex(&scp->mx);
+    code = cm_SyncOp(scp, NULL, userp, &req, 0,
+                      CM_SCACHESYNC_NEEDCALLBACK
                         | CM_SCACHESYNC_GETSTATUS
                         | CM_SCACHESYNC_LOCK);
        if (code) goto doneSync;
@@ -2807,106 +4533,106 @@ long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
        op = smb_GetSMBData(inp, NULL);
 
        for (i=0; i<NumberOfUnlocks; i++) {
-               if (LockType & 0x10) {
-                       /* Large Files */
-                       LOffset.HighPart = *((LONG *)(op + 4));
-                       LOffset.LowPart = *((DWORD *)(op + 8));
-                       LLength.HighPart = *((LONG *)(op + 12));
-                       LLength.LowPart = *((DWORD *)(op + 16));
-                       op += 20;
-               }
-               else {
-                       /* Not Large Files */
-                       LOffset.HighPart = 0;
-                       LOffset.LowPart = *((DWORD *)(op + 2));
-                       LLength.HighPart = 0;
-                       LLength.LowPart = *((DWORD *)(op + 6));
-                       op += 10;
-               }
-               if (LargeIntegerNotEqualToZero(LOffset))
-                       continue;
-               /* Do not check length -- length check done in cm_Unlock */
-
-               code = cm_Unlock(scp, LockType, LOffset, LLength, userp, &req);
-               if (code) goto done;
-       }
+            if (LockType & 0x10) {
+                /* Large Files */
+                LOffset.HighPart = *((LONG *)(op + 4));
+                LOffset.LowPart = *((DWORD *)(op + 8));
+                LLength.HighPart = *((LONG *)(op + 12));
+                LLength.LowPart = *((DWORD *)(op + 16));
+                op += 20;
+            }
+            else {
+                /* Not Large Files */
+                LOffset.HighPart = 0;
+                LOffset.LowPart = *((DWORD *)(op + 2));
+                LLength.HighPart = 0;
+                LLength.LowPart = *((DWORD *)(op + 6));
+                op += 10;
+            }
+            if (LargeIntegerNotEqualToZero(LOffset))
+                continue;
+            /* Do not check length -- length check done in cm_Unlock */
+
+            code = cm_Unlock(scp, LockType, LOffset, LLength, userp, &req);
+            if (code) goto done;
+       }       
 
        for (i=0; i<NumberOfLocks; i++) {
-               if (LockType & 0x10) {
-                       /* Large Files */
-                       LOffset.HighPart = *((LONG *)(op + 4));
-                       LOffset.LowPart = *((DWORD *)(op + 8));
-                       LLength.HighPart = *((LONG *)(op + 12));
-                       LLength.LowPart = *((DWORD *)(op + 16));
-                       op += 20;
-               }
-               else {
-                       /* Not Large Files */
-                       LOffset.HighPart = 0;
-                       LOffset.LowPart = *((DWORD *)(op + 2));
-                       LLength.HighPart = 0;
-                       LLength.LowPart = *((DWORD *)(op + 6));
-                       op += 10;
-               }
-               if (LargeIntegerNotEqualToZero(LOffset))
-                       continue;
-               if (LargeIntegerLessThan(LOffset, scp->length))
-                       continue;
-
-               code = cm_Lock(scp, LockType, LOffset, LLength, Timeout,
-                               userp, &req, &lockp);
-               if (code == CM_ERROR_WOULDBLOCK && Timeout != 0) {
-                       /* Put on waiting list */
-                       waitingLock = malloc(sizeof(smb_waitingLock_t));
-                       waitingLock->vcp = vcp;
-                       waitingLock->inp = smb_CopyPacket(inp);
-                       waitingLock->outp = smb_CopyPacket(outp);
-                       waitingLock->timeRemaining = Timeout;
-                       waitingLock->lockp = lockp;
-                       lock_ObtainWrite(&smb_globalLock);
-                       osi_QAdd((osi_queue_t **)&smb_allWaitingLocks,
-                                &waitingLock->q);
-                       osi_Wakeup((long) &smb_allWaitingLocks);
-                       lock_ReleaseWrite(&smb_globalLock);
-                       /* don't send reply immediately */
-                       outp->flags |= SMB_PACKETFLAG_NOSEND;
-               }
-               if (code) break;
-       }
+            if (LockType & 0x10) {
+                /* Large Files */
+                LOffset.HighPart = *((LONG *)(op + 4));
+                LOffset.LowPart = *((DWORD *)(op + 8));
+                LLength.HighPart = *((LONG *)(op + 12));
+                LLength.LowPart = *((DWORD *)(op + 16));
+                op += 20;
+            }
+            else {
+                /* Not Large Files */
+                LOffset.HighPart = 0;
+                LOffset.LowPart = *((DWORD *)(op + 2));
+                LLength.HighPart = 0;
+                LLength.LowPart = *((DWORD *)(op + 6));
+                op += 10;
+            }
+            if (LargeIntegerNotEqualToZero(LOffset))
+                continue;
+            if (LargeIntegerLessThan(LOffset, scp->length))
+                continue;
+
+            code = cm_Lock(scp, LockType, LOffset, LLength, Timeout,
+                            userp, &req, &lockp);
+            if (code == CM_ERROR_WOULDBLOCK && Timeout != 0) {
+                /* Put on waiting list */
+                waitingLock = malloc(sizeof(smb_waitingLock_t));
+                waitingLock->vcp = vcp;
+                waitingLock->inp = smb_CopyPacket(inp);
+                waitingLock->outp = smb_CopyPacket(outp);
+                waitingLock->timeRemaining = Timeout;
+                waitingLock->lockp = lockp;
+                lock_ObtainWrite(&smb_globalLock);
+                osi_QAdd((osi_queue_t **)&smb_allWaitingLocks,
+                          &waitingLock->q);
+                osi_Wakeup((long) &smb_allWaitingLocks);
+                lock_ReleaseWrite(&smb_globalLock);
+                /* don't send reply immediately */
+                outp->flags |= SMB_PACKETFLAG_NOSEND;
+            }
+            if (code) break;
+       }       
 
-       if (code) {
-               /* release any locks acquired before the failure */
-       }
-       else
-               smb_SetSMBDataLength(outp, 0);
-done:
-       cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
-doneSync:
-       lock_ReleaseMutex(&scp->mx);
-       cm_ReleaseUser(userp);
-       smb_ReleaseFID(fidp);
-
-       return code;
+    if (code) {
+        /* release any locks acquired before the failure */
+    }
+    else
+        smb_SetSMBDataLength(outp, 0);
+  done:   
+    cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+  doneSync:
+    lock_ReleaseMutex(&scp->mx);
+    cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
+
+    return code;
 }
 
 long smb_ReceiveV3GetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       unsigned short fid;
+    unsigned short fid;
     smb_fid_t *fidp;
     cm_scache_t *scp;
     long code = 0;
-    long searchTime;
+    time_t searchTime;
     cm_user_t *userp;
-       cm_req_t req;
+    cm_req_t req;
 
-       cm_InitReq(&req);
+    cm_InitReq(&req);
 
     fid = smb_GetSMBParm(inp, 0);
     fid = smb_ChainFID(fid, inp);
         
     fidp = smb_FindFID(vcp, fid, 0);
     if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
-               return CM_ERROR_BADFD;
+        return CM_ERROR_BADFD;
     }
         
     userp = smb_GetUser(vcp, inp);
@@ -2914,16 +4640,16 @@ long smb_ReceiveV3GetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
     scp = fidp->scp;
         
     /* otherwise, stat the file */
-       lock_ObtainMutex(&scp->mx);
+    lock_ObtainMutex(&scp->mx);
     code = cm_SyncOp(scp, NULL, userp, &req, 0,
                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-       if (code) goto done;
+    if (code) goto done;
 
-       /* decode times.  We need a search time, but the response to this
+    /* decode times.  We need a search time, but the response to this
      * call provides the date first, not the time, as returned in the
      * searchTime variable.  So we take the high-order bits first.
      */
-       smb_SearchTimeFromUnixTime(&searchTime, scp->clientModTime);
+    smb_SearchTimeFromUnixTime(&searchTime, scp->clientModTime);
     smb_SetSMBParm(outp, 0, (searchTime >> 16) & 0xffff);      /* ctime */
     smb_SetSMBParm(outp, 1, searchTime & 0xffff);
     smb_SetSMBParm(outp, 2, (searchTime >> 16) & 0xffff);      /* atime */
@@ -2937,7 +4663,7 @@ long smb_ReceiveV3GetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
     smb_SetSMBParm(outp, 8, scp->length.LowPart & 0xffff);             /* alloc size */
     smb_SetSMBParm(outp, 9, (scp->length.LowPart >> 16) & 0xffff);
 
-       /* file attribute */
+    /* file attribute */
     smb_SetSMBParm(outp, 10, smb_Attributes(scp));
         
     /* and finalize stuff */
@@ -2945,46 +4671,46 @@ long smb_ReceiveV3GetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
     code = 0;
 
   done:
-       lock_ReleaseMutex(&scp->mx);
-       cm_ReleaseUser(userp);
-       smb_ReleaseFID(fidp);
-       return code;
-}
+    lock_ReleaseMutex(&scp->mx);
+    cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
+    return code;
+}       
 
 long smb_ReceiveV3SetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       unsigned short fid;
+    unsigned short fid;
     smb_fid_t *fidp;
     cm_scache_t *scp;
     long code = 0;
-       long searchTime;
-    long unixTime;
+    time_t searchTime;
+    time_t unixTime;
     cm_user_t *userp;
     cm_attr_t attrs;
-       cm_req_t req;
+    cm_req_t req;
 
-       cm_InitReq(&req);
+    cm_InitReq(&req);
 
     fid = smb_GetSMBParm(inp, 0);
     fid = smb_ChainFID(fid, inp);
         
     fidp = smb_FindFID(vcp, fid, 0);
     if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
-               return CM_ERROR_BADFD;
+        return CM_ERROR_BADFD;
     }
         
     userp = smb_GetUser(vcp, inp);
         
     scp = fidp->scp;
         
-       /* now prepare to call cm_setattr.  This message only sets various times,
+    /* now prepare to call cm_setattr.  This message only sets various times,
      * and AFS only implements mtime, and we'll set the mtime if that's
      * requested.  The others we'll ignore.
      */
-       searchTime = smb_GetSMBParm(inp, 5) | (smb_GetSMBParm(inp, 6) << 16);
+    searchTime = smb_GetSMBParm(inp, 5) | (smb_GetSMBParm(inp, 6) << 16);
         
     if (searchTime != 0) {
-               smb_UnixTimeFromSearchTime(&unixTime, searchTime);
+        smb_UnixTimeFromSearchTime(&unixTime, searchTime);
 
         if ( unixTime != -1 ) {
             attrs.mask = CM_ATTRMASK_CLIENTMODTIME;
@@ -2998,15 +4724,15 @@ long smb_ReceiveV3SetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
     }
     else code = 0;
 
-       cm_ReleaseUser(userp);
-       smb_ReleaseFID(fidp);
-       return code;
+    cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
+    return code;
 }
 
 
 long smb_ReceiveV3ReadX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       osi_hyper_t offset;
+    osi_hyper_t offset;
     long count, finalCount;
     unsigned short fd;
     smb_fid_t *fidp;
@@ -3022,35 +4748,35 @@ long smb_ReceiveV3ReadX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     osi_Log3(smb_logp, "smb_ReceiveV3Read fd %d, off 0x%x, size 0x%x",
              fd, offset.LowPart, count);
         
-       fd = smb_ChainFID(fd, inp);
+    fd = smb_ChainFID(fd, inp);
     fidp = smb_FindFID(vcp, fd, 0);
     if (!fidp) {
-               return CM_ERROR_BADFD;
+        return CM_ERROR_BADFD;
     }
-       /* set inp->fid so that later read calls in same msg can find fid */
+    /* set inp->fid so that later read calls in same msg can find fid */
     inp->fid = fd;
 
     if (fidp->flags & SMB_FID_IOCTL) {
-               return smb_IoctlV3Read(fidp, vcp, inp, outp);
+        return smb_IoctlV3Read(fidp, vcp, inp, outp);
     }
         
-       userp = smb_GetUser(vcp, inp);
+    userp = smb_GetUser(vcp, inp);
 
-       /* 0 and 1 are reserved for request chaining, were setup by our caller,
+    /* 0 and 1 are reserved for request chaining, were setup by our caller,
      * and will be further filled in after we return.
      */
     smb_SetSMBParm(outp, 2, 0);        /* remaining bytes, for pipes */
     smb_SetSMBParm(outp, 3, 0);        /* resvd */
     smb_SetSMBParm(outp, 4, 0);        /* resvd */
-       smb_SetSMBParm(outp, 5, count); /* # of bytes we're going to read */
+    smb_SetSMBParm(outp, 5, count);    /* # of bytes we're going to read */
     /* fill in #6 when we have all the parameters' space reserved */
     smb_SetSMBParm(outp, 7, 0);        /* resv'd */
     smb_SetSMBParm(outp, 8, 0);        /* resv'd */
     smb_SetSMBParm(outp, 9, 0);        /* resv'd */
     smb_SetSMBParm(outp, 10, 0);       /* resv'd */
-       smb_SetSMBParm(outp, 11, 0);    /* reserved */
+    smb_SetSMBParm(outp, 11, 0);       /* reserved */
 
-       /* get op ptr after putting in the parms, since otherwise we don't
+    /* get op ptr after putting in the parms, since otherwise we don't
      * know where the data really is.
      */
     op = smb_GetSMBData(outp, NULL);
@@ -3058,18 +4784,18 @@ long smb_ReceiveV3ReadX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     /* now fill in offset from start of SMB header to first data byte (to op) */
     smb_SetSMBParm(outp, 6, ((int) (op - outp->data)));
 
-       /* set the packet data length the count of the # of bytes */
+    /* set the packet data length the count of the # of bytes */
     smb_SetSMBDataLength(outp, count);
 
 #ifndef DJGPP
-       code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount);
+    code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount);
 #else /* DJGPP */
-       code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount, FALSE);
+    code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount, FALSE);
 #endif /* !DJGPP */
 
-       /* fix some things up */
-       smb_SetSMBParm(outp, 5, finalCount);
-       smb_SetSMBDataLength(outp, finalCount);
+    /* fix some things up */
+    smb_SetSMBParm(outp, 5, finalCount);
+    smb_SetSMBDataLength(outp, finalCount);
 
     smb_ReleaseFID(fidp);
 
@@ -3079,144 +4805,149 @@ long smb_ReceiveV3ReadX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         
 /*
  * Values for createDisp, copied from NTDDK.H
- *
- *  FILE_SUPERSEDE     0       (???)
- *  FILE_OPEN          1       (open)
- *  FILE_CREATE                2       (exclusive)
- *  FILE_OPEN_IF       3       (non-exclusive)
- *  FILE_OVERWRITE     4       (open & truncate, but do not create)
- *  FILE_OVERWRITE_IF  5       (open & truncate, or create)
  */
+#define  FILE_SUPERSEDE        0       // (???)
+#define  FILE_OPEN             1       // (open)
+#define  FILE_CREATE   2       // (exclusive)
+#define  FILE_OPEN_IF  3       // (non-exclusive)
+#define  FILE_OVERWRITE        4       // (open & truncate, but do not create)
+#define  FILE_OVERWRITE_IF 5   // (open & truncate, or create)
 
 long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       char *pathp, *realPathp;
-       long code = 0;
-       cm_space_t *spacep;
-       cm_user_t *userp;
-       cm_scache_t *dscp;              /* parent dir */
-       cm_scache_t *scp;               /* file to create or open */
-       cm_attr_t setAttr;
-       char *lastNamep;
+    char *pathp, *realPathp;
+    long code = 0;
+    cm_space_t *spacep;
+    cm_user_t *userp;
+    cm_scache_t *dscp;         /* parent dir */
+    cm_scache_t *scp;          /* file to create or open */
+    cm_scache_t *targetScp;    /* if scp is a symlink */
+    cm_attr_t setAttr;
+    char *lastNamep;
     char *treeStartp;
-       unsigned short nameLength;
-       unsigned int flags;
-       unsigned int requestOpLock;
-       unsigned int requestBatchOpLock;
-       unsigned int mustBeDir;
+    unsigned short nameLength;
+    unsigned int flags;
+    unsigned int requestOpLock;
+    unsigned int requestBatchOpLock;
+    unsigned int mustBeDir;
     unsigned int treeCreate;
-       int realDirFlag;
-       unsigned int desiredAccess;
-       unsigned int extAttributes;
-       unsigned int createDisp;
-       unsigned int createOptions;
-       int initialModeBits;
-       unsigned short baseFid;
-       smb_fid_t *baseFidp;
-       smb_fid_t *fidp;
-       cm_scache_t *baseDirp;
-       unsigned short openAction;
-       int parmSlot;
-       long fidflags;
-       FILETIME ft;
-       LARGE_INTEGER sz;
-       char *tidPathp;
-       BOOL foundscp;
-       cm_req_t req;
-
-       cm_InitReq(&req);
+    int realDirFlag;
+    unsigned int desiredAccess;
+    unsigned int extAttributes;
+    unsigned int createDisp;
+    unsigned int createOptions;
+    int initialModeBits;
+    unsigned short baseFid;
+    smb_fid_t *baseFidp;
+    smb_fid_t *fidp;
+    cm_scache_t *baseDirp;
+    unsigned short openAction;
+    int parmSlot;
+    long fidflags;
+    FILETIME ft;
+    LARGE_INTEGER sz;
+    char *tidPathp;
+    BOOL foundscp;
+    cm_req_t req;
+
+    cm_InitReq(&req);
 
     treeCreate = FALSE;
-       foundscp = FALSE;
-       scp = NULL;
-
-       nameLength = smb_GetSMBOffsetParm(inp, 2, 1);
-       flags = smb_GetSMBOffsetParm(inp, 3, 1)
-                 | (smb_GetSMBOffsetParm(inp, 4, 1) << 16);
-       requestOpLock = flags & 0x02;
-       requestBatchOpLock = flags & 0x04;
-       mustBeDir = flags & 0x08;
-
-       /*
-        * Why all of a sudden 32-bit FID?
-        * We will reject all bits higher than 16.
-        */
-       if (smb_GetSMBOffsetParm(inp, 6, 1) != 0)
-               return CM_ERROR_INVAL;
-       baseFid = smb_GetSMBOffsetParm(inp, 5, 1);
-       desiredAccess = smb_GetSMBOffsetParm(inp, 7, 1)
-                         | (smb_GetSMBOffsetParm(inp, 8, 1) << 16);
-       extAttributes = smb_GetSMBOffsetParm(inp, 13, 1)
-                         | (smb_GetSMBOffsetParm(inp, 14, 1) << 16);
-       createDisp = smb_GetSMBOffsetParm(inp, 17, 1)
-                       | (smb_GetSMBOffsetParm(inp, 18, 1) << 16);
-       createOptions = smb_GetSMBOffsetParm(inp, 19, 1)
-                         | (smb_GetSMBOffsetParm(inp, 20, 1) << 16);
-
-       /* mustBeDir is never set; createOptions directory bit seems to be
-         * more important
-        */
-       if (createOptions & 1)
-               realDirFlag = 1;
-       else if (createOptions & 0x40)
-               realDirFlag = 0;
-       else
-               realDirFlag = -1;
-
-       /*
-        * compute initial mode bits based on read-only flag in
-        * extended attributes
-        */
-       initialModeBits = 0666;
-       if (extAttributes & 1) initialModeBits &= ~0222;
+    foundscp = FALSE;
+    scp = NULL;
+
+    nameLength = smb_GetSMBOffsetParm(inp, 2, 1);
+    flags = smb_GetSMBOffsetParm(inp, 3, 1)
+        | (smb_GetSMBOffsetParm(inp, 4, 1) << 16);
+    requestOpLock = flags & 0x02;
+    requestBatchOpLock = flags & 0x04;
+    mustBeDir = flags & 0x08;
 
-       pathp = smb_GetSMBData(inp, NULL);
-       /* Sometimes path is not null-terminated, so we make a copy. */
-       realPathp = malloc(nameLength+1);
-       memcpy(realPathp, pathp, nameLength);
-       realPathp[nameLength] = 0;
+    /*
+     * Why all of a sudden 32-bit FID?
+     * We will reject all bits higher than 16.
+     */
+    if (smb_GetSMBOffsetParm(inp, 6, 1) != 0)
+        return CM_ERROR_INVAL;
+    baseFid = smb_GetSMBOffsetParm(inp, 5, 1);
+    desiredAccess = smb_GetSMBOffsetParm(inp, 7, 1)
+        | (smb_GetSMBOffsetParm(inp, 8, 1) << 16);
+    extAttributes = smb_GetSMBOffsetParm(inp, 13, 1)
+        | (smb_GetSMBOffsetParm(inp, 14, 1) << 16);
+    createDisp = smb_GetSMBOffsetParm(inp, 17, 1)
+        | (smb_GetSMBOffsetParm(inp, 18, 1) << 16);
+    createOptions = smb_GetSMBOffsetParm(inp, 19, 1)
+        | (smb_GetSMBOffsetParm(inp, 20, 1) << 16);
+
+    /* mustBeDir is never set; createOptions directory bit seems to be
+     * more important
+     */
+    if (createOptions & 1)
+        realDirFlag = 1;
+    else if (createOptions & 0x40)
+        realDirFlag = 0;
+    else
+        realDirFlag = -1;
+
+    /*
+     * compute initial mode bits based on read-only flag in
+     * extended attributes
+     */
+    initialModeBits = 0666;
+    if (extAttributes & 1) 
+        initialModeBits &= ~0222;
+
+    pathp = smb_GetSMBData(inp, NULL);
+    /* Sometimes path is not null-terminated, so we make a copy. */
+    realPathp = malloc(nameLength+1);
+    memcpy(realPathp, pathp, nameLength);
+    realPathp[nameLength] = 0;
+    if (smb_StoreAnsiFilenames)
+        OemToChar(realPathp,realPathp);
 
-       spacep = inp->spacep;
-       smb_StripLastComponent(spacep->data, &lastNamep, realPathp);
+    spacep = inp->spacep;
+    smb_StripLastComponent(spacep->data, &lastNamep, realPathp);
 
     osi_Log1(smb_logp,"NTCreateX for [%s]",osi_LogSaveString(smb_logp,realPathp));
-    osi_Log4(smb_logp,"NTCreateX da=[%x] ea=[%x] cd=[%x] co=[%x]", desiredAccess, extAttributes, createDisp, createOptions);
-
-       if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
-               /* special case magic file name for receiving IOCTL requests
-                * (since IOCTL calls themselves aren't getting through).
-                */
-               fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
-               smb_SetupIoctlFid(fidp, spacep);
-
-               /* set inp->fid so that later read calls in same msg can find fid */
-               inp->fid = fidp->fid;
-
-               /* out parms */
-               parmSlot = 2;
-               smb_SetSMBParmByte(outp, parmSlot, 0);  /* oplock */
-               smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
-               smb_SetSMBParmLong(outp, parmSlot, 1); parmSlot += 2; /* Action */
-               /* times */
-               memset(&ft, 0, sizeof(ft));
-               smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
-               smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
-               smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
-               smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
-               smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2; /* attr */
-               sz.HighPart = 0x7fff; sz.LowPart = 0;
-               smb_SetSMBParmDouble(outp, parmSlot, (char *)&sz); parmSlot += 4; /* alen */
-               smb_SetSMBParmDouble(outp, parmSlot, (char *)&sz); parmSlot += 4; /* len */
-               smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;  /* filetype */
-               smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;  /* dev state */
-               smb_SetSMBParmByte(outp, parmSlot, 0);  /* is a dir? */
-               smb_SetSMBDataLength(outp, 0);
-
-               /* clean up fid reference */
-               smb_ReleaseFID(fidp);
-               free(realPathp);
-               return 0;
-       }
+    osi_Log4(smb_logp,"... da=[%x] ea=[%x] cd=[%x] co=[%x]", desiredAccess, extAttributes, createDisp, createOptions);
+    osi_Log2(smb_logp,"... flags=[%x] lastNamep=[%s]", flags, osi_LogSaveString(smb_logp,(lastNamep?lastNamep:"null")));
+
+    if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
+        /* special case magic file name for receiving IOCTL requests
+         * (since IOCTL calls themselves aren't getting through).
+         */
+        fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
+        smb_SetupIoctlFid(fidp, spacep);
+        osi_Log1(smb_logp,"NTCreateX Setting up IOCTL on fid[%d]",fidp->fid);
+
+        /* set inp->fid so that later read calls in same msg can find fid */
+        inp->fid = fidp->fid;
+
+        /* out parms */
+        parmSlot = 2;
+        smb_SetSMBParmByte(outp, parmSlot, 0); /* oplock */
+        smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
+        smb_SetSMBParmLong(outp, parmSlot, 1); parmSlot += 2; /* Action */
+        /* times */
+        memset(&ft, 0, sizeof(ft));
+        smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
+        smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
+        smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
+        smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
+        smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2; /* attr */
+        sz.HighPart = 0x7fff; sz.LowPart = 0;
+        smb_SetSMBParmDouble(outp, parmSlot, (char *)&sz); parmSlot += 4; /* alen */
+        smb_SetSMBParmDouble(outp, parmSlot, (char *)&sz); parmSlot += 4; /* len */
+        smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* filetype */
+        smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* dev state */
+        smb_SetSMBParmByte(outp, parmSlot, 0); /* is a dir? */
+        smb_SetSMBDataLength(outp, 0);
+
+        /* clean up fid reference */
+        smb_ReleaseFID(fidp);
+        free(realPathp);
+        return 0;
+    }
 
 #ifdef DEBUG_VERBOSE
     {
@@ -3234,73 +4965,112 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
        return CM_ERROR_INVAL;
     }
 
-       if (baseFid == 0) {
-               baseDirp = cm_rootSCachep;
-               tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
-       }
-       else {
+    if (baseFid == 0) {
+        baseDirp = cm_rootSCachep;
+        code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
+        if (code == CM_ERROR_TIDIPC) {
+            /* Attempt to use a TID allocated for IPC.  The client
+             * is probably looking for DCE RPC end points which we
+             * don't support. */
+            osi_Log0(smb_logp, "NTCreateX received IPC TID");
+            free(realPathp);
+            cm_ReleaseUser(userp);
+            return CM_ERROR_NOSUCHFILE;
+        }
+    }
+    else {
         baseFidp = smb_FindFID(vcp, baseFid, 0);
         if (!baseFidp) {
-               osi_Log1(smb_logp, "NTCreateX Invalid base fid [%d]", baseFid);
-               free(realPathp);
-               cm_ReleaseUser(userp);
-               return CM_ERROR_INVAL;
-        }
-               baseDirp = baseFidp->scp;
-               tidPathp = NULL;
-       }
+            osi_Log1(smb_logp, "NTCreateX Invalid base fid [%d]", baseFid);
+            free(realPathp);
+            cm_ReleaseUser(userp);
+            return CM_ERROR_INVAL;
+        }       
+        baseDirp = baseFidp->scp;
+        tidPathp = NULL;
+    }
 
     osi_Log1(smb_logp, "NTCreateX tidPathp=[%s]", (tidPathp==NULL)?"null": osi_LogSaveString(smb_logp,tidPathp));
-       
+
     /* compute open mode */
-       fidflags = 0;
-       if (desiredAccess & DELETE)
-               fidflags |= SMB_FID_OPENDELETE;
-       if (desiredAccess & AFS_ACCESS_READ)
-               fidflags |= SMB_FID_OPENREAD;
-       if (desiredAccess & AFS_ACCESS_WRITE)
-               fidflags |= SMB_FID_OPENWRITE;
-
-       dscp = NULL;
-       code = 0;
-       code = cm_NameI(baseDirp, realPathp, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
-                       userp, tidPathp, &req, &scp);
-       if (code == 0) foundscp = TRUE;
-       if (code != 0
-           || (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE))) {
-               /* look up parent directory */
+    fidflags = 0;
+    if (desiredAccess & DELETE)
+        fidflags |= SMB_FID_OPENDELETE;
+    if (desiredAccess & AFS_ACCESS_READ)
+        fidflags |= SMB_FID_OPENREAD;
+    if (desiredAccess & AFS_ACCESS_WRITE)
+        fidflags |= SMB_FID_OPENWRITE;
+
+    dscp = NULL;
+    code = 0;
+    /* For an exclusive create, we want to do a case sensitive match for the last component. */
+    if ( createDisp == FILE_CREATE || 
+         createDisp == FILE_OVERWRITE ||
+         createDisp == FILE_OVERWRITE_IF) {
+        code = cm_NameI(baseDirp, spacep->data, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                        userp, tidPathp, &req, &dscp);
+        if (code == 0) {
+            code = cm_Lookup(dscp, (lastNamep)?(lastNamep+1):realPathp, CM_FLAG_FOLLOW,
+                             userp, &req, &scp);
+            if (code == CM_ERROR_NOSUCHFILE) {
+                code = cm_Lookup(dscp, (lastNamep)?(lastNamep+1):realPathp, 
+                                 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, &req, &scp);
+                if (code == 0 && realDirFlag == 1) {
+                    cm_ReleaseSCache(scp);
+                    cm_ReleaseSCache(dscp);
+                    cm_ReleaseUser(userp);
+                    free(realPathp);
+                    return CM_ERROR_EXISTS;
+                }
+            }
+        } else
+            dscp = NULL;
+    } else {
+        code = cm_NameI(baseDirp, realPathp, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                        userp, tidPathp, &req, &scp);
+    }
+    if (code == 0) 
+        foundscp = TRUE;
+
+    if (!foundscp || (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE))) {
+        /* look up parent directory */
         /* If we are trying to create a path (i.e. multiple nested directories), then we don't *need*
-        the immediate parent.  We have to work our way up realPathp until we hit something that we
-        recognize.
-        */
+         * the immediate parent.  We have to work our way up realPathp until we hit something that we
+         * recognize.
+         */
 
-        while(1) {
-            char *tp;
+        if ( !dscp ) {
+            while (1) {
+                char *tp;
 
-            code = cm_NameI(baseDirp, spacep->data,
+                code = cm_NameI(baseDirp, spacep->data,
                              CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
                              userp, tidPathp, &req, &dscp);
 
-            if (code && 
-                (tp = strrchr(spacep->data,'\\')) &&
-                (createDisp == 2) &&
-                (realDirFlag == 1)) {
-                *tp++ = 0;
-                treeCreate = TRUE;
-                treeStartp = realPathp + (tp - spacep->data);
-
-                if (*tp && !smb_IsLegalFilename(tp)) {
-                    if(baseFid != 0) smb_ReleaseFID(baseFidp);
-                    cm_ReleaseUser(userp);
-                    free(realPathp);
-                    return CM_ERROR_BADNTFILENAME;
+                if (code && 
+                     (tp = strrchr(spacep->data,'\\')) &&
+                     (createDisp == FILE_CREATE) &&
+                     (realDirFlag == 1)) {
+                    *tp++ = 0;
+                    treeCreate = TRUE;
+                    treeStartp = realPathp + (tp - spacep->data);
+
+                    if (*tp && !smb_IsLegalFilename(tp)) {
+                        if (baseFid != 0) 
+                            smb_ReleaseFID(baseFidp);
+                        cm_ReleaseUser(userp);
+                        free(realPathp);
+                        return CM_ERROR_BADNTFILENAME;
+                    }
                 }
+                else
+                    break;
             }
-            else
-                break;
-        }
+        } else
+            code = 0;
 
-        if (baseFid != 0) smb_ReleaseFID(baseFidp);
+        if (baseFid != 0) 
+            smb_ReleaseFID(baseFidp);
 
         if (code) {
             osi_Log0(smb_logp,"NTCreateX parent not found");
@@ -3309,7 +5079,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
             return code;
         }
 
-        if(treeCreate && dscp->fileType == CM_SCACHETYPE_FILE) {
+        if (treeCreate && dscp->fileType == CM_SCACHETYPE_FILE) {
             /* A file exists where we want a directory. */
             cm_ReleaseSCache(dscp);
             cm_ReleaseUser(userp);
@@ -3317,8 +5087,10 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
             return CM_ERROR_EXISTS;
         }
 
-        if (!lastNamep) lastNamep = realPathp;
-        else lastNamep++;
+        if (!lastNamep) 
+            lastNamep = realPathp;
+        else 
+            lastNamep++;
 
         if (!smb_IsLegalFilename(lastNamep)) {
             cm_ReleaseSCache(dscp);
@@ -3328,20 +5100,29 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         }
 
         if (!foundscp && !treeCreate) {
-                       code = cm_Lookup(dscp, lastNamep,
-                                        CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
-                                        userp, &req, &scp);
-                       if (code && code != CM_ERROR_NOSUCHFILE) {
-                               cm_ReleaseSCache(dscp);
-                               cm_ReleaseUser(userp);
-                               free(realPathp);
-                               return code;
-                       }
-               }
-       }
-       else {
-               if (baseFid != 0) smb_ReleaseFID(baseFidp);
-       }
+            if ( createDisp == FILE_CREATE || 
+                 createDisp == FILE_OVERWRITE ||
+                 createDisp == FILE_OVERWRITE_IF) 
+            {
+                code = cm_Lookup(dscp, lastNamep,
+                                  CM_FLAG_FOLLOW, userp, &req, &scp);
+            } else {
+                code = cm_Lookup(dscp, lastNamep,
+                                 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                                 userp, &req, &scp);
+            }
+            if (code && code != CM_ERROR_NOSUCHFILE) {
+                cm_ReleaseSCache(dscp);
+                cm_ReleaseUser(userp);
+                free(realPathp);
+                return code;
+            }
+        }
+    }
+    else {
+        if (baseFid != 0) 
+            smb_ReleaseFID(baseFidp);
+       }       
 
        /* if we get here, if code is 0, the file exists and is represented by
         * scp.  Otherwise, we have to create it.  The dir may be represented
@@ -3349,229 +5130,283 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         * scp is NULL.
         */
        if (code == 0 && !treeCreate) {
-               code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp,
-                                     &req);
-               if (code) {
-                       if (dscp) cm_ReleaseSCache(dscp);
-                       cm_ReleaseSCache(scp);
-                       cm_ReleaseUser(userp);
-                       free(realPathp);
-                       return code;
-               }
-
-               if (createDisp == 2) {
-                       /* oops, file shouldn't be there */
-                       if (dscp) cm_ReleaseSCache(dscp);
-                       cm_ReleaseSCache(scp);
-                       cm_ReleaseUser(userp);
-                       free(realPathp);
-                       return CM_ERROR_EXISTS;
-               }
-
-               if (createDisp == 4
-                   || createDisp == 5) {
-                       setAttr.mask = CM_ATTRMASK_LENGTH;
-                       setAttr.length.LowPart = 0;
-                       setAttr.length.HighPart = 0;
-                       code = cm_SetAttr(scp, &setAttr, userp, &req);
-                       openAction = 3; /* truncated existing file */
-               }
-               else openAction = 1;    /* found existing file */
-       }
-       else if (createDisp == 1 || createDisp == 4) {
-               /* don't create if not found */
-               if (dscp) cm_ReleaseSCache(dscp);
-               cm_ReleaseUser(userp);
-               free(realPathp);
-               return CM_ERROR_NOSUCHFILE;
-       }
+            if (createDisp == FILE_CREATE) {
+                /* oops, file shouldn't be there */
+                if (dscp) cm_ReleaseSCache(dscp);
+                cm_ReleaseSCache(scp);
+                cm_ReleaseUser(userp);
+                free(realPathp);
+                return CM_ERROR_EXISTS;
+            }
+
+            if ( createDisp == FILE_OVERWRITE || 
+                 createDisp == FILE_OVERWRITE_IF) {
+                setAttr.mask = CM_ATTRMASK_LENGTH;
+                setAttr.length.LowPart = 0;
+                setAttr.length.HighPart = 0;
+                /* now watch for a symlink */
+                code = 0;
+                while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
+                    targetScp = 0;
+                    code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
+                    if (code == 0) {
+                        /* we have a more accurate file to use (the
+                        * target of the symbolic link).  Otherwise,
+                        * we'll just use the symlink anyway.
+                        */
+                        osi_Log2(smb_logp, "symlink vp %x to vp %x",
+                                  scp, targetScp);
+                        cm_ReleaseSCache(scp);
+                        scp = targetScp;
+                    }
+                }
+                code = cm_SetAttr(scp, &setAttr, userp, &req);
+                openAction = 3;        /* truncated existing file */
+            }
+            else 
+                openAction = 1;        /* found existing file */
+
+            code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp,
+                                  &req);
+            if (code) {
+                if (dscp) cm_ReleaseSCache(dscp);
+                cm_ReleaseSCache(scp);
+                cm_ReleaseUser(userp);
+                free(realPathp);
+                return code;
+            }
+       }       
+       else if (createDisp == FILE_OPEN || createDisp == FILE_OVERWRITE) {
+            /* don't create if not found */
+            if (dscp) cm_ReleaseSCache(dscp);
+            cm_ReleaseUser(userp);
+            free(realPathp);
+            return CM_ERROR_NOSUCHFILE;
+       }       
        else if (realDirFlag == 0 || realDirFlag == -1) {
-               osi_assert(dscp != NULL);
-               osi_Log1(smb_logp, "smb_ReceiveNTCreateX creating file %s",
-                               osi_LogSaveString(smb_logp, lastNamep));
-               openAction = 2;         /* created file */
-               setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
-               setAttr.clientModTime = time(NULL);
-               code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
-                                &req);
-               if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-                       smb_NotifyChange(FILE_ACTION_ADDED,
-                                        FILE_NOTIFY_CHANGE_FILE_NAME,
-                                        dscp, lastNamep, NULL, TRUE);
-               if (code == CM_ERROR_EXISTS && createDisp != 2) {
-                       /* Not an exclusive create, and someone else tried
-                        * creating it already, then we open it anyway.  We
-                        * don't bother retrying after this, since if this next
-                        * fails, that means that the file was deleted after we
-                        * started this call.
-                        */
-                       code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
-                                        userp, &req, &scp);
-                       if (code == 0) {
-                               if (createDisp == 5) {
-                                       setAttr.mask = CM_ATTRMASK_LENGTH;
-                                       setAttr.length.LowPart = 0;
-                                       setAttr.length.HighPart = 0;
-                                       code = cm_SetAttr(scp, &setAttr, userp,
-                                                         &req);
-                               }
-                       }       /* lookup succeeded */
-               }
-       }
+            osi_assert(dscp != NULL);
+            osi_Log1(smb_logp, "smb_ReceiveNTCreateX creating file %s",
+                      osi_LogSaveString(smb_logp, lastNamep));
+            openAction = 2;            /* created file */
+            setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
+            setAttr.clientModTime = time(NULL);
+            code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
+                              &req);
+            if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
+                smb_NotifyChange(FILE_ACTION_ADDED,
+                                  FILE_NOTIFY_CHANGE_FILE_NAME,
+                                  dscp, lastNamep, NULL, TRUE);
+            if (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE) {
+                /* Not an exclusive create, and someone else tried
+                 * creating it already, then we open it anyway.  We
+                 * don't bother retrying after this, since if this next
+                 * fails, that means that the file was deleted after we
+                 * started this call.
+                 */
+                code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
+                                  userp, &req, &scp);
+                if (code == 0) {
+                    if (createDisp == FILE_OVERWRITE_IF) {
+                        setAttr.mask = CM_ATTRMASK_LENGTH;
+                        setAttr.length.LowPart = 0;
+                        setAttr.length.HighPart = 0;
+
+                        /* now watch for a symlink */
+                        code = 0;
+                        while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
+                            targetScp = 0;
+                            code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
+                            if (code == 0) {
+                                /* we have a more accurate file to use (the
+                                * target of the symbolic link).  Otherwise,
+                                * we'll just use the symlink anyway.
+                                */
+                                osi_Log2(smb_logp, "symlink vp %x to vp %x",
+                                          scp, targetScp);
+                                cm_ReleaseSCache(scp);
+                                scp = targetScp;
+                            }
+                        }
+                        code = cm_SetAttr(scp, &setAttr, userp, &req);
+                    }
+                }      /* lookup succeeded */
+            }
+       }       
        else {
-        char *tp, *pp;
-        char *cp; /* This component */
-        int clen = 0; /* length of component */
-        cm_scache_t *tscp;
-        int isLast = 0;
+            char *tp, *pp;
+            char *cp; /* This component */
+            int clen = 0; /* length of component */
+            cm_scache_t *tscp;
+            int isLast = 0;
                
-        /* create directory */
-               if ( !treeCreate ) treeStartp = lastNamep;
-        osi_assert(dscp != NULL);
-        osi_Log1(smb_logp, "smb_ReceiveNTCreateX creating directory [%s]",
-                               osi_LogSaveString(smb_logp, treeStartp));
-               openAction = 2;         /* created directory */
-
-               setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
-               setAttr.clientModTime = time(NULL);
+            /* create directory */
+            if ( !treeCreate ) 
+                treeStartp = lastNamep;
+            osi_assert(dscp != NULL);
+            osi_Log1(smb_logp, "smb_ReceiveNTCreateX creating directory [%s]",
+                      osi_LogSaveString(smb_logp, treeStartp));
+            openAction = 2;            /* created directory */
+
+            setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
+            setAttr.clientModTime = time(NULL);
                
-               pp = treeStartp;
-               cp = spacep->data;
-               tscp = dscp;
-
-               while(pp && *pp) {
-                       tp = strchr(pp, '\\');
-                       if(!tp) {
-                               strcpy(cp,pp);
-                clen = strlen(cp);
-                               isLast = 1; /* indicate last component.  the supplied path never ends in a slash */
-                       }
-                       else {
-                               clen = tp - pp;
-                               strncpy(cp,pp,clen);
-                               *(cp + clen) = 0;
-                               tp++;
-                       }
-                       pp = tp;
-
-                       if(clen == 0) continue; /* the supplied path can't have consecutive slashes either , but */
-
-                       /* cp is the next component to be created. */
-                       code = cm_MakeDir(tscp, cp, 0, &setAttr, userp, &req);
-                       if (code == 0 && (tscp->flags & CM_SCACHEFLAG_ANYWATCH))
-                               smb_NotifyChange(FILE_ACTION_ADDED,
-                               FILE_NOTIFY_CHANGE_DIR_NAME,
-                               tscp, cp, NULL, TRUE);
-                       if (code == 0 || 
-                               (code == CM_ERROR_EXISTS && createDisp != 2)) {
-                                       /* Not an exclusive create, and someone else tried
-                                       * creating it already, then we open it anyway.  We
-                                       * don't bother retrying after this, since if this next
-                                       * fails, that means that the file was deleted after we
-                                       * started this call.
-                                       */
-                                       code = cm_Lookup(tscp, cp, CM_FLAG_CASEFOLD,
-                                               userp, &req, &scp);
-                               }
-                       if(code) break;
-
-                       if(!isLast) { /* for anything other than dscp, release it unless it's the last one */
-                               cm_ReleaseSCache(tscp);
-                               tscp = scp; /* Newly created directory will be next parent */
-                       }
-               }
-
-               /* 
-               if we get here and code == 0, then scp is the last directory created, and tscp is the
-               parent of scp.  dscp got released if dscp != tscp. both tscp and scp are held.
-               */
-               dscp = tscp;
-       }
+            pp = treeStartp;
+            cp = spacep->data;
+            tscp = dscp;
+
+            while (pp && *pp) {
+                tp = strchr(pp, '\\');
+                if (!tp) {
+                    strcpy(cp,pp);
+                    clen = strlen(cp);
+                    isLast = 1; /* indicate last component.  the supplied path never ends in a slash */
+                }
+                else {
+                    clen = tp - pp;
+                    strncpy(cp,pp,clen);
+                    *(cp + clen) = 0;
+                    tp++;
+                }
+                pp = tp;
+
+                if (clen == 0) 
+                    continue; /* the supplied path can't have consecutive slashes either , but */
+
+                /* cp is the next component to be created. */
+                code = cm_MakeDir(tscp, cp, 0, &setAttr, userp, &req);
+                if (code == 0 && (tscp->flags & CM_SCACHEFLAG_ANYWATCH))
+                    smb_NotifyChange(FILE_ACTION_ADDED,
+                                      FILE_NOTIFY_CHANGE_DIR_NAME,
+                                      tscp, cp, NULL, TRUE);
+                if (code == 0 || 
+                     (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE)) {
+                    /* Not an exclusive create, and someone else tried
+                     * creating it already, then we open it anyway.  We
+                     * don't bother retrying after this, since if this next
+                     * fails, that means that the file was deleted after we
+                     * started this call.
+                     */
+                    code = cm_Lookup(tscp, cp, CM_FLAG_CASEFOLD,
+                                      userp, &req, &scp);
+                }
+                if (code) break;
 
-       if (code) {
-               /* something went wrong creating or truncating the file */
-               if (scp) cm_ReleaseSCache(scp);
-        if (dscp) cm_ReleaseSCache(dscp);
-               cm_ReleaseUser(userp);
-               free(realPathp);
-               return code;
+                if (!isLast) { /* for anything other than dscp, release it unless it's the last one */
+                    cm_ReleaseSCache(tscp);
+                    tscp = scp; /* Newly created directory will be next parent */
+                }
+            }
+
+            /* 
+             * if we get here and code == 0, then scp is the last directory created, and tscp is the
+             * parent of scp.  dscp got released if dscp != tscp. both tscp and scp are held.
+             */
+            dscp = tscp;
        }
 
-       /* make sure we have file vs. dir right (only applies for single component case) */
-       if (realDirFlag == 0 && scp->fileType != CM_SCACHETYPE_FILE) {
-               cm_ReleaseSCache(scp);
+    if (code) {
+        /* something went wrong creating or truncating the file */
+        if (scp) cm_ReleaseSCache(scp);
         if (dscp) cm_ReleaseSCache(dscp);
-               cm_ReleaseUser(userp);
-               free(realPathp);
-               return CM_ERROR_ISDIR;
-       }
+        cm_ReleaseUser(userp);
+        free(realPathp);
+        return code;
+    }
+
+    /* make sure we have file vs. dir right (only applies for single component case) */
+    if (realDirFlag == 0 && scp->fileType != CM_SCACHETYPE_FILE) {
+        /* now watch for a symlink */
+        code = 0;
+        while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
+            cm_scache_t * targetScp = 0;
+            code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
+            if (code == 0) {
+                /* we have a more accurate file to use (the
+                * target of the symbolic link).  Otherwise,
+                * we'll just use the symlink anyway.
+                */
+                osi_Log2(smb_logp, "symlink vp %x to vp %x",
+                          scp, targetScp);
+                cm_ReleaseSCache(scp);
+                scp = targetScp;
+            }
+        }
+
+        if (scp->fileType != CM_SCACHETYPE_FILE) {
+            cm_ReleaseSCache(scp);
+            cm_ReleaseUser(userp);
+            free(realPathp);
+            return CM_ERROR_ISDIR;
+        }
+    }
+
     /* (only applies to single component case) */
-       if (realDirFlag == 1 && scp->fileType == CM_SCACHETYPE_FILE) {
-               cm_ReleaseSCache(scp);
+    if (realDirFlag == 1 && scp->fileType == CM_SCACHETYPE_FILE) {
+        cm_ReleaseSCache(scp);
         if (dscp) cm_ReleaseSCache(dscp);
-               cm_ReleaseUser(userp);
-               free(realPathp);
-               return CM_ERROR_NOTDIR;
-       }
+        cm_ReleaseUser(userp);
+        free(realPathp);
+        return CM_ERROR_NOTDIR;
+    }
 
-       /* open the file itself */
-       fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
-       osi_assert(fidp);
-       /* save a pointer to the vnode */
-       fidp->scp = scp;
+    /* open the file itself */
+    fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
+    osi_assert(fidp);
+    /* save a pointer to the vnode */
+    fidp->scp = scp;
 
-       fidp->flags = fidflags;
+    fidp->flags = fidflags;
 
-       /* save parent dir and pathname for delete or change notification */
-       if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
-               fidp->flags |= SMB_FID_NTOPEN;
-               fidp->NTopen_dscp = dscp;
-               cm_HoldSCache(dscp);
-               fidp->NTopen_pathp = strdup(lastNamep);
-       }
-       fidp->NTopen_wholepathp = realPathp;
-
-       /* we don't need this any longer */
-       if (dscp) cm_ReleaseSCache(dscp);
-       cm_Open(scp, 0, userp);
-
-       /* set inp->fid so that later read calls in same msg can find fid */
-       inp->fid = fidp->fid;
-
-       /* out parms */
-       parmSlot = 2;
-       lock_ObtainMutex(&scp->mx);
-       smb_SetSMBParmByte(outp, parmSlot, 0);  /* oplock */
-       smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
-       smb_SetSMBParmLong(outp, parmSlot, openAction); parmSlot += 2;
-       smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
-       smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
-       smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
-       smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
-       smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
-       smb_SetSMBParmLong(outp, parmSlot, smb_ExtAttributes(scp));
-                                               parmSlot += 2;
-       smb_SetSMBParmDouble(outp, parmSlot, (char *)&scp->length); parmSlot += 4;
-       smb_SetSMBParmDouble(outp, parmSlot, (char *)&scp->length); parmSlot += 4;
-       smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;  /* filetype */
-       smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;  /* dev state */
-       smb_SetSMBParmByte(outp, parmSlot,
-               scp->fileType == CM_SCACHETYPE_DIRECTORY); /* is a dir? */
-       lock_ReleaseMutex(&scp->mx);
-       smb_SetSMBDataLength(outp, 0);
-
-       osi_Log2(smb_logp, "SMB NT CreateX opening fid %d path %s", fidp->fid,
-                osi_LogSaveString(smb_logp, realPathp));
-
-       smb_ReleaseFID(fidp);
-
-       cm_ReleaseUser(userp);
+    /* save parent dir and pathname for delete or change notification */
+    if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
+        fidp->flags |= SMB_FID_NTOPEN;
+        fidp->NTopen_dscp = dscp;
+        cm_HoldSCache(dscp);
+        fidp->NTopen_pathp = strdup(lastNamep);
+    }
+    fidp->NTopen_wholepathp = realPathp;
+
+    /* we don't need this any longer */
+    if (dscp) cm_ReleaseSCache(dscp);
+    cm_Open(scp, 0, userp);
+
+    /* set inp->fid so that later read calls in same msg can find fid */
+    inp->fid = fidp->fid;
+
+    /* out parms */
+    parmSlot = 2;
+    lock_ObtainMutex(&scp->mx);
+    smb_SetSMBParmByte(outp, parmSlot, 0);     /* oplock */
+    smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
+    smb_SetSMBParmLong(outp, parmSlot, openAction); parmSlot += 2;
+    smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
+    smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
+    smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
+    smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
+    smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
+    smb_SetSMBParmLong(outp, parmSlot, smb_ExtAttributes(scp));
+    parmSlot += 2;
+    smb_SetSMBParmDouble(outp, parmSlot, (char *)&scp->length); parmSlot += 4;
+    smb_SetSMBParmDouble(outp, parmSlot, (char *)&scp->length); parmSlot += 4;
+    smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;     /* filetype */
+    smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;     /* dev state */
+    smb_SetSMBParmByte(outp, parmSlot,
+                        scp->fileType == CM_SCACHETYPE_DIRECTORY); /* is a dir? */
+    lock_ReleaseMutex(&scp->mx);
+    smb_SetSMBDataLength(outp, 0);
+
+    osi_Log2(smb_logp, "SMB NT CreateX opening fid %d path %s", fidp->fid,
+              osi_LogSaveString(smb_logp, realPathp));
+
+    smb_ReleaseFID(fidp);
+
+    cm_ReleaseUser(userp);
 
     /* Can't free realPathp if we get here since fidp->NTopen_wholepathp is pointing there */
 
-       /* leave scp held since we put it in fidp->scp */
-       return 0;
-}
+    /* leave scp held since we put it in fidp->scp */
+    return 0;
+}       
 
 /*
  * A lot of stuff copied verbatim from NT Create&X to NT Tran Create.
@@ -3579,156 +5414,169 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  */
 long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       char *pathp, *realPathp;
-       long code = 0;
-       cm_space_t *spacep;
-       cm_user_t *userp;
-       cm_scache_t *dscp;              /* parent dir */
-       cm_scache_t *scp;               /* file to create or open */
-       cm_attr_t setAttr;
-       char *lastNamep;
-       unsigned long nameLength;
-       unsigned int flags;
-       unsigned int requestOpLock;
-       unsigned int requestBatchOpLock;
-       unsigned int mustBeDir;
+    char *pathp, *realPathp;
+    long code = 0;
+    cm_space_t *spacep;
+    cm_user_t *userp;
+    cm_scache_t *dscp;         /* parent dir */
+    cm_scache_t *scp;          /* file to create or open */
+    cm_scache_t *targetScp;     /* if scp is a symlink */
+    cm_attr_t setAttr;
+    char *lastNamep;
+    unsigned long nameLength;
+    unsigned int flags;
+    unsigned int requestOpLock;
+    unsigned int requestBatchOpLock;
+    unsigned int mustBeDir;
     unsigned int extendedRespRequired;
-       int realDirFlag;
-       unsigned int desiredAccess;
+    int realDirFlag;
+    unsigned int desiredAccess;
 #ifdef DEBUG_VERBOSE    
     unsigned int allocSize;
     unsigned int shareAccess;
 #endif
-       unsigned int extAttributes;
-       unsigned int createDisp;
+    unsigned int extAttributes;
+    unsigned int createDisp;
 #ifdef DEBUG_VERBOSE
     unsigned int sdLen;
 #endif
-       unsigned int createOptions;
-       int initialModeBits;
-       unsigned short baseFid;
-       smb_fid_t *baseFidp;
-       smb_fid_t *fidp;
-       cm_scache_t *baseDirp;
-       unsigned short openAction;
-       int parmSlot;
-       long fidflags;
-       FILETIME ft;
-       char *tidPathp;
-       BOOL foundscp;
-       int parmOffset, dataOffset;
-       char *parmp;
-       ULONG *lparmp;
-       char *outData;
-       cm_req_t req;
-
-       cm_InitReq(&req);
-
-       foundscp = FALSE;
-       scp = NULL;
-
-       parmOffset = smb_GetSMBOffsetParm(inp, 11, 1)
-                       | (smb_GetSMBOffsetParm(inp, 12, 1) << 16);
-       parmp = inp->data + parmOffset;
-       lparmp = (ULONG *) parmp;
-
-       flags = lparmp[0];
-       requestOpLock = flags & 0x02;
-       requestBatchOpLock = flags & 0x04;
-       mustBeDir = flags & 0x08;
+    unsigned int createOptions;
+    int initialModeBits;
+    unsigned short baseFid;
+    smb_fid_t *baseFidp;
+    smb_fid_t *fidp;
+    cm_scache_t *baseDirp;
+    unsigned short openAction;
+    int parmSlot;
+    long fidflags;
+    FILETIME ft;
+    char *tidPathp;
+    BOOL foundscp;
+    int parmOffset, dataOffset;
+    char *parmp;
+    ULONG *lparmp;
+    char *outData;
+    cm_req_t req;
+
+    cm_InitReq(&req);
+
+    foundscp = FALSE;
+    scp = NULL;
+
+    parmOffset = smb_GetSMBOffsetParm(inp, 11, 1)
+        | (smb_GetSMBOffsetParm(inp, 12, 1) << 16);
+    parmp = inp->data + parmOffset;
+    lparmp = (ULONG *) parmp;
+
+    flags = lparmp[0];
+    requestOpLock = flags & 0x02;
+    requestBatchOpLock = flags & 0x04;
+    mustBeDir = flags & 0x08;
     extendedRespRequired = flags & 0x10;
 
-       /*
-        * Why all of a sudden 32-bit FID?
-        * We will reject all bits higher than 16.
-        */
-       if (lparmp[1] & 0xFFFF0000)
-               return CM_ERROR_INVAL;
-       baseFid = (unsigned short)lparmp[1];
-       desiredAccess = lparmp[2];
+    /*
+     * Why all of a sudden 32-bit FID?
+     * We will reject all bits higher than 16.
+     */
+    if (lparmp[1] & 0xFFFF0000)
+        return CM_ERROR_INVAL;
+    baseFid = (unsigned short)lparmp[1];
+    desiredAccess = lparmp[2];
 #ifdef DEBUG_VERBOSE
     allocSize = lparmp[3];
 #endif /* DEBUG_VERSOSE */
-       extAttributes = lparmp[5];
+    extAttributes = lparmp[5];
 #ifdef DEBUG_VEROSE
     shareAccess = lparmp[6];
 #endif
-       createDisp = lparmp[7];
-       createOptions = lparmp[8];
+    createDisp = lparmp[7];
+    createOptions = lparmp[8];
 #ifdef DEBUG_VERBOSE
     sdLen = lparmp[9];
 #endif
-       nameLength = lparmp[11];
+    nameLength = lparmp[11];
 
 #ifdef DEBUG_VERBOSE
-       osi_Log4(smb_logp,"NTTransCreate with da[%x],ea[%x],sa[%x],cd[%x]",desiredAccess,extAttributes,shareAccess,createDisp);
-       osi_Log2(smb_logp,"... co[%x],sdl[%x],as[%x]",createOptions,sdLen,allocSize);
-       osi_Log1(smb_logp,"... flags[%x]",flags);
+    osi_Log4(smb_logp,"NTTranCreate with da[%x],ea[%x],sa[%x],cd[%x]",desiredAccess,extAttributes,shareAccess,createDisp);
+    osi_Log3(smb_logp,"... co[%x],sdl[%x],as[%x]",createOptions,sdLen,allocSize);
+    osi_Log1(smb_logp,"... flags[%x]",flags);
 #endif
 
-       /* mustBeDir is never set; createOptions directory bit seems to be
-         * more important
-        */
-       if (createOptions & 1)
-               realDirFlag = 1;
-       else if (createOptions & 0x40)
-               realDirFlag = 0;
-       else
-               realDirFlag = -1;
-
-       /*
-        * compute initial mode bits based on read-only flag in
-        * extended attributes
-        */
-       initialModeBits = 0666;
-       if (extAttributes & 1) initialModeBits &= ~0222;
-
-       pathp = parmp + (13 * sizeof(ULONG)) + sizeof(UCHAR);
-       /* Sometimes path is not null-terminated, so we make a copy. */
-       realPathp = malloc(nameLength+1);
-       memcpy(realPathp, pathp, nameLength);
-       realPathp[nameLength] = 0;
-
-       spacep = cm_GetSpace();
-       smb_StripLastComponent(spacep->data, &lastNamep, realPathp);
+    /* mustBeDir is never set; createOptions directory bit seems to be
+     * more important
+     */
+    if (createOptions & 1)
+        realDirFlag = 1;
+    else if (createOptions & 0x40)
+        realDirFlag = 0;
+    else
+        realDirFlag = -1;
 
-       /*
-        * Nothing here to handle SMB_IOCTL_FILENAME.
-        * Will add it if necessary.
-        */
+    /*
+     * compute initial mode bits based on read-only flag in
+     * extended attributes
+     */
+    initialModeBits = 0666;
+    if (extAttributes & 1) 
+        initialModeBits &= ~0222;
+
+    pathp = parmp + (13 * sizeof(ULONG)) + sizeof(UCHAR);
+    /* Sometimes path is not null-terminated, so we make a copy. */
+    realPathp = malloc(nameLength+1);
+    memcpy(realPathp, pathp, nameLength);
+    realPathp[nameLength] = 0;
+    if (smb_StoreAnsiFilenames)
+        OemToChar(realPathp,realPathp);
+
+    spacep = cm_GetSpace();
+    smb_StripLastComponent(spacep->data, &lastNamep, realPathp);
+
+    /*
+     * Nothing here to handle SMB_IOCTL_FILENAME.
+     * Will add it if necessary.
+     */
 
 #ifdef DEBUG_VERBOSE
-       {
-               char *hexp, *asciip;
-               asciip = (lastNamep? lastNamep : realPathp);
-               hexp = osi_HexifyString( asciip );
-               DEBUG_EVENT2("AFS", "NTTranCreate H[%s] A[%s]", hexp, asciip);
-               free(hexp);
-       }
+    {
+        char *hexp, *asciip;
+        asciip = (lastNamep? lastNamep : realPathp);
+        hexp = osi_HexifyString( asciip );
+        DEBUG_EVENT2("AFS", "NTTranCreate H[%s] A[%s]", hexp, asciip);
+        free(hexp);
+    }
 #endif
 
-       userp = smb_GetUser(vcp, inp);
-    if(!userp) {
+    userp = smb_GetUser(vcp, inp);
+    if (!userp) {
        osi_Log1(smb_logp, "NTTranCreate invalid user [%d]", ((smb_t *) inp)->uid);
        free(realPathp);
        return CM_ERROR_INVAL;
     }
 
-       if (baseFid == 0) {
-               baseDirp = cm_rootSCachep;
-               tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
-       }
-       else {
+    if (baseFid == 0) {
+        baseDirp = cm_rootSCachep;
+        code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
+        if(code == CM_ERROR_TIDIPC) {
+            /* Attempt to use TID allocated for IPC.  The client is
+             * probably trying to locate DCE RPC endpoints, which we
+             * don't support. */
+            osi_Log0(smb_logp, "NTTranCreate received IPC TID");
+            free(realPathp);
+            cm_ReleaseUser(userp);
+            return CM_ERROR_NOSUCHPATH;
+        }
+    }
+    else {
         baseFidp = smb_FindFID(vcp, baseFid, 0);
-        if(!baseFidp) {
+        if (!baseFidp) {
                osi_Log1(smb_logp, "NTTranCreate Invalid fid [%d]", baseFid);
-               free(realPathp);
-               cm_ReleaseUser(userp);
-               return CM_ERROR_INVAL;
-        }
-               baseDirp = baseFidp->scp;
-               tidPathp = NULL;
-       }
+            free(realPathp);
+            cm_ReleaseUser(userp);
+            return CM_ERROR_INVAL;
+        }       
+        baseDirp = baseFidp->scp;
+        tidPathp = NULL;
+    }
 
     /* compute open mode */
     fidflags = 0;
@@ -3739,188 +5587,275 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     if (desiredAccess & AFS_ACCESS_WRITE)
         fidflags |= SMB_FID_OPENWRITE;
 
-       dscp = NULL;
-       code = 0;
-       code = cm_NameI(baseDirp, realPathp, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
-                       userp, tidPathp, &req, &scp);
-       if (code == 0) foundscp = TRUE;
-       if (code != 0
-           || (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE))) {
-               /* look up parent directory */
-               code = cm_NameI(baseDirp, spacep->data,
-                               CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
-                               userp, tidPathp, &req, &dscp);
-               cm_FreeSpace(spacep);
-
-               if (baseFid != 0) {
-           smb_ReleaseFID(baseFidp);
-           baseFidp = 0;
+    dscp = NULL;
+    code = 0;
+    if ( createDisp == FILE_OPEN || 
+         createDisp == FILE_OVERWRITE ||
+         createDisp == FILE_OVERWRITE_IF) {
+        code = cm_NameI(baseDirp, spacep->data, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                        userp, tidPathp, &req, &dscp);
+        if (code == 0) {
+            code = cm_Lookup(dscp, (lastNamep)?(lastNamep+1):realPathp, CM_FLAG_FOLLOW,
+                             userp, &req, &scp);
+            if (code == CM_ERROR_NOSUCHFILE) {
+                code = cm_Lookup(dscp, (lastNamep)?(lastNamep+1):realPathp, 
+                                 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, &req, &scp);
+                if (code == 0 && realDirFlag == 1) {
+                    cm_ReleaseSCache(scp);
+                    cm_ReleaseSCache(dscp);
+                    cm_ReleaseUser(userp);
+                    free(realPathp);
+                    return CM_ERROR_EXISTS;
+                }
+            }
+        } else 
+            dscp = NULL;
+    } else {
+        code = cm_NameI(baseDirp, realPathp, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                        userp, tidPathp, &req, &scp);
+    }
+
+    if (code == 0) 
+        foundscp = TRUE;
+    if (code != 0
+         || (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE))) {
+        /* look up parent directory */
+        if ( !dscp ) {
+            code = cm_NameI(baseDirp, spacep->data,
+                             CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                             userp, tidPathp, &req, &dscp);
+        } else
+            code = 0;
+        
+        cm_FreeSpace(spacep);
+
+        if (baseFid != 0) {
+            smb_ReleaseFID(baseFidp);
+            baseFidp = 0;
         }
 
-               if (code) {
-                       cm_ReleaseUser(userp);
-                       free(realPathp);
-                       return code;
-               }
+        if (code) {
+            cm_ReleaseUser(userp);
+            free(realPathp);
+            return code;
+        }
 
-               if (!lastNamep) lastNamep = realPathp;
-               else lastNamep++;
+        if (!lastNamep) lastNamep = realPathp;
+        else lastNamep++;
 
         if (!smb_IsLegalFilename(lastNamep))
             return CM_ERROR_BADNTFILENAME;
 
-               if (!foundscp) {
-                       code = cm_Lookup(dscp, lastNamep,
-                             CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
-                             userp, &req, &scp);
-                       if (code && code != CM_ERROR_NOSUCHFILE) {
-                               cm_ReleaseSCache(dscp);
-                               cm_ReleaseUser(userp);
-                               free(realPathp);
-                               return code;
-                       }
-               }
-       }
-       else {
-               if (baseFid != 0) {
+        if (!foundscp) {
+            if (createDisp == FILE_CREATE || createDisp == FILE_OVERWRITE_IF) {
+                code = cm_Lookup(dscp, lastNamep,
+                                  CM_FLAG_FOLLOW, userp, &req, &scp);
+            } else {
+                code = cm_Lookup(dscp, lastNamep,
+                                 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                                 userp, &req, &scp);
+            }
+            if (code && code != CM_ERROR_NOSUCHFILE) {
+                cm_ReleaseSCache(dscp);
+                cm_ReleaseUser(userp);
+                free(realPathp);
+                return code;
+            }
+        }
+    }
+    else {
+        if (baseFid != 0) {
             smb_ReleaseFID(baseFidp);
             baseFidp = 0;
         }
-               cm_FreeSpace(spacep);
-       }
+        cm_FreeSpace(spacep);
+    }
 
-       /* if we get here, if code is 0, the file exists and is represented by
-        * scp.  Otherwise, we have to create it.  The dir may be represented
-        * by dscp, or we may have found the file directly.  If code is non-zero,
-        * scp is NULL.
-        */
-       if (code == 0) {
-               code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp,
-                                     &req);
-               if (code) {
-                       if (dscp) cm_ReleaseSCache(dscp);
-                       cm_ReleaseSCache(scp);
-                       cm_ReleaseUser(userp);
-                       free(realPathp);
-                       return code;
-               }
-
-               if (createDisp == 2) {
-                       /* oops, file shouldn't be there */
-                       if (dscp) cm_ReleaseSCache(dscp);
-                       cm_ReleaseSCache(scp);
-                       cm_ReleaseUser(userp);
-                       free(realPathp);
-                       return CM_ERROR_EXISTS;
-               }
-
-               if (createDisp == 4
-                   || createDisp == 5) {
-                       setAttr.mask = CM_ATTRMASK_LENGTH;
-                       setAttr.length.LowPart = 0;
-                       setAttr.length.HighPart = 0;
-                       code = cm_SetAttr(scp, &setAttr, userp, &req);
-                       openAction = 3; /* truncated existing file */
-               }
-               else openAction = 1;    /* found existing file */
-       }
-       else if (createDisp == 1 || createDisp == 4) {
-               /* don't create if not found */
-               if (dscp) cm_ReleaseSCache(dscp);
-               cm_ReleaseUser(userp);
-               free(realPathp);
-               return CM_ERROR_NOSUCHFILE;
-       }
-       else if (realDirFlag == 0 || realDirFlag == -1) {
-               osi_assert(dscp != NULL);
-               osi_Log1(smb_logp, "smb_ReceiveNTTranCreate creating file %s",
-                 osi_LogSaveString(smb_logp, lastNamep));
-               openAction = 2;         /* created file */
-               setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
-               setAttr.clientModTime = time(NULL);
-               code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
-                                &req);
-               if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-                       smb_NotifyChange(FILE_ACTION_ADDED,
-                                        FILE_NOTIFY_CHANGE_FILE_NAME,
-                                        dscp, lastNamep, NULL, TRUE);
-               if (code == CM_ERROR_EXISTS && createDisp != 2) {
-                       /* Not an exclusive create, and someone else tried
-                        * creating it already, then we open it anyway.  We
-                        * don't bother retrying after this, since if this next
-                        * fails, that means that the file was deleted after we
-                        * started this call.
-                        */
-                       code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
-                                        userp, &req, &scp);
-                       if (code == 0) {
-                               if (createDisp == 5) {
-                                       setAttr.mask = CM_ATTRMASK_LENGTH;
-                                       setAttr.length.LowPart = 0;
-                                       setAttr.length.HighPart = 0;
-                                       code = cm_SetAttr(scp, &setAttr, userp,
-                                                         &req);
-                               }
-                       }       /* lookup succeeded */
-               }
-       }
-       else {
-               /* create directory */
-               osi_assert(dscp != NULL);
-               osi_Log1(smb_logp,
-                               "smb_ReceiveNTTranCreate creating directory %s",
-                               osi_LogSaveString(smb_logp, lastNamep));
-               openAction = 2;         /* created directory */
-               setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
-               setAttr.clientModTime = time(NULL);
-               code = cm_MakeDir(dscp, lastNamep, 0, &setAttr, userp, &req);
-               if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-                       smb_NotifyChange(FILE_ACTION_ADDED,
-                                        FILE_NOTIFY_CHANGE_DIR_NAME,
-                                        dscp, lastNamep, NULL, TRUE);
-               if (code == 0
-                   || (code == CM_ERROR_EXISTS && createDisp != 2)) {
-                       /* Not an exclusive create, and someone else tried
-                        * creating it already, then we open it anyway.  We
-                        * don't bother retrying after this, since if this next
-                        * fails, that means that the file was deleted after we
-                        * started this call.
-                        */
-                       code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
-                                        userp, &req, &scp);
-               }
-       }
+    /* if we get here, if code is 0, the file exists and is represented by
+     * scp.  Otherwise, we have to create it.  The dir may be represented
+     * by dscp, or we may have found the file directly.  If code is non-zero,
+     * scp is NULL.
+     */
+    if (code == 0) {
+        code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp,
+                               &req);
+        if (code) {     
+            if (dscp) cm_ReleaseSCache(dscp);
+            cm_ReleaseSCache(scp);
+            cm_ReleaseUser(userp);
+            free(realPathp);
+            return code;
+        }
 
-       if (code) {
-               /* something went wrong creating or truncating the file */
-               if (scp) cm_ReleaseSCache(scp);
-               cm_ReleaseUser(userp);
-               free(realPathp);
-               return code;
-       }
+        if (createDisp == FILE_CREATE) {
+            /* oops, file shouldn't be there */
+            if (dscp) cm_ReleaseSCache(dscp);
+            cm_ReleaseSCache(scp);
+            cm_ReleaseUser(userp);
+            free(realPathp);
+            return CM_ERROR_EXISTS;
+        }
 
-       /* make sure we have file vs. dir right */
-       if (realDirFlag == 0 && scp->fileType != CM_SCACHETYPE_FILE) {
-               cm_ReleaseSCache(scp);
-               cm_ReleaseUser(userp);
-               free(realPathp);
-               return CM_ERROR_ISDIR;
-       }
-       if (realDirFlag == 1 && scp->fileType == CM_SCACHETYPE_FILE) {
-               cm_ReleaseSCache(scp);
-               cm_ReleaseUser(userp);
-               free(realPathp);
-               return CM_ERROR_NOTDIR;
-       }
+        if (createDisp == FILE_OVERWRITE ||
+            createDisp == FILE_OVERWRITE_IF) {
+            setAttr.mask = CM_ATTRMASK_LENGTH;
+            setAttr.length.LowPart = 0;
+            setAttr.length.HighPart = 0;
+
+            /* now watch for a symlink */
+            code = 0;
+            while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
+                targetScp = 0;
+                code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
+                if (code == 0) {
+                    /* we have a more accurate file to use (the
+                    * target of the symbolic link).  Otherwise,
+                    * we'll just use the symlink anyway.
+                    */
+                    osi_Log2(smb_logp, "symlink vp %x to vp %x",
+                              scp, targetScp);
+                    cm_ReleaseSCache(scp);
+                    scp = targetScp;
+                }
+            }
+            code = cm_SetAttr(scp, &setAttr, userp, &req);
+            openAction = 3;    /* truncated existing file */
+        }
+        else openAction = 1;   /* found existing file */
+    }
+    else if (createDisp == FILE_OPEN || createDisp == FILE_OVERWRITE) {
+        /* don't create if not found */
+        if (dscp) cm_ReleaseSCache(dscp);
+        cm_ReleaseUser(userp);
+        free(realPathp);
+        return CM_ERROR_NOSUCHFILE;
+    }
+    else if (realDirFlag == 0 || realDirFlag == -1) {
+        osi_assert(dscp != NULL);
+        osi_Log1(smb_logp, "smb_ReceiveNTTranCreate creating file %s",
+                  osi_LogSaveString(smb_logp, lastNamep));
+        openAction = 2;                /* created file */
+        setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
+        setAttr.clientModTime = time(NULL);
+        code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
+                          &req);
+        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
+            smb_NotifyChange(FILE_ACTION_ADDED,
+                              FILE_NOTIFY_CHANGE_FILE_NAME,
+                              dscp, lastNamep, NULL, TRUE);
+        if (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE) {
+            /* Not an exclusive create, and someone else tried
+             * creating it already, then we open it anyway.  We
+             * don't bother retrying after this, since if this next
+             * fails, that means that the file was deleted after we
+             * started this call.
+             */
+            code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
+                              userp, &req, &scp);
+            if (code == 0) {
+                if (createDisp == FILE_OVERWRITE_IF) {
+                    setAttr.mask = CM_ATTRMASK_LENGTH;
+                    setAttr.length.LowPart = 0;
+                    setAttr.length.HighPart = 0;
+
+                    /* now watch for a symlink */
+                    code = 0;
+                    while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
+                        targetScp = 0;
+                        code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
+                        if (code == 0) {
+                            /* we have a more accurate file to use (the
+                            * target of the symbolic link).  Otherwise,
+                            * we'll just use the symlink anyway.
+                            */
+                            osi_Log2(smb_logp, "symlink vp %x to vp %x",
+                                      scp, targetScp);
+                            cm_ReleaseSCache(scp);
+                            scp = targetScp;
+                        }
+                    }
+                    code = cm_SetAttr(scp, &setAttr, userp, &req);
+                }       
+            }  /* lookup succeeded */
+        }
+    }
+    else {
+        /* create directory */
+        osi_assert(dscp != NULL);
+        osi_Log1(smb_logp,
+                  "smb_ReceiveNTTranCreate creating directory %s",
+                  osi_LogSaveString(smb_logp, lastNamep));
+        openAction = 2;                /* created directory */
+        setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
+        setAttr.clientModTime = time(NULL);
+        code = cm_MakeDir(dscp, lastNamep, 0, &setAttr, userp, &req);
+        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
+            smb_NotifyChange(FILE_ACTION_ADDED,
+                              FILE_NOTIFY_CHANGE_DIR_NAME,
+                              dscp, lastNamep, NULL, TRUE);
+        if (code == 0 ||
+            (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE)) {
+            /* Not an exclusive create, and someone else tried
+             * creating it already, then we open it anyway.  We
+             * don't bother retrying after this, since if this next
+             * fails, that means that the file was deleted after we
+             * started this call.
+             */
+            code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
+                              userp, &req, &scp);
+        }       
+    }
+
+    if (code) {
+        /* something went wrong creating or truncating the file */
+        if (scp) cm_ReleaseSCache(scp);
+        cm_ReleaseUser(userp);
+        free(realPathp);
+        return code;
+    }
+
+    /* make sure we have file vs. dir right */
+    if (realDirFlag == 0 && scp->fileType != CM_SCACHETYPE_FILE) {
+        /* now watch for a symlink */
+        code = 0;
+        while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
+            targetScp = 0;
+            code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
+            if (code == 0) {
+                /* we have a more accurate file to use (the
+                * target of the symbolic link).  Otherwise,
+                * we'll just use the symlink anyway.
+                */
+                osi_Log2(smb_logp, "symlink vp %x to vp %x",
+                          scp, targetScp);
+                cm_ReleaseSCache(scp);
+                scp = targetScp;
+            }
+        }
+
+        if (scp->fileType != CM_SCACHETYPE_FILE) {
+            cm_ReleaseSCache(scp);
+            cm_ReleaseUser(userp);
+            free(realPathp);
+            return CM_ERROR_ISDIR;
+        }
+    }
+
+    if (realDirFlag == 1 && scp->fileType == CM_SCACHETYPE_FILE) {
+        cm_ReleaseSCache(scp);
+        cm_ReleaseUser(userp);
+        free(realPathp);
+        return CM_ERROR_NOTDIR;
+    }
 
-       /* open the file itself */
-       fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
-       osi_assert(fidp);
+    /* open the file itself */
+    fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
+    osi_assert(fidp);
 
-       /* save a pointer to the vnode */
-       fidp->scp = scp;
+    /* save a pointer to the vnode */
+    fidp->scp = scp;
 
-       fidp->flags = fidflags;
+    fidp->flags = fidflags;
 
     /* save parent dir and pathname for deletion or change notification */
     if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
@@ -3929,15 +5864,15 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
         cm_HoldSCache(dscp);
         fidp->NTopen_pathp = strdup(lastNamep);
     }
-       fidp->NTopen_wholepathp = realPathp;
+    fidp->NTopen_wholepathp = realPathp;
 
-       /* we don't need this any longer */
-       if (dscp) cm_ReleaseSCache(dscp);
+    /* we don't need this any longer */
+    if (dscp) cm_ReleaseSCache(dscp);
 
-       cm_Open(scp, 0, userp);
+    cm_Open(scp, 0, userp);
 
-       /* set inp->fid so that later read calls in same msg can find fid */
-       inp->fid = fidp->fid;
+    /* set inp->fid so that later read calls in same msg can find fid */
+    inp->fid = fidp->fid;
 
     /* check whether we are required to send an extended response */
     if (!extendedRespRequired) {
@@ -4041,29 +5976,29 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
         lock_ReleaseMutex(&scp->mx);
     }
 
-       osi_Log1(smb_logp, "SMB NTTranCreate opening fid %d", fidp->fid);
+    osi_Log1(smb_logp, "SMB NTTranCreate opening fid %d", fidp->fid);
 
-       smb_ReleaseFID(fidp);
+    smb_ReleaseFID(fidp);
 
-       cm_ReleaseUser(userp);
+    cm_ReleaseUser(userp);
 
-       /* free(realPathp); Can't free realPathp here because fidp->NTopen_wholepathp points there */
-       /* leave scp held since we put it in fidp->scp */
-       return 0;
+    /* free(realPathp); Can't free realPathp here because fidp->NTopen_wholepathp points there */
+    /* leave scp held since we put it in fidp->scp */
+    return 0;
 }
 
 long smb_ReceiveNTTranNotifyChange(smb_vc_t *vcp, smb_packet_t *inp,
        smb_packet_t *outp)
 {
-       smb_packet_t *savedPacketp;
-       ULONG filter; USHORT fid, watchtree;
-       smb_fid_t *fidp;
-       cm_scache_t *scp;
+    smb_packet_t *savedPacketp;
+    ULONG filter; USHORT fid, watchtree;
+    smb_fid_t *fidp;
+    cm_scache_t *scp;
         
-       filter = smb_GetSMBParm(inp, 19)
-                       | (smb_GetSMBParm(inp, 20) << 16);
-       fid = smb_GetSMBParm(inp, 21);
-       watchtree = smb_GetSMBParm(inp, 22) && 0xffff;  /* TODO: should this be 0xff ? */
+    filter = smb_GetSMBParm(inp, 19) |
+             (smb_GetSMBParm(inp, 20) << 16);
+    fid = smb_GetSMBParm(inp, 21);
+    watchtree = smb_GetSMBParm(inp, 22) && 0xffff;  /* TODO: should this be 0xff ? */
 
     fidp = smb_FindFID(vcp, fid, 0);
     if (!fidp) {
@@ -4071,12 +6006,13 @@ long smb_ReceiveNTTranNotifyChange(smb_vc_t *vcp, smb_packet_t *inp,
         return CM_ERROR_BADFD;
     }
 
-       savedPacketp = smb_CopyPacket(inp);
-       savedPacketp->vcp = vcp; /* TODO: refcount vcp? */
-       lock_ObtainMutex(&smb_Dir_Watch_Lock);
-       savedPacketp->nextp = smb_Directory_Watches;
-       smb_Directory_Watches = savedPacketp;
-       lock_ReleaseMutex(&smb_Dir_Watch_Lock);
+    savedPacketp = smb_CopyPacket(inp);
+    smb_HoldVC(vcp);
+    savedPacketp->vcp = vcp;
+    lock_ObtainMutex(&smb_Dir_Watch_Lock);
+    savedPacketp->nextp = smb_Directory_Watches;
+    smb_Directory_Watches = savedPacketp;
+    lock_ReleaseMutex(&smb_Dir_Watch_Lock);
 
     osi_Log4(smb_logp, "Request for NotifyChange filter 0x%x fid %d wtree %d file %s",
              filter, fid, watchtree, osi_LogSaveString(smb_logp, fidp->NTopen_wholepathp));
@@ -4090,115 +6026,115 @@ long smb_ReceiveNTTranNotifyChange(smb_vc_t *vcp, smb_packet_t *inp,
     lock_ReleaseMutex(&scp->mx);
     smb_ReleaseFID(fidp);
 
-       outp->flags |= SMB_PACKETFLAG_NOSEND;
-       return 0;
+    outp->flags |= SMB_PACKETFLAG_NOSEND;
+    return 0;
 }
 
 unsigned char nullSecurityDesc[36] = {
-       0x01,                           /* security descriptor revision */
-       0x00,                           /* reserved, should be zero */
-       0x00, 0x80,                     /* security descriptor control;
-                                        * 0x8000 : self-relative format */
-       0x14, 0x00, 0x00, 0x00,         /* offset of owner SID */
-       0x1c, 0x00, 0x00, 0x00,         /* offset of group SID */
-       0x00, 0x00, 0x00, 0x00,         /* offset of DACL would go here */
-       0x00, 0x00, 0x00, 0x00,         /* offset of SACL would go here */
-       0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                                       /* "null SID" owner SID */
-       0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-                                       /* "null SID" group SID */
-};
+    0x01,                              /* security descriptor revision */
+    0x00,                              /* reserved, should be zero */
+    0x00, 0x80,                                /* security descriptor control;
+                                         * 0x8000 : self-relative format */
+    0x14, 0x00, 0x00, 0x00,            /* offset of owner SID */
+    0x1c, 0x00, 0x00, 0x00,            /* offset of group SID */
+    0x00, 0x00, 0x00, 0x00,            /* offset of DACL would go here */
+    0x00, 0x00, 0x00, 0x00,            /* offset of SACL would go here */
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                        /* "null SID" owner SID */
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+                                        /* "null SID" group SID */
+};      
 
 long smb_ReceiveNTTranQuerySecurityDesc(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       int parmOffset, parmCount, dataOffset, dataCount;
-       int parmSlot;
-       int maxData;
-       char *outData;
-       char *parmp;
-       USHORT *sparmp;
-       ULONG *lparmp;
-       USHORT fid;
-       ULONG securityInformation;
-
-       parmOffset = smb_GetSMBOffsetParm(inp, 11, 1)
-                       | (smb_GetSMBOffsetParm(inp, 12, 1) << 16);
-       parmp = inp->data + parmOffset;
-       sparmp = (USHORT *) parmp;
-       lparmp = (ULONG *) parmp;
-
-       fid = sparmp[0];
-       securityInformation = lparmp[1];
-
-       maxData = smb_GetSMBOffsetParm(inp, 7, 1)
-                       | (smb_GetSMBOffsetParm(inp, 8, 1) << 16);
-
-       if (maxData < 36)
-               dataCount = 0;
-       else
-               dataCount = 36;
-
-       /* out parms */
-       parmOffset = 8*4 + 39;
-       parmOffset += 1;        /* pad to 4 */
-       parmCount = 4;
-       dataOffset = parmOffset + parmCount;
-
-       parmSlot = 1;
-       outp->oddByte = 1;
-       /* Total Parameter Count */
-       smb_SetSMBParmLong(outp, parmSlot, parmCount); parmSlot += 2;
-       /* Total Data Count */
-       smb_SetSMBParmLong(outp, parmSlot, dataCount); parmSlot += 2;
-       /* Parameter Count */
-       smb_SetSMBParmLong(outp, parmSlot, parmCount); parmSlot += 2;
-       /* Parameter Offset */
-       smb_SetSMBParmLong(outp, parmSlot, parmOffset); parmSlot += 2;
-       /* Parameter Displacement */
-       smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
-       /* Data Count */
-       smb_SetSMBParmLong(outp, parmSlot, dataCount); parmSlot += 2;
-       /* Data Offset */
-       smb_SetSMBParmLong(outp, parmSlot, dataOffset); parmSlot += 2;
-       /* Data Displacement */
-       smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
-       smb_SetSMBParmByte(outp, parmSlot, 0);  /* Setup Count */
-       smb_SetSMBDataLength(outp, 1 + parmCount + dataCount);
-
-       outData = smb_GetSMBData(outp, NULL);
-       outData++;                      /* round to get to parmOffset */
-       *((ULONG *)outData) = 36; outData += 4; /* length */
-
-       if (maxData >= 36) {
-               memcpy(outData, nullSecurityDesc, 36);
-               outData += 36;
-               return 0;
-       } else
-               return CM_ERROR_BUFFERTOOSMALL;
+    int parmOffset, parmCount, dataOffset, dataCount;
+    int parmSlot;
+    int maxData;
+    char *outData;
+    char *parmp;
+    USHORT *sparmp;
+    ULONG *lparmp;
+    USHORT fid;
+    ULONG securityInformation;
+
+    parmOffset = smb_GetSMBOffsetParm(inp, 11, 1)
+        | (smb_GetSMBOffsetParm(inp, 12, 1) << 16);
+    parmp = inp->data + parmOffset;
+    sparmp = (USHORT *) parmp;
+    lparmp = (ULONG *) parmp;
+
+    fid = sparmp[0];
+    securityInformation = lparmp[1];
+
+    maxData = smb_GetSMBOffsetParm(inp, 7, 1)
+        | (smb_GetSMBOffsetParm(inp, 8, 1) << 16);
+
+    if (maxData < 36)
+        dataCount = 0;
+    else
+        dataCount = 36;
+
+    /* out parms */
+    parmOffset = 8*4 + 39;
+    parmOffset += 1;   /* pad to 4 */
+    parmCount = 4;
+    dataOffset = parmOffset + parmCount;
+
+    parmSlot = 1;
+    outp->oddByte = 1;
+    /* Total Parameter Count */
+    smb_SetSMBParmLong(outp, parmSlot, parmCount); parmSlot += 2;
+    /* Total Data Count */
+    smb_SetSMBParmLong(outp, parmSlot, dataCount); parmSlot += 2;
+    /* Parameter Count */
+    smb_SetSMBParmLong(outp, parmSlot, parmCount); parmSlot += 2;
+    /* Parameter Offset */
+    smb_SetSMBParmLong(outp, parmSlot, parmOffset); parmSlot += 2;
+    /* Parameter Displacement */
+    smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
+    /* Data Count */
+    smb_SetSMBParmLong(outp, parmSlot, dataCount); parmSlot += 2;
+    /* Data Offset */
+    smb_SetSMBParmLong(outp, parmSlot, dataOffset); parmSlot += 2;
+    /* Data Displacement */
+    smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
+    smb_SetSMBParmByte(outp, parmSlot, 0);     /* Setup Count */
+    smb_SetSMBDataLength(outp, 1 + parmCount + dataCount);
+
+    outData = smb_GetSMBData(outp, NULL);
+    outData++;                 /* round to get to parmOffset */
+    *((ULONG *)outData) = 36; outData += 4;    /* length */
+
+    if (maxData >= 36) {
+        memcpy(outData, nullSecurityDesc, 36);
+        outData += 36;
+        return 0;
+    } else
+        return CM_ERROR_BUFFERTOOSMALL;
 }
 
 long smb_ReceiveNTTransact(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       unsigned short function;
+    unsigned short function;
 
-       function = smb_GetSMBParm(inp, 18);
+    function = smb_GetSMBParm(inp, 18);
 
-       osi_Log1(smb_logp, "SMB NT Transact function %d", function);
+    osi_Log1(smb_logp, "SMB NT Transact function %d", function);
 
-       /* We can handle long names */
-       if (vcp->flags & SMB_VCFLAG_USENT)
-               ((smb_t *)outp)->flg2 |= 0x40;  /* IS_LONG_NAME */
+    /* We can handle long names */
+    if (vcp->flags & SMB_VCFLAG_USENT)
+        ((smb_t *)outp)->flg2 |= SMB_FLAGS2_IS_LONG_NAME;
         
-       switch (function) {
-
-               case 6: return smb_ReceiveNTTranQuerySecurityDesc(vcp, inp, outp);
-
-               case 4: return smb_ReceiveNTTranNotifyChange(vcp, inp, outp);
-
-               case 1: return smb_ReceiveNTTranCreate(vcp, inp, outp);
-
-               default: return CM_ERROR_INVAL;
-       }
+    switch (function) {
+    case 6: 
+        return smb_ReceiveNTTranQuerySecurityDesc(vcp, inp, outp);
+    case 4: 
+        return smb_ReceiveNTTranNotifyChange(vcp, inp, outp);
+    case 1: 
+        return smb_ReceiveNTTranCreate(vcp, inp, outp);
+    default: 
+        return CM_ERROR_INVAL;
+    }
 }
 
 /*
@@ -4212,201 +6148,219 @@ void smb_NotifyChange(DWORD action, DWORD notifyFilter,
        cm_scache_t *dscp, char *filename, char *otherFilename,
        BOOL isDirectParent)
 {
-       smb_packet_t *watch, *lastWatch, *nextWatch;
-       ULONG parmSlot, parmCount, parmOffset, dataOffset, nameLen;
-       char *outData, *oldOutData;
-       ULONG filter;
-       USHORT fid, wtree;
-       ULONG maxLen;
-       BOOL twoEntries = FALSE;
-       ULONG otherNameLen, oldParmCount = 0;
-       DWORD otherAction;
-       smb_vc_t *vcp;
-       smb_fid_t *fidp;
-
-       /* Get ready for rename within directory */
-       if (action == FILE_ACTION_RENAMED_OLD_NAME && otherFilename != NULL) {
-               twoEntries = TRUE;
-               otherAction = FILE_ACTION_RENAMED_NEW_NAME;
-       }
+    smb_packet_t *watch, *lastWatch, *nextWatch;
+    ULONG parmSlot, parmCount, parmOffset, dataOffset, nameLen;
+    char *outData, *oldOutData;
+    ULONG filter;
+    USHORT fid, wtree;
+    ULONG maxLen;
+    BOOL twoEntries = FALSE;
+    ULONG otherNameLen, oldParmCount = 0;
+    DWORD otherAction;
+    smb_vc_t *vcp;
+    smb_fid_t *fidp;
+
+    /* Get ready for rename within directory */
+    if (action == FILE_ACTION_RENAMED_OLD_NAME && otherFilename != NULL) {
+        twoEntries = TRUE;
+        otherAction = FILE_ACTION_RENAMED_NEW_NAME;
+    }
 
-       lock_ObtainMutex(&smb_Dir_Watch_Lock);
-       watch = smb_Directory_Watches;
-       while (watch) {
-               filter = smb_GetSMBParm(watch, 19)
-                               | (smb_GetSMBParm(watch, 20) << 16);
-               fid = smb_GetSMBParm(watch, 21);
-               wtree = smb_GetSMBParm(watch, 22) & 0xffff;  /* TODO: should this be 0xff ? */
-               maxLen = smb_GetSMBOffsetParm(watch, 5, 1)
-                               | (smb_GetSMBOffsetParm(watch, 6, 1) << 16);
-               vcp = watch->vcp;
-
-               /*
-                * Strange hack - bug in NT Client and NT Server that we
-                * must emulate?
-                */
-               if (filter == 3 && wtree)
-                       filter = 0x17;
-
-               fidp = smb_FindFID(vcp, fid, 0);
+    osi_Log2(smb_logp,"in smb_NotifyChange for file [%s] dscp [%x]",
+             osi_LogSaveString(smb_logp,filename),dscp);
+
+    lock_ObtainMutex(&smb_Dir_Watch_Lock);
+    watch = smb_Directory_Watches;
+    while (watch) {
+        filter = smb_GetSMBParm(watch, 19)
+            | (smb_GetSMBParm(watch, 20) << 16);
+        fid = smb_GetSMBParm(watch, 21);
+        wtree = smb_GetSMBParm(watch, 22) & 0xffff;  /* TODO: should this be 0xff ? */
+        maxLen = smb_GetSMBOffsetParm(watch, 5, 1)
+            | (smb_GetSMBOffsetParm(watch, 6, 1) << 16);
+        vcp = watch->vcp;
+
+        /*
+         * Strange hack - bug in NT Client and NT Server that we
+         * must emulate?
+         */
+        if (filter == 3 && wtree)
+            filter = 0x17;
+
+        fidp = smb_FindFID(vcp, fid, 0);
         if (!fidp) {
-               lastWatch = watch;
-               watch = watch->nextp;
-               continue;
+            osi_Log1(smb_logp," no fidp for fid[%d]",fid);
+            lastWatch = watch;
+            watch = watch->nextp;
+            continue;
+        }       
+        if (fidp->scp != dscp
+             || (filter & notifyFilter) == 0
+             || (!isDirectParent && !wtree)) {
+            osi_Log1(smb_logp," passing fidp->scp[%x]", fidp->scp);
+            smb_ReleaseFID(fidp);
+            lastWatch = watch;
+            watch = watch->nextp;
+            continue;
         }
-               if (fidp->scp != dscp
-                   || (filter & notifyFilter) == 0
-                   || (!isDirectParent && !wtree)) {
-                       smb_ReleaseFID(fidp);
-                       lastWatch = watch;
-                       watch = watch->nextp;
-                       continue;
-               }
-               smb_ReleaseFID(fidp);
-
-               osi_Log4(smb_logp,
-                        "Sending Change Notification for fid %d filter 0x%x wtree %d file %s",
-                        fid, filter, wtree, osi_LogSaveString(smb_logp, filename));
-
-               nextWatch = watch->nextp;
-               if (watch == smb_Directory_Watches)
-                       smb_Directory_Watches = nextWatch;
-               else
-                       lastWatch->nextp = nextWatch;
-
-               /* Turn off WATCHED flag in dscp */
-               lock_ObtainMutex(&dscp->mx);
-               if (wtree)
-                       dscp->flags &= ~CM_SCACHEFLAG_WATCHEDSUBTREE;
-               else
-                       dscp->flags &= ~CM_SCACHEFLAG_WATCHED;
-               lock_ReleaseMutex(&dscp->mx);
-
-               /* Convert to response packet */
-               ((smb_t *) watch)->reb = 0x80;
-               ((smb_t *) watch)->wct = 0;
-
-               /* out parms */
-               if (filename == NULL)
-                       parmCount = 0;
-               else {
-                       nameLen = strlen(filename);
-                       parmCount = 3*4 + nameLen*2;
-                       parmCount = (parmCount + 3) & ~3;       /* pad to 4 */
-                       if (twoEntries) {
-                               otherNameLen = strlen(otherFilename);
-                               oldParmCount = parmCount;
-                               parmCount += 3*4 + otherNameLen*2;
-                               parmCount = (parmCount + 3) & ~3; /* pad to 4 */
-                       }
-                       if (maxLen < parmCount)
-                               parmCount = 0;  /* not enough room */
-               }
-               parmOffset = 8*4 + 39;
-               parmOffset += 1;                        /* pad to 4 */
-               dataOffset = parmOffset + parmCount;
-
-               parmSlot = 1;
-               watch->oddByte = 1;
-               /* Total Parameter Count */
-               smb_SetSMBParmLong(watch, parmSlot, parmCount); parmSlot += 2;
-               /* Total Data Count */
-               smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
-               /* Parameter Count */
-               smb_SetSMBParmLong(watch, parmSlot, parmCount); parmSlot += 2;
-               /* Parameter Offset */
-               smb_SetSMBParmLong(watch, parmSlot, parmOffset); parmSlot += 2;
-               /* Parameter Displacement */
-               smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
-               /* Data Count */
-               smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
-               /* Data Offset */
-               smb_SetSMBParmLong(watch, parmSlot, dataOffset); parmSlot += 2;
-               /* Data Displacement */
-               smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
-               smb_SetSMBParmByte(watch, parmSlot, 0); /* Setup Count */
-               smb_SetSMBDataLength(watch, parmCount + 1);
-
-               if (parmCount != 0) {
-                       outData = smb_GetSMBData(watch, NULL);
-                       outData++;      /* round to get to parmOffset */
-                       oldOutData = outData;
-                       *((DWORD *)outData) = oldParmCount; outData += 4;
-                                       /* Next Entry Offset */
-                       *((DWORD *)outData) = action; outData += 4;
-                                       /* Action */
-                       *((DWORD *)outData) = nameLen*2; outData += 4;
-                                       /* File Name Length */
-                       mbstowcs((WCHAR *)outData, filename, nameLen);
-                                       /* File Name */
-                       if (twoEntries) {
-                               outData = oldOutData + oldParmCount;
-                               *((DWORD *)outData) = 0; outData += 4;
-                                       /* Next Entry Offset */
-                               *((DWORD *)outData) = otherAction; outData += 4;
-                                       /* Action */
-                               *((DWORD *)outData) = otherNameLen*2;
-                               outData += 4;   /* File Name Length */
-                               mbstowcs((WCHAR *)outData, otherFilename,
-                                        otherNameLen); /* File Name */
-                       }
-               }
-
-               /*
-                * If filename is null, we don't know the cause of the
-                * change notification.  We return zero data (see above),
-                * and set error code to NT_STATUS_NOTIFY_ENUM_DIR
-                * (= 0x010C).  We set the error code here by hand, without
-                * modifying wct and bcc.
-                */
-               if (filename == NULL) {
-                       ((smb_t *) watch)->rcls = 0x0C;
-                       ((smb_t *) watch)->reh = 0x01;
-                       ((smb_t *) watch)->errLow = 0;
-                       ((smb_t *) watch)->errHigh = 0;
-                       /* Set NT Status codes flag */
-                       ((smb_t *) watch)->flg2 |= 0x4000;
-               }
-
-               smb_SendPacket(vcp, watch);
-               smb_FreePacket(watch);
-               watch = nextWatch;
-       }
-       lock_ReleaseMutex(&smb_Dir_Watch_Lock);
-}
+        smb_ReleaseFID(fidp);
+
+        osi_Log4(smb_logp,
+                  "Sending Change Notification for fid %d filter 0x%x wtree %d file %s",
+                  fid, filter, wtree, osi_LogSaveString(smb_logp, filename));
+
+        nextWatch = watch->nextp;
+        if (watch == smb_Directory_Watches)
+            smb_Directory_Watches = nextWatch;
+        else
+            lastWatch->nextp = nextWatch;
+
+        /* Turn off WATCHED flag in dscp */
+        lock_ObtainMutex(&dscp->mx);
+        if (wtree)
+            dscp->flags &= ~CM_SCACHEFLAG_WATCHEDSUBTREE;
+        else
+            dscp->flags &= ~CM_SCACHEFLAG_WATCHED;
+        lock_ReleaseMutex(&dscp->mx);
+
+        /* Convert to response packet */
+        ((smb_t *) watch)->reb = 0x80;
+        ((smb_t *) watch)->wct = 0;
+
+        /* out parms */
+        if (filename == NULL)
+            parmCount = 0;
+        else {
+            nameLen = strlen(filename);
+            parmCount = 3*4 + nameLen*2;
+            parmCount = (parmCount + 3) & ~3;  /* pad to 4 */
+            if (twoEntries) {
+                otherNameLen = strlen(otherFilename);
+                oldParmCount = parmCount;
+                parmCount += 3*4 + otherNameLen*2;
+                parmCount = (parmCount + 3) & ~3; /* pad to 4 */
+            }
+            if (maxLen < parmCount)
+                parmCount = 0; /* not enough room */
+        }
+        parmOffset = 8*4 + 39;
+        parmOffset += 1;                       /* pad to 4 */
+        dataOffset = parmOffset + parmCount;
+
+        parmSlot = 1;
+        watch->oddByte = 1;
+        /* Total Parameter Count */
+        smb_SetSMBParmLong(watch, parmSlot, parmCount); parmSlot += 2;
+        /* Total Data Count */
+        smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
+        /* Parameter Count */
+        smb_SetSMBParmLong(watch, parmSlot, parmCount); parmSlot += 2;
+        /* Parameter Offset */
+        smb_SetSMBParmLong(watch, parmSlot, parmOffset); parmSlot += 2;
+        /* Parameter Displacement */
+        smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
+        /* Data Count */
+        smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
+        /* Data Offset */
+        smb_SetSMBParmLong(watch, parmSlot, dataOffset); parmSlot += 2;
+        /* Data Displacement */
+        smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
+        smb_SetSMBParmByte(watch, parmSlot, 0);        /* Setup Count */
+        smb_SetSMBDataLength(watch, parmCount + 1);
+
+        if (parmCount != 0) {
+            char * p;
+            outData = smb_GetSMBData(watch, NULL);
+            outData++; /* round to get to parmOffset */
+            oldOutData = outData;
+            *((DWORD *)outData) = oldParmCount; outData += 4;
+            /* Next Entry Offset */
+            *((DWORD *)outData) = action; outData += 4;
+            /* Action */
+            *((DWORD *)outData) = nameLen*2; outData += 4;
+            /* File Name Length */
+            p = strdup(filename);
+            if (smb_StoreAnsiFilenames)
+                CharToOem(p,p);
+            mbstowcs((WCHAR *)outData, p, nameLen);
+            free(p);
+            /* File Name */
+            if (twoEntries) {
+                outData = oldOutData + oldParmCount;
+                *((DWORD *)outData) = 0; outData += 4;
+                /* Next Entry Offset */
+                *((DWORD *)outData) = otherAction; outData += 4;
+                /* Action */
+                *((DWORD *)outData) = otherNameLen*2;
+                outData += 4;  /* File Name Length */
+                p = strdup(otherFilename);
+                if (smb_StoreAnsiFilenames)
+                    CharToOem(p,p);
+                mbstowcs((WCHAR *)outData, p, otherNameLen);   /* File Name */
+                free(p);
+            }       
+        }       
+
+        /*
+         * If filename is null, we don't know the cause of the
+         * change notification.  We return zero data (see above),
+         * and set error code to NT_STATUS_NOTIFY_ENUM_DIR
+         * (= 0x010C).  We set the error code here by hand, without
+         * modifying wct and bcc.
+         */
+        if (filename == NULL) {
+            ((smb_t *) watch)->rcls = 0x0C;
+            ((smb_t *) watch)->reh = 0x01;
+            ((smb_t *) watch)->errLow = 0;
+            ((smb_t *) watch)->errHigh = 0;
+            /* Set NT Status codes flag */
+            ((smb_t *) watch)->flg2 |= SMB_FLAGS2_ERR_STATUS;
+        }
+
+        smb_SendPacket(vcp, watch);
+        smb_ReleaseVC(vcp);
+        smb_FreePacket(watch);
+        watch = nextWatch;
+    }
+    lock_ReleaseMutex(&smb_Dir_Watch_Lock);
+}       
 
 long smb_ReceiveNTCancel(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
-       unsigned char *replyWctp;
-       smb_packet_t *watch, *lastWatch;
-       USHORT fid, watchtree;
-       smb_fid_t *fidp;
-       cm_scache_t *scp;
-
-       osi_Log0(smb_logp, "SMB3 receive NT cancel");
-
-       lock_ObtainMutex(&smb_Dir_Watch_Lock);
-       watch = smb_Directory_Watches;
-       while (watch) {
-               if (((smb_t *)watch)->uid == ((smb_t *)inp)->uid
-                   && ((smb_t *)watch)->pid == ((smb_t *)inp)->pid
-                   && ((smb_t *)watch)->mid == ((smb_t *)inp)->mid
-                   && ((smb_t *)watch)->tid == ((smb_t *)inp)->tid) {
-                       if (watch == smb_Directory_Watches)
-                               smb_Directory_Watches = watch->nextp;
-                       else
-                               lastWatch->nextp = watch->nextp;
-                       lock_ReleaseMutex(&smb_Dir_Watch_Lock);
-
-                       /* Turn off WATCHED flag in scp */
-                       fid = smb_GetSMBParm(watch, 21);
-                       watchtree = smb_GetSMBParm(watch, 22) & 0xffff;
-
-                       fidp = smb_FindFID(vcp, fid, 0);
+    unsigned char *replyWctp;
+    smb_packet_t *watch, *lastWatch;
+    USHORT fid, watchtree;
+    smb_fid_t *fidp;
+    cm_scache_t *scp;
+
+    osi_Log0(smb_logp, "SMB3 receive NT cancel");
+
+    lock_ObtainMutex(&smb_Dir_Watch_Lock);
+    watch = smb_Directory_Watches;
+    while (watch) {
+        if (((smb_t *)watch)->uid == ((smb_t *)inp)->uid
+             && ((smb_t *)watch)->pid == ((smb_t *)inp)->pid
+             && ((smb_t *)watch)->mid == ((smb_t *)inp)->mid
+             && ((smb_t *)watch)->tid == ((smb_t *)inp)->tid) {
+            if (watch == smb_Directory_Watches)
+                smb_Directory_Watches = watch->nextp;
+            else
+                lastWatch->nextp = watch->nextp;
+            lock_ReleaseMutex(&smb_Dir_Watch_Lock);
+
+            /* Turn off WATCHED flag in scp */
+            fid = smb_GetSMBParm(watch, 21);
+            watchtree = smb_GetSMBParm(watch, 22) & 0xffff;
+
+            if (vcp != watch->vcp)
+                osi_Log2(smb_logp, "smb_ReceiveNTCancel: vcp %x not equal to watch vcp %x", 
+                          vcp, watch->vcp);
+
+            fidp = smb_FindFID(vcp, fid, 0);
             if (fidp) {
                 osi_Log3(smb_logp, "Cancelling change notification for fid %d wtree %d file %s", 
-                         fid, watchtree,
-                         osi_LogSaveString(smb_logp, (fidp)?fidp->NTopen_wholepathp:""));
+                          fid, watchtree,
+                          osi_LogSaveString(smb_logp, (fidp)?fidp->NTopen_wholepathp:""));
 
                 scp = fidp->scp;
                 lock_ObtainMutex(&scp->mx);
@@ -4420,31 +6374,80 @@ long smb_ReceiveNTCancel(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                 osi_Log2(smb_logp,"NTCancel unable to resolve fid [%d] in vcp[%x]", fid,vcp);
             }
 
-                       /* assume STATUS32; return 0xC0000120 (CANCELED) */
-                       replyWctp = watch->wctp;
-                       *replyWctp++ = 0;
-                       *replyWctp++ = 0;
-                       *replyWctp++ = 0;
-                       ((smb_t *)watch)->rcls = 0x20;
-                       ((smb_t *)watch)->reh = 0x1;
-                       ((smb_t *)watch)->errLow = 0;
-                       ((smb_t *)watch)->errHigh = 0xC0;
-                       ((smb_t *)watch)->flg2 |= 0x4000;
-                       smb_SendPacket(vcp, watch);
-                       smb_FreePacket(watch);
-                       return 0;
-               }
-               lastWatch = watch;
-               watch = watch->nextp;
-       }
-       lock_ReleaseMutex(&smb_Dir_Watch_Lock);
+            /* assume STATUS32; return 0xC0000120 (CANCELED) */
+            replyWctp = watch->wctp;
+            *replyWctp++ = 0;
+            *replyWctp++ = 0;
+            *replyWctp++ = 0;
+            ((smb_t *)watch)->rcls = 0x20;
+            ((smb_t *)watch)->reh = 0x1;
+            ((smb_t *)watch)->errLow = 0;
+            ((smb_t *)watch)->errHigh = 0xC0;
+            ((smb_t *)watch)->flg2 |= SMB_FLAGS2_ERR_STATUS;
+            smb_SendPacket(vcp, watch);
+            if (watch->vcp)
+                smb_ReleaseVC(watch->vcp);
+            smb_FreePacket(watch);
+            return 0;
+        }
+        lastWatch = watch;
+        watch = watch->nextp;
+    }
+    lock_ReleaseMutex(&smb_Dir_Watch_Lock);
+
+    return 0;
+}
+
+/*
+ * NT rename also does hard links.
+ */
+
+#define RENAME_FLAG_MOVE_CLUSTER_INFORMATION 0x102
+#define RENAME_FLAG_HARD_LINK                0x103
+#define RENAME_FLAG_RENAME                   0x104
+#define RENAME_FLAG_COPY                     0x105
+
+long smb_ReceiveNTRename(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
+{
+    char *oldPathp, *newPathp;
+    long code = 0;
+    cm_user_t *userp;
+    char * tp;
+    int attrs;
+    int rename_type;
+
+    attrs = smb_GetSMBParm(inp, 0);
+    rename_type = smb_GetSMBParm(inp, 1);
+
+    if (rename_type != RENAME_FLAG_RENAME && rename_type != RENAME_FLAG_HARD_LINK) {
+        osi_Log1(smb_logp, "NTRename invalid infolevel [%x]", rename_type);
+        return CM_ERROR_NOACCESS;
+    }
 
-       return 0;
+    tp = smb_GetSMBData(inp, NULL);
+    oldPathp = smb_ParseASCIIBlock(tp, &tp);
+    if (smb_StoreAnsiFilenames)
+        OemToChar(oldPathp,oldPathp);
+    newPathp = smb_ParseASCIIBlock(tp, &tp);
+    if (smb_StoreAnsiFilenames)
+        OemToChar(newPathp,newPathp);
+
+    osi_Log3(smb_logp, "NTRename for [%s]->[%s] type [%s]",
+             osi_LogSaveString(smb_logp, oldPathp),
+             osi_LogSaveString(smb_logp, newPathp),
+             ((rename_type==RENAME_FLAG_RENAME)?"rename":"hardlink"));
+
+    if (rename_type == RENAME_FLAG_RENAME) {
+        code = smb_Rename(vcp,inp,oldPathp,newPathp,attrs);
+    } else { /* RENAME_FLAG_HARD_LINK */
+        code = smb_Link(vcp,inp,oldPathp,newPathp);
+    }
+    return code;
 }
 
 void smb3_Init()
 {
-       lock_InitializeMutex(&smb_Dir_Watch_Lock, "Directory Watch List Lock");
+    lock_InitializeMutex(&smb_Dir_Watch_Lock, "Directory Watch List Lock");
 }
 
 cm_user_t *smb_FindCMUserByName(/*smb_vc_t *vcp,*/ char *usern, char *machine)
@@ -4457,9 +6460,11 @@ cm_user_t *smb_FindCMUserByName(/*smb_vc_t *vcp,*/ char *usern, char *machine)
         lock_ObtainMutex(&unp->mx);
         unp->userp = cm_NewUser();
         lock_ReleaseMutex(&unp->mx);
-               osi_LogEvent("AFS smb_FindCMUserByName : New User",NULL,"name[%s] machine[%s]",usern,machine);
+               osi_Log2(smb_logp,"smb_FindCMUserByName New user name[%s] machine[%s]",osi_LogSaveString(smb_logp,usern),osi_LogSaveString(smb_logp,machine));
+        osi_LogEvent("AFS smb_FindCMUserByName : New User",NULL,"name[%s] machine[%s]",usern,machine);
     }  else    {
-               osi_LogEvent("AFS smb_FindCMUserByName : Found",NULL,"name[%s] machine[%s]",usern,machine);
+        osi_Log2(smb_logp,"smb_FindCMUserByName Not found name[%s] machine[%s]",osi_LogSaveString(smb_logp,usern),osi_LogSaveString(smb_logp,machine));
+        osi_LogEvent("AFS smb_FindCMUserByName : Found",NULL,"name[%s] machine[%s]",usern,machine);
        }
     return unp->userp;
 }