make the SEC_STD_SYNCHRONIZE test more specific
[kai/samba.git] / source4 / ntvfs / posix / pvfs_open.c
index 74f9b44c5c2df66215be3ebaf53fb04f37c06de9..cfa88b6baad40e8dbb511fa7a343f7cd4bf2efc4 100644 (file)
@@ -7,7 +7,7 @@
 
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "vfs_posix.h"
 #include "system/dir.h"
 #include "system/time.h"
-#include "dlinklist.h"
+#include "lib/util/dlinklist.h"
 #include "messaging/messaging.h"
 #include "librpc/gen_ndr/xattr.h"
 
@@ -43,11 +42,6 @@ struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
        f = talloc_get_type(p, struct pvfs_file);
        if (!f) return NULL;
 
-       if (req->session_info != f->session_info) {
-               DEBUG(2,("pvfs_find_fd: attempt to use wrong session for handle %p\n",h));
-               return NULL;
-       }
-
        return f;
 }
 
@@ -56,29 +50,10 @@ struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
 */
 static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
 {
-       int open_count;
-       char *path = NULL;
-
-       if (h->name->stream_name == NULL && 
-           pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
-           open_count == 1) {
-               NTSTATUS status;
-               status = pvfs_xattr_unlink_hook(h->pvfs, path);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
-                                path, nt_errstr(status)));
-               }
-               if (rmdir(path) != 0) {
-                       DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n", 
-                                path, strerror(errno)));
-               }
-       }
-
-       talloc_free(path);
-
        if (h->have_opendb_entry) {
                struct odb_lock *lck;
                NTSTATUS status;
+               const char *delete_path = NULL;
 
                lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
                if (lck == NULL) {
@@ -86,12 +61,24 @@ static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
                        return 0;
                }
 
-               status = odb_close_file(lck, h);
+               status = odb_close_file(lck, h, &delete_path);
                if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", 
+                       DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
                                 h->name->full_name, nt_errstr(status)));
                }
 
+               if (h->name->stream_name == NULL && delete_path) {
+                       status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
+                                        delete_path, nt_errstr(status)));
+                       }
+                       if (rmdir(delete_path) != 0) {
+                               DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
+                                        delete_path, strerror(errno)));
+                       }
+               }
+
                talloc_free(lck);
        }
 
@@ -157,8 +144,8 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
   form the lock context used for opendb locking. Note that we must
   zero here to take account of possible padding on some architectures
 */
