diff --git a/howto.txt b/howto.txt index 654ad658c8b..78bed1316aa 100644 --- a/howto.txt +++ b/howto.txt @@ -70,20 +70,17 @@ configure above to change this. Step 4: provision Samba4 ------------------------ -The "provision" step sets up a basic user database. Make sure your smbscript -binary is installed in a directory listed in your PATH environment variable. -It is presumed it's available just like any other commands from your shell. +The "provision" step sets up a basic user database. Must be run as a user with permission to write to the install directory. :: # cd source - # ./setup/provision --realm=YOUR.REALM --domain=YOURDOM \ + # bin/smbpython ./setup/provision --realm=YOUR.REALM --domain=YOURDOM \ # --adminpass=SOMEPASSWORD --server-role='domain controller' -REMINDER: Add the "bin" directory of the path you installed to - (e.g. /usr/local/samba/bin) to your path, or the provision command - will not work. +REMINDER: Use the path to smbpython, as the provision command + will not work with the system python. 'YOURDOM' is the NT4 style domain name. 'YOUR.REALM' is your kerberos realm, which is typically your DNS domain name. diff --git a/source/cluster/ctdb/opendb_ctdb.c b/source/cluster/ctdb/opendb_ctdb.c index 86dc1f50f11..3dfc6819b7c 100644 --- a/source/cluster/ctdb/opendb_ctdb.c +++ b/source/cluster/ctdb/opendb_ctdb.c @@ -279,11 +279,13 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_ent Note that the path is only used by the delete on close logic, not for comparing with other filenames */ -static NTSTATUS odb_ctdb_open_file(struct odb_lock *lck, void *file_handle, - uint32_t stream_id, uint32_t share_access, - uint32_t access_mask, bool delete_on_close, - const char *path, - uint32_t oplock_level, uint32_t *oplock_granted) +static NTSTATUS odb_ctdb_open_file(struct odb_lock *lck, + void *file_handle, const char *path, + uint32_t stream_id, uint32_t share_access, + uint32_t access_mask, bool delete_on_close, + uint32_t open_disposition, bool break_to_none, + uint32_t oplock_level, uint32_t *oplock_granted) + { struct odb_context *odb = lck->odb; struct opendb_entry e; @@ -464,6 +466,16 @@ static NTSTATUS odb_ctdb_update_oplock(struct odb_lock *lck, void *file_handle, return NT_STATUS_FOOBAR; } +static NTSTATUS odb_ctdb_break_oplocks(struct odb_lock *lck) +{ + /* + * as this file will went away and isn't used yet, + * copy the implementation from the tdb backend + * --metze + */ + return NT_STATUS_FOOBAR; +} + /* remove a pending opendb entry */ @@ -584,8 +596,9 @@ static NTSTATUS odb_ctdb_get_delete_on_close(struct odb_context *odb, create_options and access_mask */ static NTSTATUS odb_ctdb_can_open(struct odb_lock *lck, - uint32_t share_access, uint32_t create_options, - uint32_t access_mask) + uint32_t stream_id, uint32_t share_access, + uint32_t access_mask, bool delete_on_close, + uint32_t open_disposition, bool break_to_none) { struct odb_context *odb = lck->odb; NTSTATUS status; @@ -599,7 +612,7 @@ static NTSTATUS odb_ctdb_can_open(struct odb_lock *lck, } NT_STATUS_NOT_OK_RETURN(status); - if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) && + if (delete_on_close && file.num_entries != 0) { return NT_STATUS_SHARING_VIOLATION; } @@ -642,7 +655,8 @@ static const struct opendb_ops opendb_ctdb_ops = { .odb_set_delete_on_close = odb_ctdb_set_delete_on_close, .odb_get_delete_on_close = odb_ctdb_get_delete_on_close, .odb_can_open = odb_ctdb_can_open, - .odb_update_oplock = odb_ctdb_update_oplock + .odb_update_oplock = odb_ctdb_update_oplock, + .odb_break_oplocks = odb_ctdb_break_oplocks }; diff --git a/source/lib/ldb/web/index.html b/source/lib/ldb/web/index.html index 76dbbdeafb3..26dd3445273 100644 --- a/source/lib/ldb/web/index.html +++ b/source/lib/ldb/web/index.html @@ -42,9 +42,9 @@ The main features that separate ldb from other solutions are: Currently ldb is completely lacking in programmer or user documentation. This is your opportunity to make a contribution! Start with the public functions declared in ldb.h +href="http://samba.org/ftp/unpacked/ldb/include/ldb.h">ldb.h and the example code in the tools +href="http://samba.org/ftp/unpacked/ldb/tools/">tools directory. Documentation in the same docbook format used by Samba would be preferred. @@ -53,21 +53,17 @@ would be preferred. ldb does not currently have its own mailing list or bug tracking system. For now, please use the samba-technical -mailing list, and the Samba -bugzilla bug tracking system. +mailing list or the ldb +mailing list, and the Samba bugzilla bug tracking system.

Download

-You can download the latest release either via rsync or anonymous -svn. To fetch via svn use the following commands: - -
-  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/ldb ldb
-  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb tdb
-  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc talloc
-  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/replace libreplace
-
- +You can download the latest release either via rsync or thtough git.
+
+To fetch via git see the following guide:
+Using Git for Samba Development
+Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/ldb directory.
+
To fetch via rsync use these commands:
diff --git a/source/lib/replace/system/network.h b/source/lib/replace/system/network.h
index d09e3f71f8d..a84b22e5d08 100644
--- a/source/lib/replace/system/network.h
+++ b/source/lib/replace/system/network.h
@@ -83,6 +83,11 @@
 #include 
 #endif
 
+#ifndef HAVE_SOCKLEN_T
+#define HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
 #ifdef REPLACE_INET_NTOA
 /* define is in "replace.h" */
 char *rep_inet_ntoa(struct in_addr ip);
@@ -245,11 +250,6 @@ void rep_freeifaddrs(struct ifaddrs *);
 #define HOST_NAME_MAX 256
 #endif
 
-#ifndef HAVE_SOCKLEN_T
-#define HAVE_SOCKLEN_T
-typedef int socklen_t;
-#endif
-
 #ifndef HAVE_SA_FAMILY_T
 #define HAVE_SA_FAMILY_T
 typedef unsigned short int sa_family_t;
diff --git a/source/lib/talloc/web/index.html b/source/lib/talloc/web/index.html
index 628030ad4cd..5deab936653 100644
--- a/source/lib/talloc/web/index.html
+++ b/source/lib/talloc/web/index.html
@@ -12,7 +12,7 @@ destructors. It is the core memory allocator used in Samba4, and has
 made a huge difference in many aspects of Samba4 development.

To get started with talloc, I would recommend you read the talloc guide. +href="http://samba.org/ftp/unpacked/talloc/talloc_guide.txt">talloc guide.

Discussion and bug reports

@@ -24,20 +24,16 @@ bugzilla bug tracking system.

Download

-You can download the latest release either via rsync or git. -To fetch via git use the following command: - -
-  git-clone git://git.samba.org/samba.git samba
-  cd samba
-  git checkout -b samba4 origin/v4-0-test
-
- +You can download the latest release either via rsync or git.
+
+To fetch via git see the following guide:
+Using Git for Samba Development
+Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/talloc directory.
+
To fetch via rsync use this command:
-  rsync -Pavz samba.org::ftp/unpacked/samba_4_0_test/source/lib/talloc .
-  rsync -Pavz samba.org::ftp/unpacked/samba_4_0_test/source/lib/libreplace .
+  rsync -Pavz samba.org::ftp/unpacked/talloc .
 

diff --git a/source/lib/tdb/web/index.html b/source/lib/tdb/web/index.html index 979665ae769..a53da6b8f77 100644 --- a/source/lib/tdb/web/index.html +++ b/source/lib/tdb/web/index.html @@ -22,14 +22,12 @@ bugzilla bug tracking system.

