pvfs_open: pass down &f->handle->fd to odb_open_file()
[samba.git] / source4 / ntvfs / posix / pvfs_open.c
index 7cb8a5d90c629cec28cf4fe22f81984cbfa17b66..ceda3a6da0b5aa58d5b3aa623b802389b1a045e5 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 "system/filesys.h"
-#include "dlinklist.h"
-#include "messages.h"
-#include "librpc/gen_ndr/ndr_xattr.h"
-
-/*
-  create file handles with convenient numbers for sniffers
-*/
-#define PVFS_MIN_FILE_FNUM 0x100
-#define PVFS_MIN_NEW_FNUM  0x200
-#define PVFS_MIN_DIR_FNUM  0x300
+#include "lib/util/dlinklist.h"
+#include "messaging/messaging.h"
+#include "librpc/gen_ndr/xattr.h"
 
 /*
   find open file handle given fnum
 */
 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
-                              struct smbsrv_request *req, uint16_t fnum)
+                              struct ntvfs_request *req, struct ntvfs_handle *h)
 {
+       void *p;
        struct pvfs_file *f;
 
-       f = idr_find(pvfs->idtree_fnum, fnum);
-       if (f == NULL) {
-               return NULL;
-       }
-
-       if (f->fnum != fnum) {
-               smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
-       }
+       p = ntvfs_handle_get_backend_data(h, pvfs->ntvfs);
+       if (!p) return NULL;
 
-       if (req->session != f->session) {
-               DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", 
-                        fnum));
-               return NULL;
-       }
+       f = talloc_get_type(p, struct pvfs_file);
+       if (!f) return NULL;
 
        return f;
 }
 
-
 /*
   cleanup a open directory handle
 */
-static int pvfs_dir_handle_destructor(void *p)
+static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
 {
-       struct pvfs_file_handle *h = p;
+       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) {
+                       DEBUG(0,("Unable to lock opendb for close\n"));
+                       return 0;
+               }
 
-       if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
-               NTSTATUS status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
+               status = odb_close_file(lck, h, &delete_path);
                if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0,("Warning: xattr rmdir hook failed for '%s' - %s\n",
+                       DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
                                 h->name->full_name, nt_errstr(status)));
                }
-               if (rmdir(h->name->full_name) != 0) {
-                       DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n", 
-                                h->name->full_name, strerror(errno)));
+
+               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);
        }
 
        return 0;
@@ -87,28 +88,103 @@ static int pvfs_dir_handle_destructor(void *p)
 /*
   cleanup a open directory fnum
 */
-static int pvfs_dir_fnum_destructor(void *p)
+static int pvfs_dir_fnum_destructor(struct pvfs_file *f)
 {
-       struct pvfs_file *f = p;
-       DLIST_REMOVE(f->pvfs->open_files, f);
-       idr_remove(f->pvfs->idtree_fnum, f->fnum);
+       DLIST_REMOVE(f->pvfs->files.list, f);
+       ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
+
        return 0;
 }
 