-static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
-                                TALLOC_CTX *mem_ctx, DATA_BLOB *key)
+NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
+                         TALLOC_CTX *mem_ctx, DATA_BLOB *key)
 {
        struct {
                dev_t device;
@@ -192,15 +179,22 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        uint32_t create_action;
        uint32_t access_mask = io->generic.in.access_mask;
        struct odb_lock *lck;
-       BOOL del_on_close;
+       bool del_on_close;
        uint32_t create_options;
        uint32_t share_access;
+       bool forced;
 
        create_options = io->generic.in.create_options;
        share_access   = io->generic.in.share_access;
 
+       forced = (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)?true:false;
+
        if (name->stream_name) {
-               return NT_STATUS_NOT_A_DIRECTORY;
+               if (forced) {
+                       return NT_STATUS_NOT_A_DIRECTORY;
+               } else {
+                       return NT_STATUS_FILE_IS_A_DIRECTORY;
+               }
        }
 
        /* if the client says it must be a directory, and it isn't,
@@ -209,6 +203,13 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                return NT_STATUS_NOT_A_DIRECTORY;
        }
 
+       /* found with gentest */
+       if (io->ntcreatex.in.access_mask == SEC_FLAG_MAXIMUM_ALLOWED &&
+           (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
+           (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
        switch (io->generic.in.open_disposition) {
        case NTCREATEX_DISP_OPEN_IF:
                break;
@@ -256,8 +257,6 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        }
 
        f->ntvfs         = h;
-       f->session_info  = req->session_info;
-       f->smbpid        = req->smbpid;
        f->pvfs          = pvfs;
        f->pending_list  = NULL;
        f->lock_count    = 0;
@@ -266,6 +265,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        f->access_mask   = access_mask;
        f->brl_handle    = NULL;
        f->notify_buffer = NULL;
+       f->search        = NULL;
 
        f->handle->pvfs              = pvfs;
        f->handle->name              = talloc_steal(f->handle, name);
@@ -275,13 +275,14 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        f->handle->seek_offset       = 0;
        f->handle->position          = 0;
        f->handle->mode              = 0;
-       f->handle->sticky_write_time = False;
+       f->handle->oplock            = NULL;
+       f->handle->open_completed    = false;
 
        if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
            pvfs_directory_empty(pvfs, f->handle->name)) {
-               del_on_close = True;
+               del_on_close = true;
        } else {
-               del_on_close = False;
+               del_on_close = false;
        }
 
        if (name->exists) {
@@ -302,16 +303,24 @@ 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,
-                                      share_access, access_mask, del_on_close, 
-                                      name->full_name, OPLOCK_NONE, NULL);
+               status = odb_can_open(lck, name->stream_id,
+                                     share_access, access_mask, del_on_close,
+                                     io->generic.in.open_disposition, false);
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(lck);
+                       return status;
+               }
+
+               /* now really mark the file as open */
+               status = odb_open_file(lck, f->handle, name->full_name,
+                                      NULL, false, OPLOCK_NONE, NULL);
 
                if (!NT_STATUS_IS_OK(status)) {
                        talloc_free(lck);
                        return status;
                }
 
-               f->handle->have_opendb_entry = True;
+               f->handle->have_opendb_entry = true;
        }
 
        DLIST_ADD(pvfs->files.list, f);
@@ -355,15 +364,22 @@ 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,
-                                      share_access, access_mask, del_on_close, 
-                                      name->full_name, OPLOCK_NONE, NULL);
+               status = odb_can_open(lck, name->stream_id,
+                                     share_access, access_mask, del_on_close,
+                                     io->generic.in.open_disposition, false);
 
                if (!NT_STATUS_IS_OK(status)) {
                        goto cleanup_delete;
                }
 
-               f->handle->have_opendb_entry = True;
+               status = odb_open_file(lck, f->handle, name->full_name,
+                                      NULL, false, OPLOCK_NONE, NULL);
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto cleanup_delete;
+               }
+
+               f->handle->have_opendb_entry = true;
 
                create_action = NTCREATEX_ACTION_CREATED;
 
@@ -385,6 +401,8 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                goto cleanup_delete;
        }
 
+       f->handle->open_completed = true;
+
        io->generic.out.oplock_level  = OPLOCK_NONE;
        io->generic.out.file.ntvfs    = h;
        io->generic.out.create_action = create_action;
@@ -411,19 +429,6 @@ cleanup_delete:
 */
 static int pvfs_handle_destructor(struct pvfs_file_handle *h)
 {
-       int open_count;
-       char *path = NULL;
-
-       /* the write time is no longer sticky */
-       if (h->sticky_write_time) {
-               NTSTATUS status;
-               status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
-               if (NT_STATUS_IS_OK(status)) {
-                       h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
-                       pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
-               }
-       }
-       
        if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
            h->name->stream_name) {
                NTSTATUS status;
@@ -442,31 +447,10 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h)
                h->fd = -1;
        }
 
-       if (h->name->stream_name == NULL && 
-           pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
-           open_count == 1) {
-               NTSTATUS status;
-               status = pvfs_xattr_unlink_hook(h->pvfs, path);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
-                                path, nt_errstr(status)));
-               }
-               if (unlink(path) != 0) {
-                       DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n", 
-                                path, strerror(errno)));
-               } else {
-                       notify_trigger(h->pvfs->notify_context, 
-                                      NOTIFY_ACTION_REMOVED, 
-                                      FILE_NOTIFY_CHANGE_FILE_NAME,
-                                      path);
-               }
-       }
-
-       talloc_free(path);
-
        if (h->have_opendb_entry) {
                struct odb_lock *lck;
                NTSTATUS status;
+               const char *delete_path = NULL;
 
                lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
                if (lck == NULL) {
@@ -474,12 +458,30 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h)
                        return 0;
                }
 
-               status = odb_close_file(lck, h);
+               status = odb_close_file(lck, h, &delete_path);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", 
                                 h->name->full_name, nt_errstr(status)));
                }
 
+               if (h->name->stream_name == NULL &&
+                   h->open_completed && delete_path) {
+                       status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
+                                        delete_path, nt_errstr(status)));
+                       }
+                       if (unlink(delete_path) != 0) {
+                               DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
+                                        delete_path, strerror(errno)));
+                       } else {
+                               notify_trigger(h->pvfs->notify_context,
+                                              NOTIFY_ACTION_REMOVED,
+                                              FILE_NOTIFY_CHANGE_FILE_NAME,
+                                              delete_path);
+                       }
+               }
+
                talloc_free(lck);
        }
 
