make the SEC_STD_SYNCHRONIZE test more specific
[kai/samba.git] / source4 / ntvfs / posix / pvfs_open.c
index bbfe4ac7336fc2b5f756919530f35dcc0140aeb6..cfa88b6baad40e8dbb511fa7a343f7cd4bf2efc4 100644 (file)
@@ -50,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) {
@@ -80,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);
        }
 
@@ -151,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;
@@ -189,12 +182,19 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        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,
@@ -203,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;
@@ -268,7 +275,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        f->handle->seek_offset       = 0;
        f->handle->position          = 0;
        f->handle->mode              = 0;
-       f->handle->sticky_write_time = false;
+       f->handle->oplock            = NULL;
        f->handle->open_completed    = false;
 
        if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
@@ -296,10 +303,17 @@ 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, name->full_name, name->stream_id,
-                                      share_access, access_mask, del_on_close, 
-                                      io->generic.in.open_disposition,
-                                      false, 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);
@@ -350,10 +364,16 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                        return NT_STATUS_INTERNAL_DB_CORRUPTION;
                }
 
-               status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
-                                      share_access, access_mask, del_on_close, 
-                                      io->generic.in.open_disposition,
-                                      false, 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;
+               }
+
+               status = odb_open_file(lck, f->handle, name->full_name,
+                                      NULL, false, OPLOCK_NONE, NULL);
 
                if (!NT_STATUS_IS_OK(status)) {
                        goto cleanup_delete;
@@ -409,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;
@@ -440,32 +447,10 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h)
                h->fd = -1;
        }
 
-       if (h->name->stream_name == NULL && 
-           h->open_completed &&
-           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) {
@@ -473,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);
        }
 
@@ -557,12 +560,21 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        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);
 
@@ -573,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;
@@ -599,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);
        }
@@ -671,23 +683,22 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                oplock_level = OPLOCK_EXCLUSIVE;
        }
 
-       status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
-                              share_access, access_mask, del_on_close, 
-                              io->generic.in.open_disposition,
-                              false, 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->pvfs              = pvfs;
        f->pending_list      = NULL;
@@ -705,10 +716,22 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        f->handle->seek_offset       = 0;
        f->handle->position          = 0;
        f->handle->mode              = 0;
+       f->handle->oplock            = NULL;
        f->handle->have_opendb_entry = true;
-       f->handle->sticky_write_time = false;
        f->handle->open_completed    = false;
 
+       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);
 
        /* setup a destructor to avoid file descriptor leaks on
@@ -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;
@@ -751,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;
-       struct pvfs_wait *wait_handle;
        DATA_BLOB odb_locking_key;
+       void *io;
+       void *private_data;
+       void (*callback)(struct pvfs_odb_retry *r,
+                        struct ntvfs_module_context *ntvfs,
+                        struct ntvfs_request *req,
+                        void *io,
+                        void *private_data,
+                        enum pvfs_wait_notice reason);
 };
 
-/* destroy a pending open request */
-static int pvfs_retry_destructor(struct pvfs_open_retry *r)
+/* destroy a pending request */
+static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
 {
        struct pvfs_state *pvfs = r->ntvfs->private_data;
        if (r->odb_locking_key.data) {
@@ -778,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);
+}
+
 /*
-  retry an open
+  setup for a retry of a request that was rejected
+  by odb_can_open()
 */
-static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
+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_open_retry *r = private;
-       struct ntvfs_module_context *ntvfs = r->ntvfs;
-       struct ntvfs_request *req = r->req;
-       union smb_open *io = r->io;
+       struct pvfs_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 after a sharing violation
+*/
+static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
+                                   struct ntvfs_module_context *ntvfs,
+                                   struct ntvfs_request *req,
+                                   void *_io,
+                                   void *private_data,
+                                   enum pvfs_wait_notice reason)
+{
+       union smb_open *io = talloc_get_type(_io, union smb_open);
+       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);
@@ -806,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
@@ -922,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)) {
@@ -939,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);
 }
 
 /*
@@ -995,6 +1123,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        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. */
@@ -1003,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);
@@ -1010,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)) {
@@ -1020,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) {
@@ -1136,8 +1305,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        f->handle->seek_offset       = 0;
        f->handle->position          = 0;
        f->handle->mode              = 0;
+       f->handle->oplock            = NULL;
        f->handle->have_opendb_entry = false;
-       f->handle->sticky_write_time = false;
        f->handle->open_completed    = false;
 
        /* form the lock context used for byte range locking and
@@ -1188,17 +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, name->full_name, name->stream_id,
-                              share_access, access_mask, del_on_close,
-                              io->generic.in.open_disposition,
-                              false, oplock_level, &oplock_granted);
-
-       /* on a sharing violation we need to retry when the file is closed by 
-          the other user, or after 1 second */
-       if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
+       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
+        * 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)) {
@@ -1206,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 {
@@ -1219,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 */
@@ -1325,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);
@@ -1451,7 +1650,7 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
 
        status = odb_can_open(lck, name->stream_id,
                              share_access, access_mask, delete_on_close,
-                             0, false);
+                             NTCREATEX_DISP_OPEN, false);
 
        if (NT_STATUS_IS_OK(status)) {
                status = pvfs_access_check_simple(pvfs, req, name, access_mask);
@@ -1515,7 +1714,79 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
 
        status = odb_can_open(lck, name->stream_id,
                              share_access, access_mask, delete_on_close,
-                             0, false);
+                             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
@@ -1569,12 +1840,12 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
 
        share_access    = NTCREATEX_SHARE_ACCESS_READ |
                          NTCREATEX_SHARE_ACCESS_WRITE;
-       access_mask     = 0;
+       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,
-                             0, false);
+                             NTCREATEX_DISP_OPEN, false);
 
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(lck);
@@ -1587,14 +1858,13 @@ 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;
 
        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;