+/*
+  setup any EAs and the ACL on newly created files/directories
+*/
+static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
+                                       struct ntvfs_request *req,
+                                       struct pvfs_filename *name,
+                                       int fd, struct pvfs_file *f,
+                                       union smb_open *io)
+{
+       NTSTATUS status;
+
+       /* setup any EAs that were asked for */
+       if (io->ntcreatex.in.ea_list) {
+               status = pvfs_setfileinfo_ea_set(pvfs, name, fd, 
+                                                io->ntcreatex.in.ea_list->num_eas,
+                                                io->ntcreatex.in.ea_list->eas);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
+       /* setup an initial sec_desc if requested */
+       if (io->ntcreatex.in.sec_desc) {
+               union smb_setfileinfo set;
+/* 
+ * TODO: set the full ACL! 
+ *       - vista denies the creation of the file with NT_STATUS_PRIVILEGE_NOT_HELD,
+ *         when a SACL is present on the sd,
+ *         but the user doesn't have SeSecurityPrivilege
+ *       - w2k3 allows it
+ */
+               set.set_secdesc.in.file.ntvfs = f->ntvfs;
+               set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+               set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
+
+               status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
+       } else {
+               /* otherwise setup an inherited acl from the parent */
+               status = pvfs_acl_inherit(pvfs, req, name, fd);
+       }
+
+       return status;
+}
+
+/*
+  form the lock context used for opendb locking. Note that we must
+  zero here to take account of possible padding on some architectures
+*/
+NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
+                         TALLOC_CTX *mem_ctx, DATA_BLOB *key)
+{
+       struct {
+               dev_t device;
+               ino_t inode;
+       } lock_context;
+       ZERO_STRUCT(lock_context);
+
+       lock_context.device = name->st.st_dev;
+       lock_context.inode = name->st.st_ino;
+
+       *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
+       if (key->data == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       return NT_STATUS_OK;
+}
+
 
 /*
   open a directory
 */
 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, 
-                                   struct smbsrv_request *req, 
+                                   struct ntvfs_request *req, 
                                    struct pvfs_filename *name, 
                                    union smb_open *io)
 {
        struct pvfs_file *f;
-       int fnum;
+       struct ntvfs_handle *h;
        NTSTATUS status;
        uint32_t create_action;
        uint32_t access_mask = io->generic.in.access_mask;
+       struct odb_lock *lck;
+       bool del_on_close;
+       uint32_t create_options;
+       uint32_t share_access;
+
+       create_options = io->generic.in.create_options;
+       share_access   = io->generic.in.share_access;
 
        if (name->stream_name) {
                return NT_STATUS_NOT_A_DIRECTORY;
@@ -143,54 +219,98 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       f = talloc_p(req, struct pvfs_file);
+       status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       f = talloc(h, struct pvfs_file);
        if (f == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       f->handle = talloc_p(f, struct pvfs_file_handle);
+       f->handle = talloc(f, struct pvfs_file_handle);
        if (f->handle == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
-       if (fnum == -1) {
-               return NT_STATUS_TOO_MANY_OPENED_FILES;
-       }
-
        if (name->exists) {
                /* check the security descriptor */
                status = pvfs_access_check(pvfs, req, name, &access_mask);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
+       } else {
+               status = pvfs_access_check_create(pvfs, req, name, &access_mask);
+       }
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       f->fnum          = fnum;
-       f->session       = req->session;
-       f->smbpid        = req->smbpid;
+       f->ntvfs         = h;
        f->pvfs          = pvfs;
        f->pending_list  = NULL;
        f->lock_count    = 0;
        f->share_access  = io->generic.in.share_access;
        f->impersonation = io->generic.in.impersonation;
        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);
        f->handle->fd                = -1;
        f->handle->odb_locking_key   = data_blob(NULL, 0);
-       f->handle->brl_locking_key   = data_blob(NULL, 0);
        f->handle->create_options    = io->generic.in.create_options;
        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->sticky_write_time = false;
+       f->handle->open_completed    = false;
 
-       DLIST_ADD(pvfs->open_files, f);
+       if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+           pvfs_directory_empty(pvfs, f->handle->name)) {
+               del_on_close = true;
+       } else {
+               del_on_close = false;
+       }
 
-       /* TODO: should we check in the opendb? Do directory opens 
-          follow the share_access rules? */
+       if (name->exists) {
+               /* form the lock context used for opendb locking */
+               status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               /* get a lock on this file before the actual open */
+               lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+               if (lck == NULL) {
+                       DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+                                name->full_name));
+                       /* we were supposed to do a blocking lock, so something
+                          is badly wrong! */
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               
+               /* see if we are allowed to open at the same time as existing opens */
+               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;
+       }
+
+       DLIST_ADD(pvfs->files.list, f);
 
        /* setup destructors to avoid leaks on abnormal termination */
        talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
@@ -199,6 +319,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        if (!name->exists) {
                uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
                mode_t mode = pvfs_fileperms(pvfs, attrib);
+
                if (mkdir(name->full_name, mode) == -1) {
                        return pvfs_map_errno(pvfs,errno);
                }
@@ -206,10 +327,53 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                pvfs_xattr_unlink_hook(pvfs, name->full_name);
 
                status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto cleanup_delete;
+               }
+
+               status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto cleanup_delete;
+               }
+
+               /* form the lock context used for opendb locking */
+               status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
+
+               lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+               if (lck == NULL) {
+                       DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+                                name->full_name));
+                       /* we were supposed to do a blocking lock, so something
+                          is badly wrong! */
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+
+               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;
+               }
+
+               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;
+
+               notify_trigger(pvfs->notify_context, 
+                              NOTIFY_ACTION_ADDED, 
+                              FILE_NOTIFY_CHANGE_DIR_NAME,
+                              name->full_name);
        } else {
                create_action = NTCREATEX_ACTION_EXISTED;
        }