Download

-You can download the latest release either via rsync or anonymous -svn. To fetch via svn use the following commands: - -
-  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb tdb
-  svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/replace libreplace
-
- +You can download the latest release either via rsync or git.
+
+To fetch via git see the following guide:
+Using Git for Samba Development
+Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/tdb directory.
+
To fetch via rsync use these commands:
diff --git a/source/ntvfs/common/opendb.c b/source/ntvfs/common/opendb.c
index 4ac10806e13..36144d0406b 100644
--- a/source/ntvfs/common/opendb.c
+++ b/source/ntvfs/common/opendb.c
@@ -93,15 +93,16 @@ _PUBLIC_ DATA_BLOB odb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
   Note that the path is only used by the delete on close logic, not
   for comparing with other filenames
 */
-_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
-				uint32_t stream_id, uint32_t share_access, 
+_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck,
+				void *file_handle, const char *path,
+				uint32_t stream_id, uint32_t share_access,
 				uint32_t access_mask, bool delete_on_close,
-				const char *path, 
+				uint32_t open_disposition, bool break_to_none,
 				uint32_t oplock_level, uint32_t *oplock_granted)
 {
-	return ops->odb_open_file(lck, file_handle, stream_id, share_access,
-				  access_mask, delete_on_close, path, oplock_level,
-				  oplock_granted);
+	return ops->odb_open_file(lck, file_handle, path, stream_id, share_access,
+				  access_mask, delete_on_close, open_disposition,
+				  break_to_none, oplock_level, oplock_granted);
 }
 
 
@@ -165,10 +166,12 @@ _PUBLIC_ NTSTATUS odb_get_delete_on_close(struct odb_context *odb,
   create_options and access_mask
 */
 _PUBLIC_ NTSTATUS odb_can_open(struct odb_lock *lck,
-			       uint32_t share_access, uint32_t create_options, 
-			       uint32_t access_mask)
+			       uint32_t stream_id, uint32_t share_access,
+			       uint32_t access_mask, bool delete_on_close,
+			       uint32_t open_disposition, bool break_to_none)
 {
-	return ops->odb_can_open(lck, share_access, create_options, access_mask);
+	return ops->odb_can_open(lck, stream_id, share_access, access_mask,
+				 delete_on_close, open_disposition, break_to_none);
 }
 
 _PUBLIC_ NTSTATUS odb_update_oplock(struct odb_lock *lck, void *file_handle,
@@ -176,3 +179,8 @@ _PUBLIC_ NTSTATUS odb_update_oplock(struct odb_lock *lck, void *file_handle,
 {
 	return ops->odb_update_oplock(lck, file_handle, oplock_level);
 }
+
+_PUBLIC_ NTSTATUS odb_break_oplocks(struct odb_lock *lck)
+{
+	return ops->odb_break_oplocks(lck);
+}
diff --git a/source/ntvfs/common/opendb.h b/source/ntvfs/common/opendb.h
index c34a07d6fac..9591bcf6b9b 100644
--- a/source/ntvfs/common/opendb.h
+++ b/source/ntvfs/common/opendb.h
@@ -25,10 +25,11 @@ struct opendb_ops {
 	struct odb_lock *(*odb_lock)(TALLOC_CTX *mem_ctx,
 				     struct odb_context *odb, DATA_BLOB *file_key);
 	DATA_BLOB (*odb_get_key)(TALLOC_CTX *mem_ctx, struct odb_lock *lck);
-	NTSTATUS (*odb_open_file)(struct odb_lock *lck, void *file_handle,
-				  uint32_t stream_id, uint32_t share_access, 
+	NTSTATUS (*odb_open_file)(struct odb_lock *lck,
+				  void *file_handle, const char *path,
+				  uint32_t stream_id, uint32_t share_access,
 				  uint32_t access_mask, bool delete_on_close,
-				  const char *path, 
+				  uint32_t open_disposition, bool break_to_none,
 				  uint32_t oplock_level, uint32_t *oplock_granted);
 	NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private);
 	NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle);
@@ -39,10 +40,12 @@ struct opendb_ops {
 					    DATA_BLOB *key, bool *del_on_close, 
 					    int *open_count, char **path);
 	NTSTATUS (*odb_can_open)(struct odb_lock *lck,
-				 uint32_t share_access, uint32_t create_options, 
-				 uint32_t access_mask);
+				 uint32_t stream_id, uint32_t share_access,
+				 uint32_t access_mask, bool delete_on_close,
+				 uint32_t open_disposition, bool break_to_none);
 	NTSTATUS (*odb_update_oplock)(struct odb_lock *lck, void *file_handle,
 				      uint32_t oplock_level);
+	NTSTATUS (*odb_break_oplocks)(struct odb_lock *lck);
 };
 
 struct opendb_oplock_break {
diff --git a/source/ntvfs/common/opendb_tdb.c b/source/ntvfs/common/opendb_tdb.c
index 3d4a760e2e4..73c04b7c4f3 100644
--- a/source/ntvfs/common/opendb_tdb.c
+++ b/source/ntvfs/common/opendb_tdb.c
@@ -146,7 +146,10 @@ static DATA_BLOB odb_tdb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
 
   return NT_STATUS_OK on no conflict
 */
-static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2)
+static NTSTATUS share_conflict(struct opendb_entry *e1,
+			       uint32_t stream_id,
+			       uint32_t share_access,
+			       uint32_t access_mask)
 {
 	/* if either open involves no read.write or delete access then
 	   it can't conflict */
@@ -157,18 +160,18 @@ static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2)
 				 SEC_STD_DELETE))) {
 		return NT_STATUS_OK;
 	}