@@ -555,15 +557,24 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        uint32_t access_mask = io->generic.in.access_mask;
        mode_t mode;
        uint32_t attrib;
-       BOOL del_on_close;
+       bool del_on_close;
        struct pvfs_filename *parent;
        uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+       bool allow_level_II_oplock = false;
 
+       if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_ENCRYPTED) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+           
        if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
            (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
                return NT_STATUS_CANNOT_DELETE;
        }
-       
+
        status = pvfs_access_check_create(pvfs, req, name, &access_mask);
        NT_STATUS_NOT_OK_RETURN(status);
 
@@ -574,7 +585,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                status = pvfs_locking_key(parent, req, &locking_key);
                NT_STATUS_NOT_OK_RETURN(status);
                status = odb_get_delete_on_close(pvfs->odb_context, &locking_key, 
-                                                &del_on_close, NULL, NULL);
+                                                &del_on_close);
                NT_STATUS_NOT_OK_RETURN(status);
                if (del_on_close) {
                        return NT_STATUS_DELETE_PENDING;
@@ -600,7 +611,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        mode = pvfs_fileperms(pvfs, attrib);
 
        /* create the file */
-       fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
+       fd = open(name->full_name, flags | O_CREAT | O_EXCL| O_NONBLOCK, mode);
        if (fd == -1) {
                return pvfs_map_errno(pvfs, errno);
        }
@@ -659,9 +670,9 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        }
 
        if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
-               del_on_close = True;
+               del_on_close = true;
        } else {
-               del_on_close = False;
+               del_on_close = false;
        }
 
        if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
@@ -672,25 +683,23 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                oplock_level = OPLOCK_EXCLUSIVE;
        }
 
-       status = odb_open_file(lck, f->handle, name->stream_id,
-                              share_access, access_mask, del_on_close, 
-                              name->full_name, oplock_level, &oplock_granted);
-       talloc_free(lck);
+       if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+               allow_level_II_oplock = true;
+       }
+
+       status = odb_can_open(lck, name->stream_id,
+                             share_access, access_mask, del_on_close,
+                             io->generic.in.open_disposition, false);
        if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(lck);
                /* bad news, we must have hit a race - we don't delete the file
-                  here as the most likely scenario is that someone else created 
+                  here as the most likely scenario is that someone else created
                   the file at the same time */
                close(fd);
                return status;
        }
 
-       if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
-               oplock_granted = OPLOCK_BATCH;
-       }
-
        f->ntvfs             = h;
-       f->session_info      = req->session_info;
-       f->smbpid            = req->smbpid;
        f->pvfs              = pvfs;
        f->pending_list      = NULL;
        f->lock_count        = 0;
@@ -698,6 +707,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        f->access_mask       = access_mask;
        f->impersonation     = io->generic.in.impersonation;
        f->notify_buffer     = NULL;
+       f->search            = NULL;
 
        f->handle->pvfs              = pvfs;
        f->handle->name              = talloc_steal(f->handle, name);
@@ -706,8 +716,21 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        f->handle->seek_offset       = 0;
        f->handle->position          = 0;
        f->handle->mode              = 0;
-       f->handle->have_opendb_entry = True;
-       f->handle->sticky_write_time = False;
+       f->handle->oplock            = NULL;
+       f->handle->have_opendb_entry = true;
+       f->handle->open_completed    = false;
+
+       status = odb_open_file(lck, f->handle, name->full_name,
+                              &f->handle->fd, allow_level_II_oplock,
+                              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
+                  here as the most likely scenario is that someone else created
+                  the file at the same time */
+               close(fd);
+               return status;
+       }
 
        DLIST_ADD(pvfs->files.list, f);
 
@@ -716,6 +739,15 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        talloc_set_destructor(f, pvfs_fnum_destructor);
        talloc_set_destructor(f->handle, pvfs_handle_destructor);
 
+       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)) {
+                       return status;
+               }
+       }
+
        io->generic.out.oplock_level  = oplock_granted;
        io->generic.out.file.ntvfs    = f->ntvfs;
        io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
@@ -736,6 +768,8 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                goto cleanup_delete;
        }
 
+       f->handle->open_completed = true;
+
        notify_trigger(pvfs->notify_context, 
                       NOTIFY_ACTION_ADDED, 
                       FILE_NOTIFY_CHANGE_FILE_NAME,
@@ -749,20 +783,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;
-       void *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) {
@@ -776,26 +815,121 @@ static int pvfs_retry_destructor(struct pvfs_open_retry *r)
        return 0;
 }
 