@@ -219,10 +383,15 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        }
 
        /* the open succeeded, keep this handle permanently */
-       talloc_steal(pvfs, f);
+       status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup_delete;
+       }
+
+       f->handle->open_completed = true;
 
        io->generic.out.oplock_level  = OPLOCK_NONE;
-       io->generic.out.fnum          = f->fnum;
+       io->generic.out.file.ntvfs    = h;
        io->generic.out.create_action = create_action;
        io->generic.out.create_time   = name->dos.create_time;
        io->generic.out.access_time   = name->dos.access_time;
@@ -236,15 +405,17 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        io->generic.out.is_directory  = 1;
 
        return NT_STATUS_OK;
+
+cleanup_delete:
+       rmdir(name->full_name);
+       return status;
 }
 
 /*
   destroy a struct pvfs_file_handle
 */
-static int pvfs_handle_destructor(void *p)
+static int pvfs_handle_destructor(struct pvfs_file_handle *h)
 {
-       struct pvfs_file_handle *h = p;
-
        /* the write time is no longer sticky */
        if (h->sticky_write_time) {
                NTSTATUS status;
@@ -273,23 +444,10 @@ static int pvfs_handle_destructor(void *p)
                h->fd = -1;
        }
 
-       if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
-           h->name->stream_name == NULL) {
-               NTSTATUS status;
-               status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
-                                h->name->full_name, nt_errstr(status)));
-               }
-               if (unlink(h->name->full_name) != 0) {
-                       DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n", 
-                                h->name->full_name, strerror(errno)));
-               }
-       }
-
        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) {
@@ -297,12 +455,30 @@ static int pvfs_handle_destructor(void *p)
                        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);
        }
 
@@ -313,99 +489,96 @@ static int pvfs_handle_destructor(void *p)
 /*
   destroy a struct pvfs_file
 */
-static int pvfs_fnum_destructor(void *p)
+static int pvfs_fnum_destructor(struct pvfs_file *f)
 {
-       struct pvfs_file *f = p;
-
-       DLIST_REMOVE(f->pvfs->open_files, f);
+       DLIST_REMOVE(f->pvfs->files.list, f);
        pvfs_lock_close(f->pvfs, f);
-       idr_remove(f->pvfs->idtree_fnum, f->fnum);
+       ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
 
        return 0;
 }
 
 
-/*
-  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)
-{
-       struct {
-               dev_t device;
-               ino_t inode;
-       } lock_context;
-       ZERO_STRUCT(lock_context);
-
-       lock_context.device = name->st.st_dev;
-       lock_context.inode = name->st.st_ino;
-
-       *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
-       if (key->data == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       
-       return NT_STATUS_OK;
-}
-
 /*
   form the lock context used for byte range locking. This is separate
   from the locking key used for opendb locking as it needs to take
   account of file streams (each stream is a separate byte range
   locking space)
 */
-static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name, 
-                                    TALLOC_CTX *mem_ctx, DATA_BLOB *key)
+static NTSTATUS pvfs_brl_locking_handle(TALLOC_CTX *mem_ctx,
+                                       struct pvfs_filename *name,
+                                       struct ntvfs_handle *ntvfs,
+                                       struct brl_handle **_h)
 {
-       DATA_BLOB odb_key;
+       DATA_BLOB odb_key, key;
        NTSTATUS status;
+       struct brl_handle *h;
+
        status = pvfs_locking_key(name, mem_ctx, &odb_key);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       NT_STATUS_NOT_OK_RETURN(status);
+
        if (name->stream_name == NULL) {
-               *key = odb_key;
-               return NT_STATUS_OK;
-       }
-       *key = data_blob_talloc(mem_ctx, NULL, 
-                               odb_key.length + strlen(name->stream_name) + 1);
-       if (key->data == NULL) {
-               return NT_STATUS_NO_MEMORY;
+               key = odb_key;
+       } else {
+               key = data_blob_talloc(mem_ctx, NULL, 
+                                      odb_key.length + strlen(name->stream_name) + 1);
+               NT_STATUS_HAVE_NO_MEMORY(key.data);
+               memcpy(key.data, odb_key.data, odb_key.length);
+               memcpy(key.data + odb_key.length, 
+                      name->stream_name, strlen(name->stream_name) + 1);
+               data_blob_free(&odb_key);
        }
-       memcpy(key->data, odb_key.data, odb_key.length);
-       memcpy(key->data + odb_key.length, 
-              name->stream_name, strlen(name->stream_name)+1);
-       data_blob_free(&odb_key);
+
+       h = brl_create_handle(mem_ctx, ntvfs, &key);
+       NT_STATUS_HAVE_NO_MEMORY(h);
+
+       *_h = h;
        return NT_STATUS_OK;
 }
 