-	if (!(e2->access_mask & (SEC_FILE_WRITE_DATA |
-				 SEC_FILE_APPEND_DATA |
-				 SEC_FILE_READ_DATA |
-				 SEC_FILE_EXECUTE |
-				 SEC_STD_DELETE))) {
+	if (!(access_mask & (SEC_FILE_WRITE_DATA |
+			     SEC_FILE_APPEND_DATA |
+			     SEC_FILE_READ_DATA |
+			     SEC_FILE_EXECUTE |
+			     SEC_STD_DELETE))) {
 		return NT_STATUS_OK;
 	}
 
 	/* data IO access masks. This is skipped if the two open handles
 	   are on different streams (as in that case the masks don't
 	   interact) */
-	if (e1->stream_id != e2->stream_id) {
+	if (e1->stream_id != stream_id) {
 		return NT_STATUS_OK;
 	}
 
@@ -176,20 +179,20 @@ static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2)
 	if (((am) & (right)) && !((sa) & (share))) return NT_STATUS_SHARING_VIOLATION
 
 	CHECK_MASK(e1->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
-		   e2->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
-	CHECK_MASK(e2->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
+		   share_access, NTCREATEX_SHARE_ACCESS_WRITE);
+	CHECK_MASK(access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
 		   e1->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
 	
 	CHECK_MASK(e1->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
-		   e2->share_access, NTCREATEX_SHARE_ACCESS_READ);
-	CHECK_MASK(e2->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
+		   share_access, NTCREATEX_SHARE_ACCESS_READ);
+	CHECK_MASK(access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
 		   e1->share_access, NTCREATEX_SHARE_ACCESS_READ);
 
 	CHECK_MASK(e1->access_mask, SEC_STD_DELETE,
-		   e2->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
-	CHECK_MASK(e2->access_mask, SEC_STD_DELETE,
+		   share_access, NTCREATEX_SHARE_ACCESS_DELETE);
+	CHECK_MASK(access_mask, SEC_STD_DELETE,
 		   e1->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
-
+#undef CHECK_MASK
 	return NT_STATUS_OK;
 }
 
@@ -284,6 +287,100 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb,
 	return NT_STATUS_OK;
 }
 
+static bool access_attributes_only(uint32_t access_mask,
+				   uint32_t open_disposition)
+{
+	switch (open_disposition) {
+	case NTCREATEX_DISP_SUPERSEDE:
+	case NTCREATEX_DISP_OVERWRITE_IF:
+	case NTCREATEX_DISP_OVERWRITE:
+		return false;
+	default:
+		break;
+	}
+#define CHECK_MASK(m,g) ((m) && (((m) & ~(g))==0) && (((m) & (g)) != 0))
+	return CHECK_MASK(access_mask,
+			  SEC_STD_SYNCHRONIZE |
+			  SEC_FILE_READ_ATTRIBUTE |
+			  SEC_FILE_WRITE_ATTRIBUTE);
+#undef CHECK_MASK
+}
+
+static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb,
+					  const struct opendb_file *file,
+					  uint32_t stream_id, uint32_t share_access,
+					  uint32_t access_mask, bool delete_on_close,
+					  uint32_t open_disposition, bool break_to_none,
+					  bool *_attrs_only)
+{
+	NTSTATUS status;
+	uint32_t i;
+	bool attrs_only = false;
+
+	/* see if anyone has an oplock, which we need to break */
+	for (i=0;inum_entries;i++) {
+		if (file->entries[i].oplock_level == OPLOCK_BATCH) {
+			bool oplock_return = OPLOCK_BREAK_TO_LEVEL_II;
+			/* if this is an attribute only access
+			 * it doesn't conflict with a BACTCH oplock
+			 * but we'll not grant the oplock below
+			 */
+			attrs_only = access_attributes_only(access_mask,
+							    open_disposition);
+			if (attrs_only) {
+				break;
+			}
+			/* a batch oplock caches close calls, which
+			   means the client application might have
+			   already closed the file. We have to allow
+			   this close to propogate by sending a oplock
+			   break request and suspending this call
+			   until the break is acknowledged or the file
+			   is closed */
+			if (break_to_none) {
+				oplock_return = OPLOCK_BREAK_TO_NONE;
+			}
+			odb_oplock_break_send(odb, &file->entries[i],
+					      oplock_return);
+			return NT_STATUS_OPLOCK_NOT_GRANTED;
+		}
+	}
+
+	if (file->delete_on_close) {
+		/* while delete on close is set, no new opens are allowed */
+		return NT_STATUS_DELETE_PENDING;
+	}
+
+	if (file->num_entries != 0 && delete_on_close) {
+		return NT_STATUS_SHARING_VIOLATION;
+	}
+
+	/* check for sharing violations */
+	for (i=0;inum_entries;i++) {
+		status = share_conflict(&file->entries[i], stream_id,
+					share_access, access_mask);
+		NT_STATUS_NOT_OK_RETURN(status);
+	}
+
+	/* we now know the open could succeed, but we need to check
+	   for any exclusive oplocks. We can't grant a second open
+	   till these are broken. Note that we check for batch oplocks
+	   before checking for sharing violations, and check for
+	   exclusive oplocks afterwards. */
+	for (i=0;inum_entries;i++) {
+		if (file->entries[i].oplock_level == OPLOCK_EXCLUSIVE) {
+			odb_oplock_break_send(odb, &file->entries[i],
+					      OPLOCK_BREAK_TO_NONE);
+			return NT_STATUS_OPLOCK_NOT_GRANTED;
+		}
+	}
+
+	if (_attrs_only) {
+		*_attrs_only = attrs_only;
+	}
+	return NT_STATUS_OK;
+}
+
 /*
   register an open file in the open files database. This implements the share_access
   rules
@@ -291,17 +388,18 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb,
   Note that the path is only used by the delete on close logic, not
   for comparing with other filenames
 */
-static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle,
-				  uint32_t stream_id, uint32_t share_access, 
+static NTSTATUS odb_tdb_open_file(struct odb_lock *lck,
+				  void *file_handle, const char *path,
+				  uint32_t stream_id, uint32_t share_access,
 				  uint32_t access_mask, bool delete_on_close,
-				  const char *path, 
+				  uint32_t open_disposition, bool break_to_none,
 				  uint32_t oplock_level, uint32_t *oplock_granted)
 {
 	struct odb_context *odb = lck->odb;
 	struct opendb_entry e;
-	int i;
 	struct opendb_file file;
 	NTSTATUS status;
+	bool attrs_only = false;
 
 	if (odb->oplocks == false) {
 		oplock_level = OPLOCK_NONE;
@@ -316,6 +414,13 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle,
 		NT_STATUS_NOT_OK_RETURN(status);
 	}
 
+	/* see if it conflicts */
+	status = odb_tdb_open_can_internal(odb, &file, stream_id,
+					   share_access, access_mask,
+					   delete_on_close, open_disposition,
+					   break_to_none, &attrs_only);
+	NT_STATUS_NOT_OK_RETURN(status);
+
 	/* see if it conflicts */
 	e.server          = odb->ntvfs_ctx->server_id;
 	e.file_handle     = file_handle;
@@ -324,61 +429,37 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle,
 	e.access_mask     = access_mask;
 	e.delete_on_close = delete_on_close;
 	e.oplock_level    = OPLOCK_NONE;
-		
-	/* see if anyone has an oplock, which we need to break */
-	for (i=0;iodb;
+	NTSTATUS status;
+	struct opendb_file file;
+	int i;
+	bool modified = true;
+
+	status = odb_pull_record(lck, &file);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+		return NT_STATUS_OK;
+	}
+	NT_STATUS_NOT_OK_RETURN(status);
+
+	/* see if anyone has an oplock, which we need to break */
+	for (i=0;iodb;
 	NTSTATUS status;
 	struct opendb_file file;
-	struct opendb_entry e;
-	int i;
+	bool attrs_only = false;
 
 	status = odb_pull_record(lck, &file);
 	if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
@@ -639,32 +757,11 @@ static NTSTATUS odb_tdb_can_open(struct odb_lock *lck,
 	}
 	NT_STATUS_NOT_OK_RETURN(status);
 
-	if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) && 
-	    file.num_entries != 0) {
-		return NT_STATUS_SHARING_VIOLATION;
-	}
-
-	if (file.delete_on_close) {
-		return NT_STATUS_DELETE_PENDING;
-	}
-
-	e.server       = odb->ntvfs_ctx->server_id;
-	e.file_handle  = NULL;
-	e.stream_id    = 0;
-	e.share_access = share_access;
-	e.access_mask  = access_mask;
-		
-	for (i=0;ilockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
+		return pvfs_oplock_release(ntvfs, req, lck);
+	}
+
 	f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
 	if (!f) {
 		return NT_STATUS_INVALID_HANDLE;
@@ -303,6 +307,9 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
 		return NT_STATUS_FILE_IS_A_DIRECTORY;
 	}
 
+	status = pvfs_break_level2_oplocks(f);
+	NT_STATUS_NOT_OK_RETURN(status);
+
 	if (lck->lockx.in.timeout != 0 && 
 	    (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
 		pending = talloc(f, struct pvfs_pending_lock);
@@ -338,13 +345,6 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
 		return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks);
 	}
 
-	if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
-		DEBUG(0,("received unexpected oplock break\n"));
-		talloc_free(pending);
-		return NT_STATUS_NOT_IMPLEMENTED;
-	}
-
-
 	/* the unlocks happen first */
 	locks = lck->lockx.in.locks;
 
diff --git a/source/ntvfs/posix/pvfs_open.c b/source/ntvfs/posix/pvfs_open.c
index 3a8b17ab16d..12b70c00fd9 100644
--- a/source/ntvfs/posix/pvfs_open.c
+++ b/source/ntvfs/posix/pvfs_open.c
@@ -268,6 +268,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
 	f->handle->seek_offset       = 0;
 	f->handle->position          = 0;
 	f->handle->mode              = 0;
+	f->handle->oplock            = NULL;
 	f->handle->sticky_write_time = false;
 	f->handle->open_completed    = false;
 
@@ -296,9 +297,10 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
 		}
 		
 		/* see if we are allowed to open at the same time as existing opens */
