windows-byte-range-locks-20050816
[openafs.git] / src / WINNT / afsd / smb3.c
index 389449b..149c740 100644 (file)
@@ -121,7 +121,6 @@ unsigned char *smb_ParseString(unsigned char *inp, char **chainpp)
     return inp;
 }   
 
-/*DEBUG do not checkin*/
 void OutputDebugF(char * format, ...) {
     va_list args;
     int len;
@@ -174,9 +173,9 @@ void OutputDebugHexDump(unsigned char * buffer, int len) {
         OutputDebugString(buf);
     }   
 }
-/**/
 
 #define SMB_EXT_SEC_PACKAGE_NAME "Negotiate"
+
 void smb_NegotiateExtendedSecurity(void ** secBlob, int * secBlobLength) {
     SECURITY_STATUS status, istatus;
     CredHandle creds = {0,0};
@@ -201,7 +200,7 @@ void smb_NegotiateExtendedSecurity(void ** secBlob, int * secBlobLength) {
                                        &creds,
                                        &expiry);
 
-    if (status != SEC_E_OK) {       
+    if (status != SEC_E_OK) {
         /* Really bad. We return an empty security blob */
         OutputDebugF("AcquireCredentialsHandle failed with %lX", status);
         goto nes_0;
@@ -649,11 +648,12 @@ long smb_GetNormalizedUsername(char * usern, const char * accountName, const cha
     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.
+/* When using SMB auth, all SMB sessions have to pass through here
+ * first to authenticate the user.  
+ *
+ * Caveat: If not using 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)
 {
@@ -3185,7 +3185,6 @@ smb_ReceiveTran2GetDFSReferral(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     char requestFileName[1024] = "";
     smb_tran2Packet_t *outp = 0;
     cm_user_t *userp = 0;
-    cm_scache_t *scp;
     cm_req_t req;
     CPINFO CodePageInfo;
     int i, nbnLen, reqLen;
@@ -4512,17 +4511,17 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     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 */
+    trunc = ((openFun & 3) == 2); /* truncate it */
     openMode = (smb_GetSMBParm(inp, 3) & 0x7);
-    openAction = 0;                    /* tracks what we did */
+    openAction = 0;             /* tracks what we did */
 
     attributes = smb_GetSMBParm(inp, 5);
     dosTime = smb_GetSMBParm(inp, 6) | (smb_GetSMBParm(inp, 7) << 16);
 
-       /* compute initial mode bits based on read-only flag in attributes */
+                                /* compute initial mode bits based on read-only flag in attributes */
     initialModeBits = 0666;
     if (attributes & 1) initialModeBits &= ~0222;
         
@@ -4783,15 +4782,32 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     return 0;
 }       
 
-/* The file locking code is incomplete and that which is implemented in cm_Lock()
- * is broken.  What exists functions only because it is rarely if ever called.
- * The tests activated by FULL_LOCKS_ONLY ensure that cm_Lock() is only called
- * if the lock covers the entire file.  Therefore, RXAFS_SetLock is only called 
- * rarely.   That means that AFS locks are ignored by Windows clients.
- * When cm_Lock is re-written, undefine or better yet remove, the FULL_LOCKS_ONLY
- * code.
- */
-#define FULL_LOCKS_ONLY
+static void smb_GetLockParams(unsigned char LockType, 
+                              char ** buf, 
+                              unsigned int * ppid, 
+                              LARGE_INTEGER * pOffset, 
+                              LARGE_INTEGER * pLength)
+{
+    if (LockType & LOCKING_ANDX_LARGE_FILES) {
+        /* Large Files */
+        *ppid = *((USHORT *) *buf);
+        pOffset->HighPart = *((LONG *)(*buf + 4));
+        pOffset->LowPart = *((DWORD *)(*buf + 8));
+        pLength->HighPart = *((LONG *)(*buf + 12));
+        pLength->LowPart = *((DWORD *)(*buf + 16));
+        *buf += 20;
+    }
+    else {
+        /* Not Large Files */
+        *ppid = *((USHORT *) *buf);
+        pOffset->HighPart = 0;
+        pOffset->LowPart = *((DWORD *)(*buf + 2));
+        pLength->HighPart = 0;
+        pLength->LowPart = *((DWORD *)(*buf + 6));
+        *buf += 10;
+    }
+}
+
 long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
     cm_req_t req;
@@ -4801,13 +4817,16 @@ long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     cm_scache_t *scp;
     unsigned char LockType;
     unsigned short NumberOfUnlocks, NumberOfLocks;
-    unsigned long Timeout;
+    long Timeout;
     char *op;
+    char *op_locks;
     LARGE_INTEGER LOffset, LLength;
-    smb_waitingLock_t *waitingLock;
-    void *lockp;
+    smb_waitingLockRequest_t *wlRequest = NULL;
+    cm_file_lock_t *lockp;
     long code = 0;
     int i;
+    cm_key_t key;
+    unsigned int pid;
 
     cm_InitReq(&req);
 
@@ -4841,103 +4860,186 @@ long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     NumberOfUnlocks = smb_GetSMBParm(inp, 6);
     NumberOfLocks = smb_GetSMBParm(inp, 7);
 
+    if ((LockType & LOCKING_ANDX_CANCEL_LOCK) ||
+        (LockType & LOCKING_ANDX_CHANGE_LOCKTYPE)) {
+
+        /* We don't support these requests.  Apparently, we can safely
+           not deal with them too. */
+        osi_Log1(smb_logp, "smb_ReceiveV3Locking received unsupported request [%s]",
+                 ((LockType & LOCKING_ANDX_CANCEL_LOCK)?
+                  "LOCKING_ANDX_CANCEL_LOCK":
+                  "LOCKING_ANDX_CHANGE_LOCKTYPE")); 
+        /* No need to call osi_LogSaveString since these are string
+           constants.*/
+
+        code = CM_ERROR_BADOP;
+        goto done;
+
+    }
+
     op = smb_GetSMBData(inp, NULL);
 
     for (i=0; i<NumberOfUnlocks; i++) {
-        if (LockType & LOCKING_ANDX_LARGE_FILES) {
-            /* 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;
-        }
-#ifdef FULL_LOCKS_ONLY
-        if (LargeIntegerNotEqualToZero(LOffset)) {
-            osi_Log2(smb_logp, "smb_ReceiveV3Locking Unlock %d offset 0x%x != Zero",
-                     i, (long)LOffset.QuadPart);
-            continue;
-        }
-#endif /* FULL_LOCKS_ONLY */
-        /* Do not check length -- length check done in cm_Unlock */
+        smb_GetLockParams(LockType, &op, &pid, &LOffset, &LLength);
 
-        code = cm_Unlock(scp, LockType, LOffset, LLength, userp, &req);
-        if (code) goto done;
-    }       
+        key = cm_GenerateKey(vcp->vcID, pid, fidp->fid);
+
+        code = cm_Unlock(scp, LockType, LOffset, LLength, key, userp, &req);
+
+        if (code) 
+            goto done;
+    }
+
+    op_locks = op;
 
     for (i=0; i<NumberOfLocks; i++) {
-        if (LockType & LOCKING_ANDX_LARGE_FILES) {
-            /* 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;
-        }
-#ifdef FULL_LOCKS_ONLY
-        if (LargeIntegerNotEqualToZero(LOffset)) {
-            osi_Log2(smb_logp, "smb_ReceiveV3Locking Lock %d offset 0x%x != Zero",
-                     i, (long)LOffset.QuadPart);
-            continue;
-        }
-        if (LargeIntegerLessThan(LOffset, scp->length)) {
-            osi_Log3(smb_logp, "smb_ReceiveV3Locking Unlock %d offset 0x%x < 0x%x",
-                     i, (long)LOffset.QuadPart, (long)scp->length.QuadPart);
-            continue;
-        }
-#endif /* FULL_LOCKS_ONLY */
-        code = cm_Lock(scp, LockType, LOffset, LLength, Timeout,
+        smb_GetLockParams(LockType, &op, &pid, &LOffset, &LLength);
+
+        key = cm_GenerateKey(vcp->vcID, pid, fidp->fid);
+
+        code = cm_Lock(scp, LockType, LOffset, LLength, key, (Timeout != 0),
                         userp, &req, &lockp);
+
         if (code == CM_ERROR_WOULDBLOCK && Timeout != 0) {
+            smb_waitingLock_t * wLock;
+
             /* Put on waiting list */
-            waitingLock = malloc(sizeof(smb_waitingLock_t));
-            waitingLock->vcp = vcp;
-            smb_HoldVC(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(wlRequest == NULL) {
+                int j;
+                char * opt;
+                cm_key_t tkey;
+                LARGE_INTEGER tOffset, tLength;
+
+                wlRequest = malloc(sizeof(smb_waitingLockRequest_t));
+
+                osi_assert(wlRequest != NULL);
+
+                wlRequest->vcp = vcp;
+                smb_HoldVC(vcp);
+                wlRequest->scp = scp;
+                cm_HoldSCache(scp);
+                wlRequest->inp = smb_CopyPacket(inp);
+                wlRequest->outp = smb_CopyPacket(outp);
+                wlRequest->lockType = LockType;
+                wlRequest->timeRemaining = Timeout;
+                wlRequest->locks = NULL;
+
+                /* The waiting lock request needs to have enough
+                   information to undo all the locks in the request.
+                   We do the following to store info about locks that
+                   have already been granted.  Sure, we can get most
+                   of the info from the packet, but the packet doesn't
+                   hold the result of cm_Lock call.  In practice we
+                   only receive packets with one or two locks, so we
+                   are only wasting a few bytes here and there and
+                   only for a limited period of time until the waiting
+                   lock times out or is freed. */
+
+                for(opt = op_locks, j=i; j > 0; j--) {
+                    smb_GetLockParams(LockType, &opt, &pid, &tOffset, &tLength);
+
+                    tkey = cm_GenerateKey(vcp->vcID, pid, fidp->fid);
+
+                    wLock = malloc(sizeof(smb_waitingLock_t));
+
+                    osi_assert(wLock != NULL);
+
+                    wLock->key = tkey;
+                    wLock->LOffset = tOffset;
+                    wLock->LLength = tLength;
+                    wLock->lockp = NULL;
+                    wLock->state = SMB_WAITINGLOCKSTATE_DONE;
+                    osi_QAdd((osi_queue_t **) &wlRequest->locks,
+                             &wLock->q);
+                }
+            }
+
+            wLock = malloc(sizeof(smb_waitingLock_t));
+
+            osi_assert(wLock != NULL);
+
+            wLock->key = key;
+            wLock->LOffset = LOffset;
+            wLock->LLength = LLength;
+            wLock->lockp = lockp;
+            wLock->state = SMB_WAITINGLOCKSTATE_WAITING;
+            osi_QAdd((osi_queue_t **) &wlRequest->locks,
+                     &wLock->q);
+
             osi_Log1(smb_logp, "smb_ReceiveV3Locking WaitingLock created 0x%x",
-                     (long) waitingLock);
+                     (long) wLock);
+
+            code = 0;
             continue;
         }
+
         if (code) {
             osi_Log1(smb_logp, "smb_ReceiveV3Locking cm_Lock failure code 0x%x", code);
             break;
         }
-    }           
+    }
 
     if (code) {
-        /* release any locks acquired before the failure */
-        osi_Log0(smb_logp, "smb_ReceiveV3Locking - failure; should be releasing locks but don't!!!!");
-    }
-    else
+
+        /* Since something went wrong with the lock number i, we now
+           have to go ahead and release any locks acquired before the
+           failure.  All locks before lock number i (of which there
+           are i of them) have either been successful or are waiting.
+           Either case requires calling cm_Unlock(). */
+
+        /* And purge the waiting lock */
+        if(wlRequest != NULL) {
+            smb_waitingLock_t * wl;
+            smb_waitingLock_t * wlNext;
+            long ul_code;
+
+            for(wl = wlRequest->locks; wl; wl = wlNext) {
+
+                wlNext = (smb_waitingLock_t *) osi_QNext(&wl->q);
+
+                ul_code = cm_Unlock(scp, LockType, wl->LOffset, wl->LLength, wl->key, userp, &req);
+                
+                if(ul_code != 0) {
+                    osi_Log1(smb_logp, "smb_ReceiveV3Locking cm_Unlock returns code %d", ul_code);
+                } else {
+                    osi_Log0(smb_logp, "smb_ReceiveV3Locking cm_Unlock successful");
+                }
+
+                osi_QRemove((osi_queue_t **) &wlRequest->locks, &wl->q);
+                free(wl);
+
+            }
+
+            smb_ReleaseVC(wlRequest->vcp);
+            cm_ReleaseSCache(wlRequest->scp);
+            smb_FreePacket(wlRequest->inp);
+            smb_FreePacket(wlRequest->outp);
+
+            free(wlRequest);
+
+            wlRequest = NULL;
+        }
+
+    } else {
+
+        if (wlRequest != NULL) {
+
+            lock_ObtainWrite(&smb_globalLock);
+            osi_QAdd((osi_queue_t **)&smb_allWaitingLocks,
+                     &wlRequest->q);
+            osi_Wakeup((long) &smb_allWaitingLocks);
+            lock_ReleaseWrite(&smb_globalLock);
+
+            /* don't send reply immediately */
+            outp->flags |= SMB_PACKETFLAG_NOSEND;
+        }
+
         smb_SetSMBDataLength(outp, 0);
+    }
+
   done:   
     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
+
   doneSync:
     lock_ReleaseMutex(&scp->mx);
     cm_ReleaseUser(userp);
@@ -5064,11 +5166,14 @@ long smb_ReceiveV3SetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
 long smb_ReceiveV3ReadX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 {
     osi_hyper_t offset;
-    long count, finalCount;
+    long count;
+    long finalCount = 0;
     unsigned short fd;
+    unsigned pid;
     smb_fid_t *fidp;
     long code = 0;
     cm_user_t *userp;
+    cm_key_t key;
     char *op;
         
     fd = smb_GetSMBParm(inp, 2);
@@ -5084,13 +5189,34 @@ long smb_ReceiveV3ReadX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     if (!fidp) {
         return CM_ERROR_BADFD;
     }
+
+    pid = ((smb_t *) inp)->pid;
+    key = cm_GenerateKey(vcp->vcID, pid, fd);
+    {
+        LARGE_INTEGER LOffset, LLength;
+
+        LOffset.HighPart = offset.HighPart;
+        LOffset.LowPart = offset.LowPart;
+        LLength.HighPart = 0;
+        LLength.LowPart = count;
+
+        lock_ObtainMutex(&fidp->scp->mx);
+        code = cm_LockCheckRead(fidp->scp, LOffset, LLength, key);
+        lock_ReleaseMutex(&fidp->scp->mx);
+    }
+
+    if (code) {
+        smb_ReleaseFID(fidp);
+        return code;
+    }
+
     /* 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);
     }
-        
+
     userp = smb_GetUser(vcp, inp);
 
     /* 0 and 1 are reserved for request chaining, were setup by our caller,
@@ -5185,6 +5311,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     unsigned int extAttributes;
     unsigned int createDisp;
     unsigned int createOptions;
+    unsigned int shareAccess;
     int initialModeBits;
     unsigned short baseFid;
     smb_fid_t *baseFidp;
@@ -5229,6 +5356,8 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         | (smb_GetSMBOffsetParm(inp, 8, 1) << 16);
     extAttributes = smb_GetSMBOffsetParm(inp, 13, 1)
         | (smb_GetSMBOffsetParm(inp, 14, 1) << 16);
+    shareAccess = smb_GetSMBOffsetParm(inp, 15, 1)
+        | (smb_GetSMBOffsetParm(inp, 16, 1) << 16);
     createDisp = smb_GetSMBOffsetParm(inp, 17, 1)
         | (smb_GetSMBOffsetParm(inp, 18, 1) << 16);
     createOptions = smb_GetSMBOffsetParm(inp, 19, 1)
@@ -5249,7 +5378,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
      * extended attributes
      */
     initialModeBits = 0666;
-    if (extAttributes & 1) 
+    if (extAttributes & SMB_ATTR_READONLY) 
         initialModeBits &= ~0222;
 
     pathp = smb_GetSMBData(inp, NULL);
@@ -5265,7 +5394,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     osi_Log1(smb_logp,"NTCreateX for [%s]",osi_LogSaveString(smb_logp,realPathp));
     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")));
+    osi_Log3(smb_logp,"... share=[%x] flags=[%x] lastNamep=[%s]", shareAccess, 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
@@ -5313,6 +5442,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
        free(hexp);
     }
 #endif
+
     userp = smb_GetUser(vcp, inp);
     if (!userp) {
        osi_Log1(smb_logp, "NTCreateX Invalid user [%d]", ((smb_t *) inp)->uid);
@@ -5361,6 +5491,12 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     if (createOptions & FILE_DELETE_ON_CLOSE)
         fidflags |= SMB_FID_DELONCLOSE;
 
+    /* and the share mode */
+    if (shareAccess & FILE_SHARE_READ)
+        fidflags |= SMB_FID_SHARE_READ;
+    if (shareAccess & FILE_SHARE_WRITE)
+        fidflags |= SMB_FID_SHARE_WRITE;
+
     code = 0;
 
     /* For an exclusive create, we want to do a case sensitive match for the last component. */
@@ -5536,7 +5672,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         /* we have scp but not dscp */
         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
@@ -5557,6 +5693,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
         if ( createDisp == FILE_OVERWRITE || 
              createDisp == FILE_OVERWRITE_IF) {
+
             setAttr.mask = CM_ATTRMASK_LENGTH;
             setAttr.length.LowPart = 0;
             setAttr.length.HighPart = 0;
@@ -5781,6 +5918,46 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     /* open the file itself */
     fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
     osi_assert(fidp);
+
+    /* If we are restricting sharing, we should do so with a suitable
+       share lock. */
+    if (scp->fileType == CM_SCACHETYPE_FILE &&
+        !(fidflags & SMB_FID_SHARE_WRITE)) {
+        cm_key_t key;
+        LARGE_INTEGER LOffset, LLength;
+        int sLockType;
+
+        LOffset.HighPart = SMB_FID_QLOCK_HIGH;
+        LOffset.LowPart = SMB_FID_QLOCK_LOW;
+        LLength.HighPart = 0;
+        LLength.LowPart = SMB_FID_QLOCK_LENGTH;
+
+        if (fidflags & SMB_FID_SHARE_READ) {
+            sLockType = LOCKING_ANDX_SHARED_LOCK;
+        } else {
+            sLockType = 0;
+        }
+
+        key = cm_GenerateKey(vcp->vcID, SMB_FID_QLOCK_PID, fidp->fid);
+        
+        lock_ObtainMutex(&scp->mx);
+        code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, &req, NULL);
+        lock_ReleaseMutex(&scp->mx);
+
+        if (code) {
+            fidp->flags = SMB_FID_DELETE;
+            smb_ReleaseFID(fidp);
+
+            cm_ReleaseSCache(scp);
+            if (dscp)
+                cm_ReleaseSCache(dscp);
+            cm_ReleaseUser(userp);
+            free(realPathp);
+            
+            return CM_ERROR_SHARING_VIOLATION;
+        }
+    }
+
     /* save a pointer to the vnode */
     fidp->scp = scp;    /* Hold transfered to fidp->scp and no longer needed */
 
@@ -5800,6 +5977,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         cm_ReleaseSCache(dscp);
         dscp = NULL;
     }
+
     cm_Open(scp, 0, userp);
 
     /* set inp->fid so that later read calls in same msg can find fid */
@@ -5834,7 +6012,8 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     cm_ReleaseUser(userp);
 
-    /* Can't free realPathp if we get here since fidp->NTopen_wholepathp is pointing there */
+    /* 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;
@@ -5865,8 +6044,8 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     unsigned int desiredAccess;
 #ifdef DEBUG_VERBOSE    
     unsigned int allocSize;
-    unsigned int shareAccess;
 #endif
+    unsigned int shareAccess;
     unsigned int extAttributes;
     unsigned int createDisp;
 #ifdef DEBUG_VERBOSE
@@ -5918,9 +6097,7 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     allocSize = lparmp[3];
 #endif /* DEBUG_VERSOSE */
     extAttributes = lparmp[5];
-#ifdef DEBUG_VEROSE
     shareAccess = lparmp[6];
-#endif
     createDisp = lparmp[7];
     createOptions = lparmp[8];
 #ifdef DEBUG_VERBOSE
@@ -6024,6 +6201,12 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     if (createOptions & FILE_DELETE_ON_CLOSE)
         fidflags |= SMB_FID_DELONCLOSE;
 
+    /* And the share mode */
+    if (shareAccess & FILE_SHARE_READ)
+        fidflags |= SMB_FID_SHARE_READ;
+    if (shareAccess & FILE_SHARE_WRITE)
+        fidflags |= SMB_FID_SHARE_WRITE;
+
     dscp = NULL;
     code = 0;
     if ( createDisp == FILE_OPEN || 
@@ -6324,6 +6507,43 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
     osi_assert(fidp);
 
+    /* If we are restricting sharing, we should do so with a suitable
+       share lock. */
+    if (scp->fileType == CM_SCACHETYPE_FILE &&
+        !(fidflags & SMB_FID_SHARE_WRITE)) {
+        cm_key_t key;
+        LARGE_INTEGER LOffset, LLength;
+        int sLockType;
+
+        LOffset.HighPart = SMB_FID_QLOCK_HIGH;
+        LOffset.LowPart = SMB_FID_QLOCK_LOW;
+        LLength.HighPart = 0;
+        LLength.LowPart = SMB_FID_QLOCK_LENGTH;
+
+        if (fidflags & SMB_FID_SHARE_READ) {
+            sLockType = LOCKING_ANDX_SHARED_LOCK;
+        } else {
+            sLockType = 0;
+        }
+
+        key = cm_GenerateKey(vcp->vcID, SMB_FID_QLOCK_PID, fidp->fid);
+        
+        lock_ObtainMutex(&scp->mx);
+        code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, &req, NULL);
+        lock_ReleaseMutex(&scp->mx);
+
+        if (code) {
+            fidp->flags = SMB_FID_DELETE;
+            smb_ReleaseFID(fidp);
+
+            cm_ReleaseSCache(scp);
+            cm_ReleaseUser(userp);
+            free(realPathp);
+            
+            return CM_ERROR_SHARING_VIOLATION;
+        }
+    }
+
     /* save a pointer to the vnode */
     fidp->scp = scp;