-
 /*
   create a new file
 */
 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
-                                struct smbsrv_request *req, 
+                                struct ntvfs_request *req, 
                                 struct pvfs_filename *name, 
                                 union smb_open *io)
 {
        struct pvfs_file *f;
        NTSTATUS status;
-       int flags, fnum, fd;
+       struct ntvfs_handle *h;
+       int flags, fd;
        struct odb_lock *lck;
        uint32_t create_options = io->generic.in.create_options;
        uint32_t share_access = io->generic.in.share_access;
        uint32_t access_mask = io->generic.in.access_mask;
        mode_t mode;
        uint32_t attrib;
+       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_READONLY) &&
            (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
                return NT_STATUS_CANNOT_DELETE;
        }
        
-       if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
-               access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
+       status = pvfs_access_check_create(pvfs, req, name, &access_mask);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       /* check that the parent isn't opened with delete on close set */
+       status = pvfs_resolve_parent(pvfs, req, name, &parent);
+       if (NT_STATUS_IS_OK(status)) {
+               DATA_BLOB locking_key;
+               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);
+               NT_STATUS_NOT_OK_RETURN(status);
+               if (del_on_close) {
+                       return NT_STATUS_DELETE_PENDING;
+               }
        }
 
        if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
@@ -414,20 +587,14 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                flags = O_RDONLY;
        }
 
-       f = talloc_p(req, struct pvfs_file);
-       if (f == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
+       status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+       NT_STATUS_NOT_OK_RETURN(status);
 
-       f->handle = talloc_p(f, struct pvfs_file_handle);
-       if (f->handle == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
+       f = talloc(h, struct pvfs_file);
+       NT_STATUS_HAVE_NO_MEMORY(f);
 
-       fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
-       if (fnum == -1) {
-               return NT_STATUS_TOO_MANY_OPENED_FILES;
-       }
+       f->handle = talloc(f, struct pvfs_file_handle);
+       NT_STATUS_HAVE_NO_MEMORY(f->handle);
 
        attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
        mode = pvfs_fileperms(pvfs, attrib);
@@ -435,7 +602,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        /* create the file */
        fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
        if (fd == -1) {
-               idr_remove(pvfs->idtree_fnum, fnum);
                return pvfs_map_errno(pvfs, errno);
        }
 
@@ -445,7 +611,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        if (name->stream_name) {
                status = pvfs_stream_create(pvfs, name, fd);
                if (!NT_STATUS_IS_OK(status)) {
-                       idr_remove(pvfs->idtree_fnum, fnum);
                        close(fd);
                        return status;
                }
@@ -454,7 +619,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        /* re-resolve the open fd */
        status = pvfs_resolve_name_fd(pvfs, fd, name);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, fnum);
                close(fd);
                return status;
        }
@@ -462,53 +626,25 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        name->dos.attrib = attrib;
        status = pvfs_dosattrib_save(pvfs, name, fd);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, fnum);
-               close(fd);
-               return status;
+               goto cleanup_delete;
        }
 
-       /* setup any EAs that were asked for */
-       if (io->ntcreatex.in.ea_list) {
-               status = pvfs_setfileinfo_ea_set(pvfs, name, fd, 
-                                                io->ntcreatex.in.ea_list->num_eas,
-                                                io->ntcreatex.in.ea_list->eas);
-               if (!NT_STATUS_IS_OK(status)) {
-                       idr_remove(pvfs->idtree_fnum, fnum);
-                       close(fd);
-                       return status;
-               }
-       }
 