-		status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
+		status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
 				       share_access, access_mask, del_on_close, 
-				       name->full_name, OPLOCK_NONE, NULL);
+				       io->generic.in.open_disposition,
+				       false, OPLOCK_NONE, NULL);
 
 		if (!NT_STATUS_IS_OK(status)) {
 			talloc_free(lck);
@@ -349,9 +351,10 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
 			return NT_STATUS_INTERNAL_DB_CORRUPTION;
 		}
 
-		status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
+		status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
 				       share_access, access_mask, del_on_close, 
-				       name->full_name, OPLOCK_NONE, NULL);
+				       io->generic.in.open_disposition,
+				       false, OPLOCK_NONE, NULL);
 
 		if (!NT_STATUS_IS_OK(status)) {
 			goto cleanup_delete;
@@ -669,9 +672,10 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
 		oplock_level = OPLOCK_EXCLUSIVE;
 	}
 
-	status = odb_open_file(lck, f->handle, name->stream_id,
+	status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
 			       share_access, access_mask, del_on_close, 
-			       name->full_name, oplock_level, &oplock_granted);
+			       io->generic.in.open_disposition,
+			       false, oplock_level, &oplock_granted);
 	talloc_free(lck);
 	if (!NT_STATUS_IS_OK(status)) {
 		/* bad news, we must have hit a race - we don't delete the file
@@ -681,9 +685,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
 		return status;
 	}
 
-	if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
-		oplock_granted = OPLOCK_BATCH;
-	}
 
 	f->ntvfs             = h;
 	f->pvfs              = pvfs;
@@ -702,12 +703,23 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
 	f->handle->seek_offset       = 0;
 	f->handle->position          = 0;
 	f->handle->mode              = 0;
+	f->handle->oplock            = NULL;
 	f->handle->have_opendb_entry = true;
 	f->handle->sticky_write_time = false;
 	f->handle->open_completed    = false;
 
 	DLIST_ADD(pvfs->files.list, f);
 
+	if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+		oplock_granted = OPLOCK_BATCH;
+	} else if (oplock_granted != OPLOCK_NONE) {
+		status = pvfs_setup_oplock(f, oplock_granted);
+		if (!NT_STATUS_IS_OK(status)) {
+			talloc_free(lck);
+			return status;
+		}
+	}
+
 	/* setup a destructor to avoid file descriptor leaks on
 	   abnormal termination */
 	talloc_set_destructor(f, pvfs_fnum_destructor);
@@ -748,20 +760,25 @@ cleanup_delete:
 	return status;
 }
 
-
 /*
-  state of a pending open retry
+  state of a pending retry
 */
-struct pvfs_open_retry {
+struct pvfs_odb_retry {
 	struct ntvfs_module_context *ntvfs;
 	struct ntvfs_request *req;
-	union smb_open *io;
-	struct pvfs_wait *wait_handle;
 	DATA_BLOB odb_locking_key;
+	void *io;
+	void *private_data;
+	void (*callback)(struct pvfs_odb_retry *r,
+			 struct ntvfs_module_context *ntvfs,
+			 struct ntvfs_request *req,
+			 void *io,
+			 void *private_data,
+			 enum pvfs_wait_notice reason);
 };
 
-/* destroy a pending open request */
-static int pvfs_retry_destructor(struct pvfs_open_retry *r)
+/* destroy a pending request */
+static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
 {
 	struct pvfs_state *pvfs = r->ntvfs->private_data;
 	if (r->odb_locking_key.data) {
@@ -775,15 +792,92 @@ static int pvfs_retry_destructor(struct pvfs_open_retry *r)
 	return 0;
 }
 
-/*
-  retry an open
-*/
-static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
+static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
 {
-	struct pvfs_open_retry *r = private;
-	struct ntvfs_module_context *ntvfs = r->ntvfs;
-	struct ntvfs_request *req = r->req;
-	union smb_open *io = r->io;
+	struct pvfs_odb_retry *r = talloc_get_type(_r, struct pvfs_odb_retry);
+
+	if (reason == PVFS_WAIT_EVENT) {
+		/*
+		 * The pending odb entry is already removed.
+		 * We use a null locking key to indicate this
+		 * to the destructor.
+		 */
+		data_blob_free(&r->odb_locking_key);
+	}
+
+	r->callback(r, r->ntvfs, r->req, r->io, r->private_data, reason);
+}
+
+/*
+  setup for a retry of a request that was rejected
+  by odb_open_file() or odb_can_open()
+*/
+NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
+			      struct ntvfs_request *req,
+			      struct odb_lock *lck,
+			      struct timeval end_time,
+			      void *io,
+			      void *private_data,
+			      void (*callback)(struct pvfs_odb_retry *r,
+					       struct ntvfs_module_context *ntvfs,
+					       struct ntvfs_request *req,
+					       void *io,
+					       void *private_data,
+					       enum pvfs_wait_notice reason))
+{
+	struct pvfs_state *pvfs = ntvfs->private_data;
+	struct pvfs_odb_retry *r;
+	struct pvfs_wait *wait_handle;
+	NTSTATUS status;
+
+	r = talloc(req, struct pvfs_odb_retry);
+	NT_STATUS_HAVE_NO_MEMORY(r);
+
+	r->ntvfs = ntvfs;
+	r->req = req;
+	r->io = io;
+	r->private_data = private_data;
+	r->callback = callback;
+	r->odb_locking_key = odb_get_key(r, lck);
+	if (r->odb_locking_key.data == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	/* setup a pending lock */
+	status = odb_open_file_pending(lck, r);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	talloc_free(lck);
+
+	talloc_set_destructor(r, pvfs_odb_retry_destructor);
+
+	wait_handle = pvfs_wait_message(pvfs, req,
+					MSG_PVFS_RETRY_OPEN, end_time,
+					pvfs_odb_retry_callback, r);
+	if (wait_handle == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	talloc_steal(r, wait_handle);
+
+	talloc_steal(pvfs, r);
+
+	return NT_STATUS_OK;
+}
+
+/*
+  retry an open after a sharing violation
+*/
+static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
+				    struct ntvfs_module_context *ntvfs,
+				    struct ntvfs_request *req,
+				    void *_io,
+				    void *private_data,
+				    enum pvfs_wait_notice reason)
+{
+	union smb_open *io = talloc_get_type(_io, union smb_open);
 	NTSTATUS status;
 
 	/* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
@@ -792,8 +886,6 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
 		return;
 	}
 
-	talloc_free(r->wait_handle);
-
 	if (reason == PVFS_WAIT_TIMEOUT) {
 		/* if it timed out, then give the failure
 		   immediately */
@@ -803,9 +895,6 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
 		return;
 	}
 
-	/* the pending odb entry is already removed. We use a null locking
-	   key to indicate this */
-	data_blob_free(&r->odb_locking_key);
 	talloc_free(r);
 
 	/* try the open again, which could trigger another retry setup
@@ -919,10 +1008,10 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
 				      struct ntvfs_request *req, 
 				      union smb_open *io,
 				      struct pvfs_file *f,
-				      struct odb_lock *lck)
+				      struct odb_lock *lck,
+				      NTSTATUS parent_status)
 {
 	struct pvfs_state *pvfs = ntvfs->private_data;
-	struct pvfs_open_retry *r;
 	NTSTATUS status;
 	struct timeval end_time;
 
@@ -936,40 +1025,21 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
 		}
 	}
 
-	r = talloc(req, struct pvfs_open_retry);
-	if (r == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	r->ntvfs = ntvfs;
-	r->req = req;
-	r->io = io;
-	r->odb_locking_key = data_blob_talloc(r, 
-					      f->handle->odb_locking_key.data, 
-					      f->handle->odb_locking_key.length);
-
-	end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
-
-	/* setup a pending lock */
-	status = odb_open_file_pending(lck, r);
-	if (!NT_STATUS_IS_OK(status)) {
-		return status;
-	}
-
-	talloc_free(lck);
+	/* the retry should allocate a new file handle */
 	talloc_free(f);
 
-	talloc_set_destructor(r, pvfs_retry_destructor);
-
-	r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time, 
-					   pvfs_open_retry, r);
-	if (r->wait_handle == NULL) {
-		return NT_STATUS_NO_MEMORY;
+	if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) {
+		end_time = timeval_add(&req->statistics.request_time,
+				       0, pvfs->sharing_violation_delay);
+	} else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+		end_time = timeval_add(&req->statistics.request_time,
+				       pvfs->oplock_break_timeout, 0);
+	} else {
+		return NT_STATUS_INTERNAL_ERROR;
 	}
 