+static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
+{
+       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_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_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND,status)) {
+               /*
+                * maybe only a unix application
+                * has the file open
+                */
+               data_blob_free(&r->odb_locking_key);
+       } else 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);
+
+       return NT_STATUS_OK;
+}
+
 /*
-  retry an open
+  retry an open after a sharing violation
 */
-static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
+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)
 {
-       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;
+       union smb_open *io = talloc_get_type(_io, union smb_open);
+       struct timeval *final_timeout = NULL;
        NTSTATUS status;
 
+       if (private_data) {
+               final_timeout = talloc_get_type(private_data,
+                                               struct timeval);
+       }
+
        /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
           just a bug in their server, but we better do the same */
        if (reason == PVFS_WAIT_CANCEL) {
                return;
        }
 
-       talloc_free(r->wait_handle);
-
        if (reason == PVFS_WAIT_TIMEOUT) {
+               if (final_timeout &&
+                   !timeval_expired(final_timeout)) {
+                       /*
+                        * we need to retry periodictly
+                        * after an EAGAIN as there's
+                        * no way the kernel tell us
+                        * an oplock is released.
+                        */
+                       goto retry;
+               }
                /* if it timed out, then give the failure
                   immediately */
                talloc_free(r);
@@ -804,9 +938,7 @@ 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);
+retry:
        talloc_free(r);
 
        /* try the open again, which could trigger another retry setup
@@ -859,8 +991,8 @@ static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
        */
        for (f2=pvfs->files.list;f2;f2=f2->next) {
                if (f2 != f &&
-                   f2->session_info == req->session_info &&
-                   f2->smbpid == req->smbpid &&
+                   f2->ntvfs->session_info == req->session_info &&
+                   f2->ntvfs->smbpid == req->smbpid &&
                    (f2->handle->create_options & 
                     (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
                      NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
@@ -920,12 +1052,13 @@ 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;
+       struct timeval *final_timeout = NULL;
 
        if (io->generic.in.create_options & 
            (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
@@ -937,40 +1070,37 @@ 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 if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) {
+               /*
+                * we got EAGAIN which means a unix application
+                * has an oplock or share mode
+                *
+                * we retry every 4/5 of the sharing violation delay
+                * to see if the unix application
+                * has released the oplock or share mode.
+                */
+               final_timeout = talloc(req, struct timeval);
+               NT_STATUS_HAVE_NO_MEMORY(final_timeout);
+               *final_timeout = timeval_add(&req->statistics.request_time,
+                                            pvfs->oplock_break_timeout,
+                                            0);
+               end_time = timeval_current_ofs(0, (pvfs->sharing_violation_delay*4)/5);
+               end_time = timeval_min(final_timeout, &end_time);
+       } 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,
+                                   final_timeout, pvfs_retry_open_sharing);
 }
 
 /*
@@ -990,8 +1120,10 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        uint32_t create_options;
        uint32_t share_access;
        uint32_t access_mask;
-       BOOL stream_existed, stream_truncate=False;
+       bool del_on_close;
+       bool stream_existed, stream_truncate=false;
        uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+       bool allow_level_II_oplock = false;
 
        /* use the generic mapping code to avoid implementing all the
           different open calls. */
@@ -1000,6 +1132,42 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return ntvfs_map_open(ntvfs, req, io);
        }
 
+       create_options = io->generic.in.create_options;
+       share_access   = io->generic.in.share_access;
+       access_mask    = io->generic.in.access_mask;
+
+       if (share_access & ~NTCREATEX_SHARE_ACCESS_MASK) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* some create options are not supported */
+       if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       /* other create options are not allowed */
+       if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+           !(access_mask & SEC_STD_DELETE)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (access_mask & SEC_MASK_INVALID) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       /* what does this bit really mean?? */
+       if (req->ctx->protocol == PROTOCOL_SMB2 &&
+           (access_mask & SEC_STD_SYNCHRONIZE) &&
+           !(access_mask & SEC_STD_READ_CONTROL)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
+                                         FILE_ATTRIBUTE_VOLUME| 
+                                         (~FILE_ATTRIBUTE_ALL_MASK))) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
        /* resolve the cifs name to a posix name */
        status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 
                                   PVFS_RESOLVE_STREAMS, &name);
@@ -1007,6 +1175,20 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return status;
        }
 