-       /* setup an initial sec_desc is required */
-       if (io->ntcreatex.in.sec_desc) {
-               union smb_setfileinfo set;
-
-               set.set_secdesc.file.fnum = fnum;
-               set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
-               set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
-
-               status = pvfs_acl_set(pvfs, req, name, fd, &set);
-               if (!NT_STATUS_IS_OK(status)) {
-                       idr_remove(pvfs->idtree_fnum, fnum);
-                       close(fd);
-                       return status;
-               }
+       status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup_delete;
        }
 
        /* form the lock context used for byte range locking and
           opendb locking */
        status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, fnum);
-               close(fd);
-               return status;
+               goto cleanup_delete;
        }
 
-       status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
+       status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, fnum);
-               close(fd);
-               return status;
+               goto cleanup_delete;
        }
 
        /* grab a lock on the open file record */
@@ -518,30 +654,49 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                         name->full_name));
                /* we were supposed to do a blocking lock, so something
                   is badly wrong! */
-               idr_remove(pvfs->idtree_fnum, fnum);
-               close(fd);
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+               goto cleanup_delete;
        }
 
-       status = odb_open_file(lck, f->handle, name->stream_id,
-                              share_access, create_options, access_mask);
-       talloc_free(lck);
+       if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
+               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) {
+               oplock_level = OPLOCK_BATCH;
+       } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
+               oplock_level = OPLOCK_EXCLUSIVE;
+       }
+
+       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)) {
-               /* bad news, we must have hit a race */
-               idr_remove(pvfs->idtree_fnum, fnum);
+               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
+                  the file at the same time */
                close(fd);
                return status;
        }
 
-       f->fnum              = fnum;
-       f->session           = req->session;
-       f->smbpid            = req->smbpid;
+       f->ntvfs             = h;
        f->pvfs              = pvfs;
        f->pending_list      = NULL;
        f->lock_count        = 0;
        f->share_access      = io->generic.in.share_access;
        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);
@@ -550,23 +705,41 @@ 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->sticky_write_time = false;
+       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->open_files, f);
+       DLIST_ADD(pvfs->files.list, f);
 
        /* setup a destructor to avoid file descriptor leaks on
           abnormal termination */
        talloc_set_destructor(f, pvfs_fnum_destructor);
        talloc_set_destructor(f->handle, pvfs_handle_destructor);
 
-       
        if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
-               io->generic.out.oplock_level  = OPLOCK_EXCLUSIVE;
-       } else {
-               io->generic.out.oplock_level  = OPLOCK_NONE;
+               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.fnum          = f->fnum;
+
+       io->generic.out.oplock_level  = oplock_granted;
+       io->generic.out.file.ntvfs    = f->ntvfs;
        io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
        io->generic.out.create_time   = name->dos.create_time;
        io->generic.out.access_time   = name->dos.access_time;
@@ -580,27 +753,46 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        io->generic.out.is_directory  = 0;
 
        /* success - keep the file handle */
-       talloc_steal(pvfs, f);
+       status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup_delete;
+       }
+
+       f->handle->open_completed = true;
+
+       notify_trigger(pvfs->notify_context, 
+                      NOTIFY_ACTION_ADDED, 
+                      FILE_NOTIFY_CHANGE_FILE_NAME,
+                      name->full_name);
 
        return NT_STATUS_OK;
-}
 
+cleanup_delete:
+       close(fd);
+       unlink(name->full_name);
+       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 smbsrv_request *req;
-       union smb_open *io;
-       void *wait_handle;
+       struct ntvfs_request *req;
        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(void *ptr)