-	talloc_steal(pvfs, r);
-
-	return NT_STATUS_OK;
+	return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
+				    pvfs_retry_open_sharing);
 }
 
 /*
@@ -1133,6 +1203,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
 	f->handle->seek_offset       = 0;
 	f->handle->position          = 0;
 	f->handle->mode              = 0;
+	f->handle->oplock            = NULL;
 	f->handle->have_opendb_entry = false;
 	f->handle->sticky_write_time = false;
 	f->handle->open_completed    = false;
@@ -1186,15 +1257,21 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
 	}
 
 	/* see if we are allowed to open at the same time as existing opens */
-	status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
+	status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
 			       share_access, access_mask, del_on_close,
-			       name->full_name, oplock_level, &oplock_granted);
+			       io->generic.in.open_disposition,
+			       false, oplock_level, &oplock_granted);
 
-	/* on a sharing violation we need to retry when the file is closed by 
-	   the other user, or after 1 second */
-	if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
+	/*
+	 * on a sharing violation we need to retry when the file is closed by
+	 * the other user, or after 1 second
+	 * on a non granted oplock we need to retry when the file is closed by
+	 * the other user, or after 30 seconds
+	*/
+	if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+	     NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
 	    (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
-		return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
+		return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
 	}
 
 	if (!NT_STATUS_IS_OK(status)) {
@@ -1204,6 +1281,12 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
 
 	if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
 		oplock_granted = OPLOCK_BATCH;
+	} else if (oplock_granted != OPLOCK_NONE) {
+		status = pvfs_setup_oplock(f, oplock_granted);
+		if (!NT_STATUS_IS_OK(status)) {
+			talloc_free(lck);
+			return status;
+		}
 	}
 
 	f->handle->have_opendb_entry = true;
@@ -1424,6 +1507,9 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
 	NTSTATUS status;
 	DATA_BLOB key;
 	struct odb_lock *lck;
+	uint32_t share_access;
+	uint32_t access_mask;
+	bool delete_on_close;
 
 	status = pvfs_locking_key(name, name, &key);
 	if (!NT_STATUS_IS_OK(status)) {
@@ -1436,15 +1522,18 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
 		return NT_STATUS_INTERNAL_DB_CORRUPTION;
 	}
 