+       /* if the client specified that it must not be a directory then
+          check that it isn't */
+       if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+           (io->generic.in.create_options & NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)) {
+               return NT_STATUS_FILE_IS_A_DIRECTORY;
+       }
+
+       /* if the client specified that it must be a directory then
+          check that it is */
+       if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+           (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
+               return NT_STATUS_NOT_A_DIRECTORY;
+       }
+
        /* directory opens are handled separately */
        if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
            (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
@@ -1017,16 +1199,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
           open doesn't match */
        io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
 
-       create_options = io->generic.in.create_options;
-       share_access   = io->generic.in.share_access;
-       access_mask    = io->generic.in.access_mask;
-
-       /* certain create options are not allowed */
-       if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
-           !(access_mask & SEC_STD_DELETE)) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
        flags = 0;
 
        switch (io->generic.in.open_disposition) {
@@ -1035,7 +1207,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                if (name->stream_name == NULL) {
                        flags = O_TRUNC;
                } else {
-                       stream_truncate = True;
+                       stream_truncate = true;
                }
                break;
 
@@ -1053,7 +1225,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                if (name->stream_name == NULL) {
                        flags = O_TRUNC;
                } else {
-                       stream_truncate = True;
+                       stream_truncate = true;
                }
                break;
 
@@ -1117,8 +1289,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        }
 
        f->ntvfs         = h;
-       f->session_info  = req->session_info;
-       f->smbpid        = req->smbpid;
        f->pvfs          = pvfs;
        f->pending_list  = NULL;
        f->lock_count    = 0;
@@ -1126,6 +1296,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        f->access_mask   = access_mask;
        f->impersonation = io->generic.in.impersonation;
        f->notify_buffer = NULL;
+       f->search        = NULL;
 
        f->handle->pvfs              = pvfs;
        f->handle->fd                = -1;
@@ -1134,8 +1305,9 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        f->handle->seek_offset       = 0;
        f->handle->position          = 0;
        f->handle->mode              = 0;
-       f->handle->have_opendb_entry = False;
-       f->handle->sticky_write_time = False;
+       f->handle->oplock            = NULL;
+       f->handle->have_opendb_entry = false;
+       f->handle->open_completed    = false;
 
        /* form the lock context used for byte range locking and
           opendb locking */
@@ -1166,6 +1338,17 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        talloc_set_destructor(f, pvfs_fnum_destructor);
        talloc_set_destructor(f->handle, pvfs_handle_destructor);
 
+       /* 
+        * Only SMB2 takes care of the delete_on_close,
+        * on existing files
+        */
+       if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE &&
+           req->ctx->protocol == PROTOCOL_SMB2) {
+               del_on_close = true;
+       } else {
+               del_on_close = false;
+       }
+
        if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
                oplock_level = OPLOCK_NONE;
        } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
@@ -1174,16 +1357,25 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                oplock_level = OPLOCK_EXCLUSIVE;
        }
 
+       if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+               allow_level_II_oplock = true;
+       }
+
        /* 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,
-                              share_access, access_mask, False, name->full_name,
-                              oplock_level, &oplock_granted);
+       status = odb_can_open(lck, name->stream_id,
+                             share_access, access_mask, del_on_close,
+                             io->generic.in.open_disposition, false);
 
-       /* 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)) {
@@ -1191,12 +1383,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return status;
        }
 
-       if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
-               oplock_granted = OPLOCK_BATCH;
-       }
-
-       f->handle->have_opendb_entry = True;
-
        if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
                flags |= O_RDWR;
        } else {
@@ -1204,14 +1390,46 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        }
 
        /* do the actual open */
-       fd = open(f->handle->name->full_name, flags);
+       fd = open(f->handle->name->full_name, flags | O_NONBLOCK);
        if (fd == -1) {
+               status = pvfs_map_errno(f->pvfs, errno);
+
+               /*
+                * STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK
+                */
+               if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+                   (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+                       return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
+               }
+
                talloc_free(lck);
-               return pvfs_map_errno(f->pvfs, errno);
+               return status;
        }
 
        f->handle->fd = fd;
 
+       /* now really mark the file as open */
+       status = odb_open_file(lck, f->handle, name->full_name,
+                              &f->handle->fd, allow_level_II_oplock,
+                              oplock_level, &oplock_granted);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(lck);
+               return status;
+       }
+
+       f->handle->have_opendb_entry = true;
+
+       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;
+               }
+       }
+
        stream_existed = name->stream_exists;
 
        /* if this was a stream create then create the stream as well */