+/* destroy a pending request */
+static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
 {
-       struct pvfs_open_retry *r = ptr;
        struct pvfs_state *pvfs = r->ntvfs->private_data;
        if (r->odb_locking_key.data) {
                struct odb_lock *lck;
@@ -613,15 +805,92 @@ static int pvfs_retry_destructor(void *ptr)
        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_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
+  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 smbsrv_request *req = r->req;
-       union smb_open *io = r->io;
+       union smb_open *io = talloc_get_type(_io, union smb_open);
        NTSTATUS status;
 
        /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
@@ -630,8 +899,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 */
@@ -641,9 +908,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
@@ -676,12 +940,13 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
   open processing continues.
 */
 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
-                                  struct smbsrv_request *req, union smb_open *io,
+                                  struct ntvfs_request *req, union smb_open *io,
                                   struct pvfs_file *f, struct odb_lock *lck)
 {
        struct pvfs_state *pvfs = ntvfs->private_data;
        struct pvfs_file *f2;
        struct pvfs_filename *name;
+       NTSTATUS status;
 
        /* search for an existing open with the right parameters. Note
           the magic ntcreatex options flag, which is set in the
@@ -693,15 +958,15 @@ static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
           circumstances you actually get the _same_ handle back
           twice, rather than a new handle.
        */
-       for (f2=pvfs->open_files;f2;f2=f2->next) {
+       for (f2=pvfs->files.list;f2;f2=f2->next) {
                if (f2 != f &&
-                   f2->session == req->session &&
-                   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)) &&
                    (f2->access_mask & SEC_FILE_WRITE_DATA) &&
-                   StrCaseCmp(f2->handle->name->original_name, 
+                   strcasecmp_m(f2->handle->name->original_name, 
                               io->generic.in.fname)==0) {
                        break;
                }
@@ -728,7 +993,7 @@ static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
        name = f->handle->name;
 
        io->generic.out.oplock_level  = OPLOCK_NONE;
-       io->generic.out.fnum          = f->fnum;
+       io->generic.out.file.ntvfs    = f->ntvfs;
        io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
        io->generic.out.create_time   = name->dos.create_time;
        io->generic.out.access_time   = name->dos.access_time;
@@ -740,8 +1005,9 @@ static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
        io->generic.out.file_type     = FILE_TYPE_DISK;
        io->generic.out.ipc_state     = 0;
        io->generic.out.is_directory  = 0;
-
-       talloc_steal(f->pvfs, f);
+       status = ntvfs_handle_set_backend_data(f->ntvfs, ntvfs, f);
+       NT_STATUS_NOT_OK_RETURN(status);
 
        return NT_STATUS_OK;
 }
@@ -752,13 +1018,13 @@ static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
   setup for a open retry after a sharing violation
 */
 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
-                                     struct smbsrv_request *req, 
+                                     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;
 
@@ -772,65 +1038,50 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
                }
        }
 
-       r = talloc_p(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->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);
 }
 
 /*
   open a file
 */
 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
-                  struct smbsrv_request *req, union smb_open *io)
+                  struct ntvfs_request *req, union smb_open *io)
 {
        struct pvfs_state *pvfs = ntvfs->private_data;
        int flags;
        struct pvfs_filename *name;
        struct pvfs_file *f;
+       struct ntvfs_handle *h;
        NTSTATUS status;
-       int fnum, fd;
+       int fd;
        struct odb_lock *lck;
        uint32_t create_options;
        uint32_t share_access;
        uint32_t access_mask;
-       BOOL stream_existed;
+       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. */
        if (io->generic.level != RAW_OPEN_GENERIC &&
            io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
-               return ntvfs_map_open(req, io, ntvfs);
+               return ntvfs_map_open(ntvfs, req, io);
        }
 
        /* resolve the cifs name to a posix name */
@@ -846,6 +1097,10 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return pvfs_open_directory(pvfs, req, name, io);
        }
 