-	status = odb_can_open(lck,
-			      NTCREATEX_SHARE_ACCESS_READ |
-			      NTCREATEX_SHARE_ACCESS_WRITE | 
-			      NTCREATEX_SHARE_ACCESS_DELETE, 
-			      NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 
-			      SEC_STD_DELETE);
+	share_access	= NTCREATEX_SHARE_ACCESS_READ |
+			  NTCREATEX_SHARE_ACCESS_WRITE |
+			  NTCREATEX_SHARE_ACCESS_DELETE;
+	access_mask	= SEC_STD_DELETE;
+	delete_on_close	= true;
+
+	status = odb_can_open(lck, name->stream_id,
+			      share_access, access_mask, delete_on_close,
+			      0, false);
 
 	if (NT_STATUS_IS_OK(status)) {
-		status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
+		status = pvfs_access_check_simple(pvfs, req, name, access_mask);
 	}
 
 	/*
@@ -1483,6 +1572,9 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
 	NTSTATUS status;
 	DATA_BLOB key;
 	struct odb_lock *lck;
+	uint32_t share_access;
+	uint32_t access_mask;
+	bool delete_on_close;
 
 	status = pvfs_locking_key(name, name, &key);
 	if (!NT_STATUS_IS_OK(status)) {
@@ -1495,11 +1587,76 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
 		return NT_STATUS_INTERNAL_DB_CORRUPTION;
 	}
 
-	status = odb_can_open(lck,
-			      NTCREATEX_SHARE_ACCESS_READ |
-			      NTCREATEX_SHARE_ACCESS_WRITE,
-			      0,
-			      SEC_STD_DELETE);
+	share_access	= NTCREATEX_SHARE_ACCESS_READ |
+			  NTCREATEX_SHARE_ACCESS_WRITE;
+	access_mask	= SEC_STD_DELETE;
+	delete_on_close	= false;
+
+	status = odb_can_open(lck, name->stream_id,
+			      share_access, access_mask, delete_on_close,
+			      0, false);
+
+	/*
+	 * if it's a sharing violation or we got no oplock
+	 * only keep the lock if the caller requested access
+	 * to the lock
+	 */
+	if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+	    NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+		if (lckp) {
+			*lckp = lck;
+		} else {
+			talloc_free(lck);
+		}
+	} else if (!NT_STATUS_IS_OK(status)) {
+		talloc_free(lck);
+		if (lckp) {
+			*lckp = NULL;
+		}
+	} else if (lckp) {
+		*lckp = lck;
+	}
+
+	return status;
+}
+
+/*
+  determine if the file size of a file can be changed,
+  or if it is prevented by an already open file
+*/
+NTSTATUS pvfs_can_update_file_size(struct pvfs_state *pvfs,
+				   struct ntvfs_request *req,
+				   struct pvfs_filename *name,
+				   struct odb_lock **lckp)
+{
+	NTSTATUS status;
+	DATA_BLOB key;
+	struct odb_lock *lck;
+	uint32_t share_access;
+	uint32_t access_mask;
+	bool break_to_none;
+	bool delete_on_close;
+
+	status = pvfs_locking_key(name, name, &key);
+	if (!NT_STATUS_IS_OK(status)) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	lck = odb_lock(req, pvfs->odb_context, &key);
+	if (lck == NULL) {
+		DEBUG(0,("Unable to lock opendb for can_stat\n"));
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+
+	/* TODO: this may needs some more flags */
+	share_access	= NTCREATEX_SHARE_ACCESS_WRITE;
+	access_mask	= 0;
+	delete_on_close	= false;
+	break_to_none	= true;
+
+	status = odb_can_open(lck, name->stream_id,
+			      share_access, access_mask, delete_on_close,
+			      0, break_to_none);
 
 	/*
 	 * if it's a sharing violation or we got no oplock
@@ -1536,6 +1693,9 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
 	NTSTATUS status;
 	DATA_BLOB key;
 	struct odb_lock *lck;
+	uint32_t share_access;
+	uint32_t access_mask;
+	bool delete_on_close;
 
 	status = pvfs_locking_key(name, name, &key);
 	if (!NT_STATUS_IS_OK(status)) {
@@ -1548,10 +1708,14 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
 		return NT_STATUS_INTERNAL_DB_CORRUPTION;
 	}
 
-	status = odb_can_open(lck,
-			      NTCREATEX_SHARE_ACCESS_READ |
-			      NTCREATEX_SHARE_ACCESS_WRITE,
-			      0, 0);
+	share_access	= NTCREATEX_SHARE_ACCESS_READ |
+			  NTCREATEX_SHARE_ACCESS_WRITE;
+	access_mask	= 0;
+	delete_on_close	= false;
+
+	status = odb_can_open(lck, name->stream_id,
+			      share_access, access_mask, delete_on_close,
+			      0, false);
 
 	if (!NT_STATUS_IS_OK(status)) {
 		talloc_free(lck);
diff --git a/source/ntvfs/posix/pvfs_oplock.c b/source/ntvfs/posix/pvfs_oplock.c
new file mode 100644
index 00000000000..cf30ddbc59b
--- /dev/null
+++ b/source/ntvfs/posix/pvfs_oplock.c
@@ -0,0 +1,239 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   POSIX NTVFS backend - oplock handling
+
+   Copyright (C) Stefan Metzmacher 2008
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see .
+*/
+
+#include "includes.h"
+#include "lib/messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "vfs_posix.h"
+
+
+struct pvfs_oplock {
+	struct pvfs_file_handle *handle;
+	struct pvfs_file *file;
+	uint32_t level;
+	struct messaging_context *msg_ctx;
+};
+
+/*
+  receive oplock breaks and forward them to the client
+*/
+static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
+{
+	NTSTATUS status;
+	struct pvfs_file *f = opl->file;
+	struct pvfs_file_handle *h = opl->handle;
+	struct pvfs_state *pvfs = h->pvfs;
+
+	DEBUG(10,("pvfs_oplock_break: sending oplock break level %d for '%s' %p\n",
+		level, h->name->original_name, h));
+	status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0,("pvfs_oplock_break: sending oplock break failed: %s\n",
+			nt_errstr(status)));
+	}
+}
+
+static void pvfs_oplock_break_dispatch(struct messaging_context *msg,
+				       void *private_data, uint32_t msg_type,
+				       struct server_id src, DATA_BLOB *data)
+{
+	struct pvfs_oplock *opl = talloc_get_type(private_data,
+						  struct pvfs_oplock);
+	struct opendb_oplock_break opb;
+
+	ZERO_STRUCT(opb);
+
+	/* we need to check that this one is for us. See
+	   messaging_send_ptr() for the other side of this.
+	 */
+	if (data->length == sizeof(struct opendb_oplock_break)) {
+		struct opendb_oplock_break *p;
+		p = (struct opendb_oplock_break *)data->data;
+		opb = *p;
+	} else {
+		DEBUG(0,("%s: ignore oplock break with length[%u]\n",
+			__location__, data->length));
+		return;
+	}
+	if (opb.file_handle != opl->handle) {
+		return;
+	}
+
+	/*
+	 * maybe we should use ntvfs_setup_async()
+	 */
+	pvfs_oplock_break(opl, opb.level);
+}
+
+static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
+{
+	messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
+	return 0;
+}
+
+NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
+{
+	NTSTATUS status;
+	struct pvfs_oplock *opl;
+	uint32_t level = OPLOCK_NONE;
+
+	f->handle->oplock = NULL;
+
+	switch (oplock_granted) {
+	case EXCLUSIVE_OPLOCK_RETURN:
+		level = OPLOCK_EXCLUSIVE;
+		break;
+	case BATCH_OPLOCK_RETURN:
+		level = OPLOCK_BATCH;
+		break;
+	case LEVEL_II_OPLOCK_RETURN:
+		level = OPLOCK_LEVEL_II;
+		break;
+	}
+
+	if (level == OPLOCK_NONE) {
+		return NT_STATUS_OK;
+	}
+
+	opl = talloc(f->handle, struct pvfs_oplock);
+	NT_STATUS_HAVE_NO_MEMORY(opl);
+
+	opl->handle	= f->handle;
+	opl->file	= f;
+	opl->level	= level;
+	opl->msg_ctx	= f->pvfs->ntvfs->ctx->msg_ctx;
+
+	status = messaging_register(opl->msg_ctx,
+				    opl,
+				    MSG_NTVFS_OPLOCK_BREAK,
+				    pvfs_oplock_break_dispatch);
+	NT_STATUS_NOT_OK_RETURN(status);
+
+	/* destructor */
+	talloc_set_destructor(opl, pvfs_oplock_destructor);
+
+	f->handle->oplock = opl;
+
+	return NT_STATUS_OK;
+}
+
+NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
+			     struct ntvfs_request *req, union smb_lock *lck)
+{
+	struct pvfs_state *pvfs = ntvfs->private_data;
+	struct pvfs_file *f;
+	struct pvfs_file_handle *h;
+	struct odb_lock *olck;
+	uint8_t oplock_break;
+	NTSTATUS status;
+
+	f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
+	if (!f) {
+		return NT_STATUS_INVALID_HANDLE;
+	}
+
+	h = f->handle;
+
+	if (h->fd == -1) {
+		return NT_STATUS_FILE_IS_A_DIRECTORY;
+	}
+
+	if (!h->have_opendb_entry) {
+		return NT_STATUS_FOOBAR;
+	}
+
+	if (!h->oplock) {
+		return NT_STATUS_FOOBAR;
+	}
+
+	olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+	if (olck == NULL) {
+		DEBUG(0,("Unable to lock opendb for oplock update\n"));
+		return NT_STATUS_FOOBAR;
+	}
+
+	oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
+	if (oplock_break == OPLOCK_BREAK_TO_NONE) {
+		h->oplock->level = OPLOCK_NONE;
+	} else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
+		h->oplock->level = OPLOCK_LEVEL_II;
+	} else {
+		/* fallback to level II in case of a invalid value */
+		DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
+		h->oplock->level = OPLOCK_LEVEL_II;
+	}
+	status = odb_update_oplock(olck, h, h->oplock->level);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
+			 h->name->full_name, nt_errstr(status)));
+		talloc_free(olck);
+		return status;
+	}
+
+	talloc_free(olck);
+
+	/* after a break to none, we no longer have an oplock attached */
+	if (h->oplock->level == OPLOCK_NONE) {
+		talloc_free(h->oplock);
+		h->oplock = NULL;
+	}
+
+	return NT_STATUS_OK;
+}
+
+NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
+{
+	struct pvfs_file_handle *h = f->handle;
+	struct odb_lock *olck;
+	NTSTATUS status;
+
+	if (h->oplock && h->oplock->level == OPLOCK_EXCLUSIVE) {
+		return NT_STATUS_OK;
+	}
+
+	olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+	if (olck == NULL) {
+		DEBUG(0,("Unable to lock opendb for oplock update\n"));
+		return NT_STATUS_FOOBAR;
+	}
+
+	if (h->oplock && h->oplock->level == OPLOCK_BATCH) {
+		status = odb_update_oplock(olck, h, OPLOCK_LEVEL_II);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
+				 h->name->full_name, nt_errstr(status)));
+			talloc_free(olck);
+			return status;
+		}
+	}
+
+	status = odb_break_oplocks(olck);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
+			 h->name->full_name, nt_errstr(status)));
+		talloc_free(olck);
+		return status;
+	}
+
+	talloc_free(olck);
+
+	return NT_STATUS_OK;
+}
diff --git a/source/ntvfs/posix/pvfs_setfileinfo.c b/source/ntvfs/posix/pvfs_setfileinfo.c
index fbbb8c2d4ba..da1e31e6390 100644
--- a/source/ntvfs/posix/pvfs_setfileinfo.c
+++ b/source/ntvfs/posix/pvfs_setfileinfo.c
@@ -152,6 +152,9 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
 		}
 
 		status = pvfs_can_delete(pvfs, req, name2, NULL);
+		if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
+			return NT_STATUS_ACCESS_DENIED;
+		}
 		if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
 			return NT_STATUS_ACCESS_DENIED;
 		}
@@ -352,6 +355,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
 
 	case RAW_SFILEINFO_ALLOCATION_INFO:
 	case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+		status = pvfs_break_level2_oplocks(f);