@@ -1260,6 +1478,10 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        status = ntvfs_handle_set_backend_data(h, ntvfs, f);
        NT_STATUS_NOT_OK_RETURN(status);
 
+       /* mark the open as having completed fully, so delete on close
+          can now be used */
+       f->handle->open_completed     = true;
+
        io->generic.out.oplock_level  = oplock_granted;
        io->generic.out.file.ntvfs    = h;
        io->generic.out.create_action = stream_existed?
@@ -1306,10 +1528,6 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
                unix_times.actime = 0;
                unix_times.modtime = io->close.in.write_time;
                utime(f->handle->name->full_name, &unix_times);
-       } else if (f->handle->sticky_write_time) {
-               unix_times.actime = 0;
-               unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
-               utime(f->handle->name->full_name, &unix_times);
        }
 
        talloc_free(f);
@@ -1329,7 +1547,7 @@ NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
 
        for (f=pvfs->files.list;f;f=next) {
                next = f->next;
-               if (f->session_info == req->session_info) {
+               if (f->ntvfs->session_info == req->session_info) {
                        talloc_free(f);
                }
        }
@@ -1349,8 +1567,8 @@ NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
 
        for (f=pvfs->files.list;f;f=next) {
                next = f->next;
-               if (f->session_info == req->session_info &&
-                   f->smbpid == req->smbpid) {
+               if (f->ntvfs->session_info == req->session_info &&
+                   f->ntvfs->smbpid == req->smbpid) {
                        talloc_free(f);
                }
        }
@@ -1364,7 +1582,7 @@ NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
 */
 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
                                  struct ntvfs_request *req, 
-                                 struct pvfs_file *f, BOOL del_on_close)
+                                 struct pvfs_file *f, bool del_on_close)
 {
        struct odb_lock *lck;
        NTSTATUS status;
@@ -1409,6 +1627,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)) {
@@ -1421,21 +1642,38 @@ 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,
+                             NTCREATEX_DISP_OPEN, 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);
        }
 
-       if (!NT_STATUS_IS_OK(status)) {
+       /*
+        * 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);
-               *lckp = lck;
-       } else if (lckp != NULL) {
+               if (lckp) {
+                       *lckp = NULL;
+               }
+       } else if (lckp) {
                *lckp = lck;
        }
 
@@ -1454,6 +1692,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)) {
@@ -1466,16 +1707,105 @@ 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;
 
-       if (!NT_STATUS_IS_OK(status)) {
+       status = odb_can_open(lck, name->stream_id,
+                             share_access, access_mask, delete_on_close,
+                             NTCREATEX_DISP_OPEN, 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;
-       } else if (lckp != NULL) {
+       }
+
+       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;
+       }
+
+       share_access    = NTCREATEX_SHARE_ACCESS_READ |
+                         NTCREATEX_SHARE_ACCESS_WRITE |
+                         NTCREATEX_SHARE_ACCESS_DELETE;
+       /*
+        * I would have thought that we would need to pass
+        * SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA here too
+        *
+        * But you only need SEC_FILE_WRITE_ATTRIBUTE permissions
+        * to set the filesize.
+        *
+        * --metze
+        */
+       access_mask     = SEC_FILE_WRITE_ATTRIBUTE;
+       delete_on_close = false;
+       break_to_none   = true;
+
+       status = odb_can_open(lck, name->stream_id,
+                             share_access, access_mask, delete_on_close,
+                             NTCREATEX_DISP_OPEN, break_to_none);
+
+       /*
+        * 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;
        }
 
@@ -1493,6 +1823,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)) {
@@ -1505,10 +1838,18 @@ 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     = SEC_FILE_READ_ATTRIBUTE;
+       delete_on_close = false;
+
+       status = odb_can_open(lck, name->stream_id,
+                             share_access, access_mask, delete_on_close,
+                             NTCREATEX_DISP_OPEN, false);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(lck);
+       }
 
        return status;
 }
@@ -1517,17 +1858,16 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
 /*
   determine if delete on close is set on 
 */
-BOOL pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h, 
-                             int *open_count, char **path)
+bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h)
 {
        NTSTATUS status;
-       BOOL del_on_close;
+       bool del_on_close;
 
        status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key, 
-                                        &del_on_close, open_count, path);
+                                        &del_on_close);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
-               return False;
+               return false;
        }
 
        return del_on_close;