+       /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
+          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;
@@ -856,13 +1111,16 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       flags = 0;
+
        switch (io->generic.in.open_disposition) {
        case NTCREATEX_DISP_SUPERSEDE:
-               flags = O_TRUNC;
-               break;
-
        case NTCREATEX_DISP_OVERWRITE_IF:
-               flags = O_TRUNC;
+               if (name->stream_name == NULL) {
+                       flags = O_TRUNC;
+               } else {
+                       stream_truncate = true;
+               }
                break;
 
        case NTCREATEX_DISP_OPEN:
@@ -876,7 +1134,11 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                if (!name->stream_exists) {
                        return NT_STATUS_OBJECT_NAME_NOT_FOUND;
                }
-               flags = O_TRUNC;
+               if (name->stream_name == NULL) {
+                       flags = O_TRUNC;
+               } else {
+                       stream_truncate = true;
+               }
                break;
 
        case NTCREATEX_DISP_CREATE:
@@ -894,10 +1156,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
        /* handle creating a new file separately */
        if (!name->exists) {
                status = pvfs_create_file(pvfs, req, name, io);
@@ -929,31 +1187,28 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return status;
        }
 
-       f = talloc_p(req, struct pvfs_file);
+       status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       f = talloc(h, struct pvfs_file);
        if (f == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       f->handle = talloc_p(f, struct pvfs_file_handle);
+       f->handle = talloc(f, struct pvfs_file_handle);
        if (f->handle == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       /* allocate a fnum */
-       fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
-       if (fnum == -1) {
-               return NT_STATUS_TOO_MANY_OPENED_FILES;
-       }
-
-       f->fnum          = fnum;
-       f->session       = req->session;
-       f->smbpid        = req->smbpid;
+       f->ntvfs         = h;
        f->pvfs          = pvfs;
        f->pending_list  = NULL;
        f->lock_count    = 0;
        f->share_access  = io->generic.in.share_access;
        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;
@@ -962,20 +1217,20 @@ 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->sticky_write_time = false;
+       f->handle->open_completed    = false;
 
        /* form the lock context used for byte range locking and
           opendb locking */
        status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, f->fnum);
                return status;
        }
 
-       status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
+       status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
        if (!NT_STATUS_IS_OK(status)) {
-               idr_remove(pvfs->idtree_fnum, f->fnum);
                return status;
        }
 
@@ -986,27 +1241,54 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                         name->full_name));
                /* we were supposed to do a blocking lock, so something
                   is badly wrong! */
-               idr_remove(pvfs->idtree_fnum, fnum);
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
-       DLIST_ADD(pvfs->open_files, f);
+       DLIST_ADD(pvfs->files.list, f);
 
        /* setup a destructor to avoid file descriptor leaks on
           abnormal termination */
        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) {
+               oplock_level = OPLOCK_BATCH;
+       } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
+               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, create_options, access_mask);
+       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)) {
@@ -1014,8 +1296,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return status;
        }
 
-       f->handle->have_opendb_entry = True;
-
        if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
                flags |= O_RDWR;
        } else {
@@ -1031,6 +1311,28 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
 
        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;
+       }
+
+       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;
+
        stream_existed = name->stream_exists;
 
        /* if this was a stream create then create the stream as well */
@@ -1040,6 +1342,13 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                        talloc_free(lck);
                        return status;
                }
+               if (stream_truncate) {
+                       status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               talloc_free(lck);
+                               return status;
+                       }
+               }
        }
 
        /* re-resolve the open fd */
@@ -1069,12 +1378,15 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
            
        talloc_free(lck);
 
-       if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
-               io->generic.out.oplock_level  = OPLOCK_EXCLUSIVE;
-       } else {
-               io->generic.out.oplock_level  = OPLOCK_NONE;
-       }
-       io->generic.out.fnum          = f->fnum;
+       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?
                NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
        io->generic.out.create_time   = name->dos.create_time;
@@ -1088,9 +1400,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        io->generic.out.ipc_state     = 0;
        io->generic.out.is_directory  = 0;
 
-       /* success - keep the file handle */
-       talloc_steal(f->pvfs, f);
-
        return NT_STATUS_OK;
 }
 
@@ -1099,21 +1408,21 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
   close a file
 */
 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
-                   struct smbsrv_request *req, union smb_close *io)
+                   struct ntvfs_request *req, union smb_close *io)
 {
        struct pvfs_state *pvfs = ntvfs->private_data;
        struct pvfs_file *f;
        struct utimbuf unix_times;
 
        if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
-               return NT_STATUS_UNSUCCESSFUL;
+               return NT_STATUS_DOS(ERRSRV, ERRerror);
        }
 
        if (io->generic.level != RAW_CLOSE_CLOSE) {
-               return ntvfs_map_close(req, io, ntvfs);
+               return ntvfs_map_close(ntvfs, req, io);
        }
 
-       f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
+       f = pvfs_find_fd(pvfs, req, io->close.in.file.ntvfs);
        if (!f) {
                return NT_STATUS_INVALID_HANDLE;
        }
@@ -1138,14 +1447,14 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
   logoff - close all file descriptors open by a vuid
 */
 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