+		NT_STATUS_NOT_OK_RETURN(status);
+
 		newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
 		if (newstats.dos.alloc_size < newstats.st.st_size) {
 			newstats.st.st_size = newstats.dos.alloc_size;
@@ -362,6 +368,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
 
 	case RAW_SFILEINFO_END_OF_FILE_INFO:
 	case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+		status = pvfs_break_level2_oplocks(f);
+		NT_STATUS_NOT_OK_RETURN(status);
+
 		newstats.st.st_size = info->end_of_file_info.in.size;
 		break;
 
@@ -463,6 +472,84 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
 	return pvfs_dosattrib_save(pvfs, h->name, h->fd);
 }
 
+/*
+  retry an open after a sharing violation
+*/
+static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
+				   struct ntvfs_module_context *ntvfs,
+				   struct ntvfs_request *req,
+				   void *_info,
+				   void *private_data,
+				   enum pvfs_wait_notice reason)
+{
+	union smb_setfileinfo *info = talloc_get_type(_info,
+				      union smb_setfileinfo);
+	NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+
+	talloc_free(r);
+
+	switch (reason) {
+	case PVFS_WAIT_CANCEL:
+/*TODO*/
+		status = NT_STATUS_CANCELLED;
+		break;
+	case PVFS_WAIT_TIMEOUT:
+		/* if it timed out, then give the failure
+		   immediately */
+/*TODO*/
+		status = NT_STATUS_SHARING_VIOLATION;
+		break;
+	case PVFS_WAIT_EVENT:
+
+		/* try the open again, which could trigger another retry setup
+		   if it wants to, so we have to unmark the async flag so we
+		   will know if it does a second async reply */
+		req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
+
+		status = pvfs_setpathinfo(ntvfs, req, info);
+		if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+			/* the 2nd try also replied async, so we don't send
+			   the reply yet */
+			return;
+		}
+
+		/* re-mark it async, just in case someone up the chain does
+		   paranoid checking */
+		req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+		break;
+	}
+
+	/* send the reply up the chain */
+	req->async_states->status = status;
+	req->async_states->send_fn(req);
+}
+
+/*
+  setup for a unlink retry after a sharing violation
+  or a non granted oplock
+*/
+static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
+					     struct ntvfs_request *req,
+					     union smb_setfileinfo *info,
+					     struct odb_lock *lck,
+					     NTSTATUS status)
+{
+	struct pvfs_state *pvfs = ntvfs->private_data;
+	struct timeval end_time;
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+		end_time = timeval_add(&req->statistics.request_time,
+				       0, pvfs->sharing_violation_delay);
+	} else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+		end_time = timeval_add(&req->statistics.request_time,
+				       pvfs->oplock_break_timeout, 0);
+	} else {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
+				    pvfs_retry_setpathinfo);
+}
 
 /*
   set info on a pathname
@@ -477,6 +564,7 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
 	struct utimbuf unix_times;
 	uint32_t access_needed;
 	uint32_t change_mask = 0;
+	struct odb_lock *lck = NULL;
 
 	/* resolve the cifs name to a posix name */
 	status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, 
@@ -551,6 +639,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
 
 	case RAW_SFILEINFO_ALLOCATION_INFO:
 	case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+		status = pvfs_can_update_file_size(pvfs, req, name, &lck);
+		/*
+		 * on a sharing violation we need to retry when the file is closed by
+		 * the other user, or after 1 second
+		 * on a non granted oplock we need to retry when the file is closed by
+		 * the other user, or after 30 seconds
+		*/
+		if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+		     NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+		    (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+			return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
+		}
+		NT_STATUS_NOT_OK_RETURN(status);
+
 		if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
 			/* strange. Increasing the allocation size via setpathinfo 
 			   should be silently ignored */
@@ -566,6 +668,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
 
 	case RAW_SFILEINFO_END_OF_FILE_INFO:
 	case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+		status = pvfs_can_update_file_size(pvfs, req, name, &lck);
+		/*
+		 * on a sharing violation we need to retry when the file is closed by
+		 * the other user, or after 1 second
+		 * on a non granted oplock we need to retry when the file is closed by
+		 * the other user, or after 30 seconds
+		*/
+		if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+		     NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+		    (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+			return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
+		}
+		NT_STATUS_NOT_OK_RETURN(status);
+
 		newstats.st.st_size = info->end_of_file_info.in.size;
 		break;
 
diff --git a/source/ntvfs/posix/pvfs_unlink.c b/source/ntvfs/posix/pvfs_unlink.c
index bda4014beec..7a2d964b9dd 100644
--- a/source/ntvfs/posix/pvfs_unlink.c
+++ b/source/ntvfs/posix/pvfs_unlink.c
@@ -23,6 +23,84 @@
 #include "vfs_posix.h"
 #include "system/dir.h"
 
+/*
+  retry an open after a sharing violation
+*/
+static void pvfs_retry_unlink(struct pvfs_odb_retry *r,
+			      struct ntvfs_module_context *ntvfs,
+			      struct ntvfs_request *req,
+			      void *_io,
+			      void *private_data,
+			      enum pvfs_wait_notice reason)
+{
+	union smb_unlink *io = talloc_get_type(_io, union smb_unlink);
+	NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+
+	talloc_free(r);
+
+	switch (reason) {
+	case PVFS_WAIT_CANCEL:
+/*TODO*/
+		status = NT_STATUS_CANCELLED;
+		break;
+	case PVFS_WAIT_TIMEOUT:
+		/* if it timed out, then give the failure
+		   immediately */
+/*TODO*/
+		status = NT_STATUS_SHARING_VIOLATION;
+		break;
+	case PVFS_WAIT_EVENT:
+
+		/* try the open again, which could trigger another retry setup
+		   if it wants to, so we have to unmark the async flag so we
+		   will know if it does a second async reply */
+		req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
+
+		status = pvfs_unlink(ntvfs, req, io);
+		if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+			/* the 2nd try also replied async, so we don't send
+			   the reply yet */
+			return;
+		}
+
+		/* re-mark it async, just in case someone up the chain does
+		   paranoid checking */
+		req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+		break;
+	}
+
+	/* send the reply up the chain */
+	req->async_states->status = status;
+	req->async_states->send_fn(req);
+}
+
+/*
+  setup for a unlink retry after a sharing violation
+  or a non granted oplock
+*/
+static NTSTATUS pvfs_unlink_setup_retry(struct ntvfs_module_context *ntvfs,
+					struct ntvfs_request *req,
+					union smb_unlink *io,
+					struct odb_lock *lck,
+					NTSTATUS status)
+{
+	struct pvfs_state *pvfs = ntvfs->private_data;
+	struct timeval end_time;
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+		end_time = timeval_add(&req->statistics.request_time,
+				       0, pvfs->sharing_violation_delay);
+	} else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+		end_time = timeval_add(&req->statistics.request_time,
+				       pvfs->oplock_break_timeout, 0);
+	} else {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
+				    pvfs_retry_unlink);
+}
+
 
 /*
   unlink a file
@@ -67,6 +145,7 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
 				struct pvfs_filename *name)
 {
 	NTSTATUS status;
+	struct odb_lock *lck = NULL;
 
 	/* make sure its matches the given attributes */
 	status = pvfs_match_attrib(pvfs, name,
@@ -75,7 +154,20 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
 		return status;
 	}
 
-	status = pvfs_can_delete(pvfs, req, name, NULL);
+	status = pvfs_can_delete(pvfs, req, name, &lck);
+
+	/*
+	 * on a sharing violation we need to retry when the file is closed by
+	 * the other user, or after 1 second
+	 * on a non granted oplock we need to retry when the file is closed by
+	 * the other user, or after 30 seconds
+	 */
+	if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+	     NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+	    (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+		return pvfs_unlink_setup_retry(pvfs->ntvfs, req, unl, lck, status);
+	}
+
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
diff --git a/source/ntvfs/posix/pvfs_write.c b/source/ntvfs/posix/pvfs_write.c
index 5a11f01952b..dda8c834073 100644
--- a/source/ntvfs/posix/pvfs_write.c
+++ b/source/ntvfs/posix/pvfs_write.c
@@ -57,7 +57,10 @@ NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs,
 				 wr->writex.in.count,
 				 WRITE_LOCK);
 	NT_STATUS_NOT_OK_RETURN(status);
-	
+
+	status = pvfs_break_level2_oplocks(f);
+	NT_STATUS_NOT_OK_RETURN(status);
+
 	if (f->handle->name->stream_name) {
 		ret = pvfs_stream_write(pvfs,
 					f->handle,
diff --git a/source/ntvfs/posix/vfs_posix.c b/source/ntvfs/posix/vfs_posix.c
index e058e6f546d..ca874d1db1f 100644
--- a/source/ntvfs/posix/vfs_posix.c
+++ b/source/ntvfs/posix/vfs_posix.c
@@ -91,6 +91,10 @@ static void pvfs_setup_options(struct pvfs_state *pvfs)
 							PVFS_SHARE_DELAY,
 							PVFS_SHARE_DELAY_DEFAULT);
 
+	pvfs->oplock_break_timeout = share_int_option(scfg,
+						      PVFS_OPLOCK_TIMEOUT,
+						      PVFS_OPLOCK_TIMEOUT_DEFAULT);
+
 	pvfs->share_name = talloc_strdup(pvfs, scfg->name);
 
 	pvfs->fs_attribs = 
diff --git a/source/ntvfs/posix/vfs_posix.h b/source/ntvfs/posix/vfs_posix.h
index 84c456dcfee..4d22a917149 100644
--- a/source/ntvfs/posix/vfs_posix.h
+++ b/source/ntvfs/posix/vfs_posix.h
@@ -29,6 +29,7 @@
 #include "dsdb/samdb/samdb.h"
 
 struct pvfs_wait;
+struct pvfs_oplock;
 
 /* this is the private structure for the posix vfs backend. It is used
    to hold per-connection (per tree connect) state information */
@@ -51,9 +52,12 @@ struct pvfs_state {
 	   ntcancel */
 	struct pvfs_wait *wait_list;
 
-	/* the sharing violation timeout */
+	/* the sharing violation timeout (nsecs) */
 	uint_t sharing_violation_delay;
 
+	/* the oplock break timeout (secs) */
+	uint_t oplock_break_timeout;
+
 	/* filesystem attributes (see FS_ATTR_*) */
 	uint32_t fs_attribs;
 
@@ -154,6 +158,13 @@ struct pvfs_file_handle {
 
 	bool have_opendb_entry;
 
+	/*
+	 * we need to wait for oplock break requests from other processes,
+	 * and we need to remember the pvfs_file so we can correctly
+	 * forward the oplock break to the client
+	 */
+	struct pvfs_oplock *oplock;
+
 	/* we need this hook back to our parent for lock destruction */
 	struct pvfs_state *pvfs;
 
@@ -230,10 +241,16 @@ struct pvfs_dir;
 /* types of notification for pvfs wait events */
 enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
 
+/*
+  state of a pending retry
+*/
+struct pvfs_odb_retry;
+
 #define PVFS_EADB			"posix:eadb"
 #define PVFS_XATTR			"posix:xattr"
 #define PVFS_FAKE_OPLOCKS		"posix:fakeoplocks"
 #define PVFS_SHARE_DELAY		"posix:sharedelay"
+#define PVFS_OPLOCK_TIMEOUT		"posix:oplocktimeout"
 #define PVFS_ALLOCATION_ROUNDING	"posix:allocationrounding"
 #define PVFS_SEARCH_INACTIVITY		"posix:searchinactivity"
 #define PVFS_ACL			"posix:acl"
@@ -241,7 +258,8 @@ enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
 
 #define PVFS_XATTR_DEFAULT			true
 #define PVFS_FAKE_OPLOCKS_DEFAULT		false
-#define PVFS_SHARE_DELAY_DEFAULT		1000000
+#define PVFS_SHARE_DELAY_DEFAULT		1000000 /* nsecs */
+#define PVFS_OPLOCK_TIMEOUT_DEFAULT		30 /* secs */
 #define PVFS_ALLOCATION_ROUNDING_DEFAULT	512
 #define PVFS_SEARCH_INACTIVITY_DEFAULT		300
 
diff --git a/source/samba4-knownfail b/source/samba4-knownfail
index 18fb4b914bb..4d850caae5f 100644
--- a/source/samba4-knownfail
+++ b/source/samba4-knownfail
@@ -3,7 +3,6 @@ local.iconv.*.next_codepoint()
 base.delaywrite.finfo update on close
 base.delete.*.deltest20a
 base.delete.*.deltest20b
-raw.oplock.*.OPLOCK
 rpc.winreg
 local.registry.*.security # Not implemented yet
 rpc.wkssvc
diff --git a/source/samba4-skip b/source/samba4-skip
index 541738ecc4e..4d2da6ed646 100644
--- a/source/samba4-skip
+++ b/source/samba4-skip
@@ -1,12 +1,10 @@
 base.delaywrite
 raw.composite
-raw.oplock
 base.iometer
 base.casetable
 base.nttrans
 .*base.bench.holdcon.*				# Very slow
 base.scan.maxfid
-raw.bench.oplock
 raw.hold.oplock
 raw.ping.pong
 rpc.samr_accessmask
@@ -25,9 +23,7 @@ ntvfs.cifs.base.scan-maxfid
 ntvfs.cifs.base.utable
 ntvfs.cifs.base.smb
 ntvfs.cifs.raw.composite
-ntvfs.cifs.raw.oplock
 ntvfs.cifs.raw.notify
-ntvfs.cifs.raw.bench-oplock
 ntvfs.cifs.raw.scan-eamax
 ntvfs.cifs.raw.context
 ntvfs.cifs.raw.qfileinfo.ipc
diff --git a/source/selftest/target/Samba4.pm b/source/selftest/target/Samba4.pm
index 37e3cbe3549..e0be8048a0c 100644
--- a/source/selftest/target/Samba4.pm
+++ b/source/selftest/target/Samba4.pm
@@ -562,12 +562,16 @@ sub provision($$$$$$)
 	gensec:require_pac = true
 	log level = $smbd_loglevel
 
+	# this is a global option
+	opendb:oplocks = yes
+
 [tmp]
 	path = $tmpdir
 	read only = no
 	ntvfs handler = posix
 	posix:sharedelay = 100000
 	posix:eadb = $lockdir/eadb.tdb
+	posix:oplocktimeout = 3
 
 [test1]
 	path = $tmpdir/test1
@@ -575,6 +579,7 @@ sub provision($$$$$$)
 	ntvfs handler = posix
 	posix:sharedelay = 100000
 	posix:eadb = $lockdir/eadb.tdb
+	posix:oplocktimeout = 3
 
 [test2]
 	path = $tmpdir/test2
@@ -582,6 +587,7 @@ sub provision($$$$$$)
 	ntvfs handler = posix
 	posix:sharedelay = 100000
 	posix:eadb = $lockdir/eadb.tdb
+	posix:oplocktimeout = 3
 
 [cifs]
 	read only = no
diff --git a/source/torture/basic/base.c b/source/torture/basic/base.c
index 42d7ddaaa12..66f9359744d 100644
--- a/source/torture/basic/base.c
+++ b/source/torture/basic/base.c
@@ -664,7 +664,7 @@ static bool run_deferopen(struct torture_context *tctx, struct smbcli_state *cli
 			}
 			if (NT_STATUS_EQUAL(smbcli_nt_error(cli->tree),NT_STATUS_SHARING_VIOLATION)) {
 				double e = timeval_elapsed(&tv);
-				if (e < (0.5 * sec) || e > (1.5 * sec)) {
+				if (e < (0.5 * sec) || e > ((1.5 * sec) + 1)) {
 					torture_comment(tctx,"Timing incorrect %.2f violation 1 sec == %.2f\n",
 						e, sec);
 					return false;