-                    struct smbsrv_request *req)
+                    struct ntvfs_request *req)
 {
        struct pvfs_state *pvfs = ntvfs->private_data;
        struct pvfs_file *f, *next;
 
-       for (f=pvfs->open_files;f;f=next) {
+       for (f=pvfs->files.list;f;f=next) {
                next = f->next;
-               if (f->session == req->session) {
+               if (f->ntvfs->session_info == req->session_info) {
                        talloc_free(f);
                }
        }
@@ -1158,14 +1467,15 @@ NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
   exit - close files for the current pid
 */
 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
-                  struct smbsrv_request *req)
+                  struct ntvfs_request *req)
 {
        struct pvfs_state *pvfs = ntvfs->private_data;
        struct pvfs_file *f, *next;
 
-       for (f=pvfs->open_files;f;f=next) {
+       for (f=pvfs->files.list;f;f=next) {
                next = f->next;
-               if (f->smbpid == req->smbpid) {
+               if (f->ntvfs->session_info == req->session_info &&
+                   f->ntvfs->smbpid == req->smbpid) {
                        talloc_free(f);
                }
        }
@@ -1175,33 +1485,38 @@ NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
 
 
 /*
-  change the create options on an already open file
+  change the delete on close flag on an already open file
 */
-NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
-                                   struct smbsrv_request *req, 
-                                   struct pvfs_file *f, uint32_t create_options)
+NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
+                                 struct ntvfs_request *req, 
+                                 struct pvfs_file *f, bool del_on_close)
 {
        struct odb_lock *lck;
        NTSTATUS status;
 
-       if (f->handle->create_options == create_options) {
-               return NT_STATUS_OK;
-       }
-
-       if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
-           (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+       if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
                return NT_STATUS_CANNOT_DELETE;
        }
+       
+       if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+           !pvfs_directory_empty(pvfs, f->handle->name)) {
+               return NT_STATUS_DIRECTORY_NOT_EMPTY;
+       }
 
+       if (del_on_close) {
+               f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+       } else {
+               f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+       }
+       
        lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
        if (lck == NULL) {
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
-       status = odb_set_create_options(lck, f->handle, create_options);
-       if (NT_STATUS_IS_OK(status)) {
-               f->handle->create_options = create_options;
-       }
+       status = odb_set_delete_on_close(lck, del_on_close);
+
+       talloc_free(lck);
 
        return status;
 }
@@ -1211,22 +1526,63 @@ NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
   determine if a file can be deleted, or if it is prevented by an
   already open file
 */
-NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
+NTSTATUS pvfs_can_delete(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 delete_on_close;
 
        status = pvfs_locking_key(name, name, &key);
        if (!NT_STATUS_IS_OK(status)) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = odb_can_open(pvfs->odb_context, &key, 
-                             NTCREATEX_SHARE_ACCESS_READ |
-                             NTCREATEX_SHARE_ACCESS_WRITE | 
-                             NTCREATEX_SHARE_ACCESS_DELETE, 
-                             NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 
-                             SEC_STD_DELETE);
+       lck = odb_lock(req, pvfs->odb_context, &key);
+       if (lck == NULL) {
+               DEBUG(0,("Unable to lock opendb for can_delete\n"));
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+       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, access_mask);
+       }
+
+       /*
+        * 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;
 }
@@ -1235,21 +1591,191 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
   determine if a file can be renamed, or if it is prevented by an
   already open file
 */
-NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
+NTSTATUS pvfs_can_rename(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 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;
+       access_mask     = SEC_STD_DELETE;
+       delete_on_close = false;
+
+       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;
+       }
+
+       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;
+       }
+
+       return status;
+}
+
+/*
+  determine if file meta data can be accessed, or if it is prevented by an
+  already open file
+*/
+NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs, 
+                      struct ntvfs_request *req,
+                      struct pvfs_filename *name)
 {
        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)) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = odb_can_open(pvfs->odb_context, &key, 
-                             NTCREATEX_SHARE_ACCESS_READ |
-                             NTCREATEX_SHARE_ACCESS_WRITE,
-                             0,
-                             SEC_STD_DELETE);
+       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;
+       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;
 }
+
+
+/*
+  determine if delete on close is set on 
+*/
+bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h)
+{
+       NTSTATUS status;
+       bool del_on_close;
+
+       status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key, 
+                                        &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 del_on_close;
+}