s4-pvfs: log more error conditions in NTVFS backend
[ira/wip.git] / source4 / ntvfs / posix / pvfs_open.c
index adf4c1ac185080f4a8f7a7d629aeea9dd6e29de9..d9d0d2178ab134949398e5acdb439fd02927c777 100644 (file)
@@ -23,7 +23,7 @@
 #include "vfs_posix.h"
 #include "system/dir.h"
 #include "system/time.h"
-#include "lib/util/dlinklist.h"
+#include "../lib/util/dlinklist.h"
 #include "messaging/messaging.h"
 #include "librpc/gen_ndr/xattr.h"
 
@@ -103,9 +103,10 @@ 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)
+                                       union smb_open *io,
+                                       struct security_descriptor *sd)
 {
-       NTSTATUS status;
+       NTSTATUS status = NT_STATUS_OK;
 
        /* setup any EAs that were asked for */
        if (io->ntcreatex.in.ea_list) {
@@ -118,7 +119,7 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
        }
 
        /* setup an initial sec_desc if requested */
-       if (io->ntcreatex.in.sec_desc) {
+       if (sd && (sd->type & SEC_DESC_DACL_PRESENT)) {
                union smb_setfileinfo set;
 /* 
  * TODO: set the full ACL! 
@@ -129,12 +130,9 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
  */
                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;
+               set.set_secdesc.in.sd = sd;
 
                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;
@@ -182,12 +180,20 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        bool del_on_close;
        uint32_t create_options;
        uint32_t share_access;
+       bool forced;
+       struct security_descriptor *sd = NULL;
 
        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,
@@ -196,6 +202,15 @@ 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)) {
+               DEBUG(3,(__location__ ": Invalid access_mask/create_options 0x%08x 0x%08x for %s\n",
+                        io->ntcreatex.in.access_mask, io->ntcreatex.in.create_options, name->original_name));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
        switch (io->generic.in.open_disposition) {
        case NTCREATEX_DISP_OPEN_IF:
                break;
@@ -216,6 +231,8 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        case NTCREATEX_DISP_OVERWRITE:
        case NTCREATEX_DISP_SUPERSEDE:
        default:
+               DEBUG(3,(__location__ ": Invalid open disposition 0x%08x for %s\n",
+                        io->generic.in.open_disposition, name->original_name));
                return NT_STATUS_INVALID_PARAMETER;
        }
 
@@ -235,11 +252,16 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        if (name->exists) {
                /* check the security descriptor */
                status = pvfs_access_check(pvfs, req, name, &access_mask);
-       } else {
-               status = pvfs_access_check_create(pvfs, req, name, &access_mask);
+       } else {                
+               sd = io->ntcreatex.in.sec_desc;
+               status = pvfs_access_check_create(pvfs, req, name, &access_mask, true, &sd);
        }
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       if (io->generic.in.query_maximal_access) {
+               status = pvfs_access_maximal_allowed(pvfs, req, name, 
+                                                    &io->generic.out.maximal_access);
+               NT_STATUS_NOT_OK_RETURN(status);
        }
 
        f->ntvfs         = h;
@@ -258,11 +280,12 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        f->handle->fd                = -1;
        f->handle->odb_locking_key   = data_blob(NULL, 0);
        f->handle->create_options    = io->generic.in.create_options;
+       f->handle->private_flags     = io->generic.in.private_flags;
        f->handle->seek_offset       = 0;
        f->handle->position          = 0;
        f->handle->mode              = 0;
        f->handle->oplock            = NULL;
-       f->handle->sticky_write_time = false;
+       ZERO_STRUCT(f->handle->write_time);
        f->handle->open_completed    = false;
 
        if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
@@ -290,9 +313,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,
+               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, name->dos.write_time,
                                       false, OPLOCK_NONE, NULL);
 
                if (!NT_STATUS_IS_OK(status)) {
@@ -324,7 +355,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                        goto cleanup_delete;
                }
 
-               status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
+               status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io, sd);
                if (!NT_STATUS_IS_OK(status)) {
                        goto cleanup_delete;
                }
@@ -344,9 +375,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,
+               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, name->dos.write_time,
                                       false, OPLOCK_NONE, NULL);
 
                if (!NT_STATUS_IS_OK(status)) {
@@ -403,16 +441,9 @@ cleanup_delete:
 */
 static int pvfs_handle_destructor(struct pvfs_file_handle *h)
 {
-       /* 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);
-               }
-       }
-       
+       talloc_free(h->write_time.update_event);
+       h->write_time.update_event = NULL;
+
        if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
            h->name->stream_name) {
                NTSTATUS status;
@@ -431,6 +462,14 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h)
                h->fd = -1;
        }
 
+       if (!h->write_time.update_forced &&
+           h->write_time.update_on_close &&
+           h->write_time.close_time == 0) {
+               struct timeval tv;
+               tv = timeval_current();
+               h->write_time.close_time = timeval_to_nttime(&tv);
+       }
+
        if (h->have_opendb_entry) {
                struct odb_lock *lck;
                NTSTATUS status;
@@ -442,6 +481,26 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h)
                        return 0;
                }
 
+               if (h->write_time.update_forced) {
+                       status = odb_get_file_infos(h->pvfs->odb_context,
+                                                   &h->odb_locking_key,
+                                                   NULL,
+                                                   &h->write_time.close_time);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(0,("Unable get write time for '%s' - %s\n",
+                                        h->name->full_name, nt_errstr(status)));
+                       }
+
+                       h->write_time.update_forced = false;
+                       h->write_time.update_on_close = true;
+               } else if (h->write_time.update_on_close) {
+                       status = odb_set_write_time(lck, h->write_time.close_time, true);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(0,("Unable set write time for '%s' - %s\n",
+                                        h->name->full_name, nt_errstr(status)));
+                       }
+               }
+
                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", 
@@ -464,11 +523,26 @@ static int pvfs_handle_destructor(struct pvfs_file_handle *h)
                                               FILE_NOTIFY_CHANGE_FILE_NAME,
                                               delete_path);
                        }
+                       h->write_time.update_on_close = false;
                }
 
                talloc_free(lck);
        }
 
+       if (h->write_time.update_on_close) {
+               struct timeval tv[2];
+
+               nttime_to_timeval(&tv[0], h->name->dos.access_time);
+               nttime_to_timeval(&tv[1], h->write_time.close_time);
+
+               if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+                       if (utimes(h->name->full_name, tv) == -1) {
+                               DEBUG(3,("pvfs_handle_destructor: utimes() failed '%s' - %s\n",
+                                        h->name->full_name, strerror(errno)));
+                       }
+               }
+       }
+
        return 0;
 }
 
@@ -544,13 +618,30 @@ 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;
+       struct security_descriptor *sd = NULL;
 
+       if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
+               DEBUG(3,(__location__ ": Invalid file_attr 0x%08x for %s\n",
+                        io->ntcreatex.in.file_attr, name->original_name));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_ENCRYPTED) {
+               DEBUG(3,(__location__ ": Invalid encryption request for %s\n",
+                        name->original_name));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+           
        if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
            (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+               DEBUG(4,(__location__ ": Invalid delete on close for readonly file %s\n",
+                        name->original_name));
                return NT_STATUS_CANNOT_DELETE;
        }
-       
-       status = pvfs_access_check_create(pvfs, req, name, &access_mask);
+
+       sd = io->ntcreatex.in.sec_desc;
+       status = pvfs_access_check_create(pvfs, req, name, &access_mask, false, &sd);
        NT_STATUS_NOT_OK_RETURN(status);
 
        /* check that the parent isn't opened with delete on close set */
@@ -559,8 +650,8 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                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);
+               status = odb_get_file_infos(pvfs->odb_context, &locking_key,
+                                           &del_on_close, NULL);
                NT_STATUS_NOT_OK_RETURN(status);
                if (del_on_close) {
                        return NT_STATUS_DELETE_PENDING;
@@ -586,7 +677,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);
        }
@@ -603,12 +694,14 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        }
 
        /* re-resolve the open fd */
-       status = pvfs_resolve_name_fd(pvfs, fd, name);
+       status = pvfs_resolve_name_fd(pvfs, fd, name, 0);
        if (!NT_STATUS_IS_OK(status)) {
                close(fd);
                return status;
        }
 
+       /* support initial alloc sizes */
+       name->dos.alloc_size = io->ntcreatex.in.alloc_size;
        name->dos.attrib = attrib;
        status = pvfs_dosattrib_save(pvfs, name, fd);
        if (!NT_STATUS_IS_OK(status)) {
@@ -616,11 +709,17 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        }
 
 
-       status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io);
+       status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io, sd);
        if (!NT_STATUS_IS_OK(status)) {
                goto cleanup_delete;
        }
 
+       if (io->generic.in.query_maximal_access) {
+               status = pvfs_access_maximal_allowed(pvfs, req, name, 
+                                                    &io->generic.out.maximal_access);
+               NT_STATUS_NOT_OK_RETURN(status);
+       }
+
        /* form the lock context used for byte range locking and
           opendb locking */
        status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
@@ -658,20 +757,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;
        }
 
-
        f->ntvfs             = h;
        f->pvfs              = pvfs;
        f->pending_list      = NULL;
@@ -686,31 +787,44 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        f->handle->name              = talloc_steal(f->handle, name);
        f->handle->fd                = fd;
        f->handle->create_options    = io->generic.in.create_options;
+       f->handle->private_flags     = io->generic.in.private_flags;
        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;
+       ZERO_STRUCT(f->handle->write_time);
        f->handle->open_completed    = false;
 
+       status = odb_open_file(lck, f->handle, name->full_name,
+                              &f->handle->fd, name->dos.write_time,
+                              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
+          abnormal termination */
+       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)) {
-                       talloc_free(lck);
                        return status;
                }
        }
 
-       /* 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);
-
        io->generic.out.oplock_level  = oplock_granted;
        io->generic.out.file.ntvfs    = f->ntvfs;
        io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
@@ -766,7 +880,8 @@ struct pvfs_odb_retry {
 /* destroy a pending request */
 static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
 {
-       struct pvfs_state *pvfs = r->ntvfs->private_data;
+       struct pvfs_state *pvfs = talloc_get_type(r->ntvfs->private_data,
+                                 struct pvfs_state);
        if (r->odb_locking_key.data) {
                struct odb_lock *lck;
                lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
@@ -796,7 +911,7 @@ static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
 
 /*
   setup for a retry of a request that was rejected
-  by odb_open_file() or odb_can_open()
+  by odb_can_open()
 */
 NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
                              struct ntvfs_request *req,
@@ -811,7 +926,8 @@ NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
                                               void *private_data,
                                               enum pvfs_wait_notice reason))
 {
-       struct pvfs_state *pvfs = ntvfs->private_data;
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
        struct pvfs_odb_retry *r;
        struct pvfs_wait *wait_handle;
        NTSTATUS status;
@@ -831,7 +947,13 @@ NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
 
        /* setup a pending lock */
        status = odb_open_file_pending(lck, r);
-       if (!NT_STATUS_IS_OK(status)) {
+       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;
        }
 
@@ -848,8 +970,6 @@ NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
 
        talloc_steal(r, wait_handle);
 
-       talloc_steal(pvfs, r);
-
        return NT_STATUS_OK;
 }
 
@@ -864,8 +984,14 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
                                    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) {
@@ -873,6 +999,16 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
        }
 
        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);
@@ -881,6 +1017,7 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
                return;
        }
 
+retry:
        talloc_free(r);
 
        /* try the open again, which could trigger another retry setup
@@ -916,7 +1053,8 @@ static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
                                   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_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
        struct pvfs_file *f2;
        struct pvfs_filename *name;
        NTSTATUS status;
@@ -935,7 +1073,7 @@ static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
                if (f2 != f &&
                    f2->ntvfs->session_info == req->session_info &&
                    f2->ntvfs->smbpid == req->smbpid &&
-                   (f2->handle->create_options & 
+                   (f2->handle->private_flags &
                     (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
                      NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
                    (f2->access_mask & SEC_FILE_WRITE_DATA) &&
@@ -951,7 +1089,7 @@ static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
 
        /* quite an insane set of semantics ... */
        if (is_exe_filename(io->generic.in.fname) &&
-           (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
+           (f2->handle->private_flags & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
                return NT_STATUS_SHARING_VIOLATION;
        }
 
@@ -997,11 +1135,13 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
                                      struct odb_lock *lck,
                                      NTSTATUS parent_status)
 {
-       struct pvfs_state *pvfs = ntvfs->private_data;
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
        NTSTATUS status;
        struct timeval end_time;
+       struct timeval *final_timeout = NULL;
 
-       if (io->generic.in.create_options & 
+       if (io->generic.in.private_flags &
            (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
                /* see if we can satisfy the request using the special DENY_DOS
                   code */
@@ -1020,12 +1160,28 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
        } 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;
        }
 
-       return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
-                                   pvfs_retry_open_sharing);
+       return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
+                                   final_timeout, pvfs_retry_open_sharing);
 }
 
 /*
@@ -1034,8 +1190,9 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                   struct ntvfs_request *req, union smb_open *io)
 {
-       struct pvfs_state *pvfs = ntvfs->private_data;
-       int flags;
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
+       int flags = 0;
        struct pvfs_filename *name;
        struct pvfs_file *f;
        struct ntvfs_handle *h;
@@ -1043,11 +1200,14 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        int fd;
        struct odb_lock *lck;
        uint32_t create_options;
+       uint32_t create_options_must_ignore_mask;
        uint32_t share_access;
        uint32_t access_mask;
+       uint32_t create_action = NTCREATEX_ACTION_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. */
@@ -1056,6 +1216,109 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                return ntvfs_map_open(ntvfs, req, io);
        }
 
+       ZERO_STRUCT(io->generic.out);
+
+       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) {
+               DEBUG(3,(__location__ ": Invalid share_access 0x%08x for %s\n",
+                        share_access, io->ntcreatex.in.fname));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /*
+        * These options are ignored,
+        * but we reuse some of them as private values for the generic mapping
+        */
+       create_options_must_ignore_mask = NTCREATEX_OPTIONS_MUST_IGNORE_MASK;
+       create_options &= ~create_options_must_ignore_mask;
+
+       if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
+               DEBUG(2,(__location__ " create_options 0x%x not supported\n", 
+                        create_options));
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       if (create_options & NTCREATEX_OPTIONS_INVALID_PARAM_MASK) {
+               DEBUG(3,(__location__ ": Invalid create_options 0x%08x for %s\n",
+                        create_options, io->ntcreatex.in.fname));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* TODO: When we implement HSM, add a hook here not to pull
+        * the actual file off tape, when this option is passed from
+        * the client */
+       if (create_options & NTCREATEX_OPTIONS_NO_RECALL) {
+               /* no-op */
+       }
+
+       /* TODO: If (unlikely) Linux does a good compressed
+        * filesystem, we might need an ioctl call for this */
+       if (create_options & NTCREATEX_OPTIONS_NO_COMPRESSION) {
+               /* no-op */
+       }
+
+       if (create_options & NTCREATEX_OPTIONS_NO_INTERMEDIATE_BUFFERING) {
+               create_options |= NTCREATEX_OPTIONS_WRITE_THROUGH;
+       }
+
+       /* Open the file with sync, if they asked for it, but
+          'strict sync = no' turns this client request into a no-op */
+       if (create_options & (NTCREATEX_OPTIONS_WRITE_THROUGH) && !(pvfs->flags | PVFS_FLAG_STRICT_SYNC)) {
+               flags |= O_SYNC;
+       }
+
+
+       /* other create options are not allowed */
+       if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+           !(access_mask & SEC_STD_DELETE)) {
+               DEBUG(3,(__location__ ": Invalid delete_on_close option 0x%08x with access_mask 0x%08x for %s\n",
+                        create_options, access_mask, io->ntcreatex.in.fname));
+               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) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       /* cope with non-zero root_fid */
+       if (io->ntcreatex.in.root_fid.ntvfs != NULL) {
+               f = pvfs_find_fd(pvfs, req, io->ntcreatex.in.root_fid.ntvfs);
+               if (f == NULL) {
+                       return NT_STATUS_INVALID_HANDLE;
+               }
+               if (f->handle->fd != -1) {
+                       return NT_STATUS_INVALID_DEVICE_REQUEST;
+               }
+               io->ntcreatex.in.fname = talloc_asprintf(req, "%s\\%s", 
+                                                        f->handle->name->original_name,
+                                                        io->ntcreatex.in.fname);
+               NT_STATUS_HAVE_NO_MEMORY(io->ntcreatex.in.fname);                       
+       }
+
+       if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
+                                         FILE_ATTRIBUTE_VOLUME| 
+                                         (~FILE_ATTRIBUTE_ALL_MASK))) {
+               DEBUG(3,(__location__ ": Invalid file_attr 0x%08x for %s\n",
+                        io->ntcreatex.in.file_attr, io->ntcreatex.in.fname));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* we ignore some file_attr bits */
+       io->ntcreatex.in.file_attr &= ~(FILE_ATTRIBUTE_NONINDEXED | 
+                                       FILE_ATTRIBUTE_COMPRESSED |
+                                       FILE_ATTRIBUTE_REPARSE_POINT |
+                                       FILE_ATTRIBUTE_SPARSE |
+                                       FILE_ATTRIBUTE_NORMAL);
+
        /* resolve the cifs name to a posix name */
        status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 
                                   PVFS_RESOLVE_STREAMS, &name);
@@ -1063,6 +1326,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)) {
@@ -1073,18 +1350,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) {
        case NTCREATEX_DISP_SUPERSEDE:
        case NTCREATEX_DISP_OVERWRITE_IF:
@@ -1093,6 +1358,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                } else {
                        stream_truncate = true;
                }
+               create_action = NTCREATEX_ACTION_TRUNCATED;
                break;
 
        case NTCREATEX_DISP_OPEN:
@@ -1111,6 +1377,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                } else {
                        stream_truncate = true;
                }
+               create_action = NTCREATEX_ACTION_TRUNCATED;
                break;
 
        case NTCREATEX_DISP_CREATE:
@@ -1125,6 +1392,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                break;
 
        default:
+               DEBUG(3,(__location__ ": Invalid open disposition 0x%08x for %s\n",
+                        io->generic.in.open_disposition, name->original_name));
                return NT_STATUS_INVALID_PARAMETER;
        }
 
@@ -1155,8 +1424,12 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
 
        /* check the security descriptor */
        status = pvfs_access_check(pvfs, req, name, &access_mask);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       if (io->generic.in.query_maximal_access) {
+               status = pvfs_access_maximal_allowed(pvfs, req, name, 
+                                                    &io->generic.out.maximal_access);
+               NT_STATUS_NOT_OK_RETURN(status);
        }
 
        status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
@@ -1186,12 +1459,13 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        f->handle->fd                = -1;
        f->handle->name              = talloc_steal(f->handle, name);
        f->handle->create_options    = io->generic.in.create_options;
+       f->handle->private_flags     = io->generic.in.private_flags;
        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;
+       ZERO_STRUCT(f->handle->write_time);
        f->handle->open_completed    = false;
 
        /* form the lock context used for byte range locking and
@@ -1242,11 +1516,14 @@ 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);
+       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
@@ -1265,18 +1542,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
                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;
-
        if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
                flags |= O_RDWR;
        } else {
@@ -1284,14 +1549,49 @@ 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);
+
+               DEBUG(0,(__location__ " mapped errno %s for %s (was %d)\n", 
+                        nt_errstr(status), f->handle->name->full_name, 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, name->dos.write_time,
+                              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 */
@@ -1311,7 +1611,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        }
 
        /* re-resolve the open fd */
-       status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
+       status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name, PVFS_RESOLVE_NO_OPENDB);
        if (!NT_STATUS_IS_OK(status)) {
                talloc_free(lck);
                return status;
@@ -1320,13 +1620,16 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        if (f->handle->name->stream_id == 0 &&
            (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
             io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
-               /* for overwrite we need to replace file permissions */
+               /* for overwrite we may need to replace file permissions */
                uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
                mode_t mode = pvfs_fileperms(pvfs, attrib);
-               if (fchmod(fd, mode) == -1) {
+               if (f->handle->name->st.st_mode != mode &&
+                   f->handle->name->dos.attrib != attrib &&
+                   fchmod(fd, mode) == -1) {
                        talloc_free(lck);
                        return pvfs_map_errno(pvfs, errno);
                }
+               name->dos.alloc_size = io->ntcreatex.in.alloc_size;
                name->dos.attrib = attrib;
                status = pvfs_dosattrib_save(pvfs, name, fd);
                if (!NT_STATUS_IS_OK(status)) {
@@ -1347,7 +1650,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        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;
+               create_action:NTCREATEX_ACTION_CREATED;
+       
        io->generic.out.create_time   = name->dos.create_time;
        io->generic.out.access_time   = name->dos.access_time;
        io->generic.out.write_time    = name->dos.write_time;
@@ -1369,31 +1673,50 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
                    struct ntvfs_request *req, union smb_close *io)
 {
-       struct pvfs_state *pvfs = ntvfs->private_data;
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
        struct pvfs_file *f;
-       struct utimbuf unix_times;
 
        if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
                return NT_STATUS_DOS(ERRSRV, ERRerror);
        }
 
-       if (io->generic.level != RAW_CLOSE_CLOSE) {
+       if (io->generic.level != RAW_CLOSE_GENERIC) {
                return ntvfs_map_close(ntvfs, req, io);
        }
 
-       f = pvfs_find_fd(pvfs, req, io->close.in.file.ntvfs);
+       f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
        if (!f) {
                return NT_STATUS_INVALID_HANDLE;
        }
 
-       if (!null_time(io->close.in.write_time)) {
-               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);
+       if (!null_time(io->generic.in.write_time)) {
+               f->handle->write_time.update_forced = false;
+               f->handle->write_time.update_on_close = true;
+               unix_to_nt_time(&f->handle->write_time.close_time, io->generic.in.write_time);
+       }
+
+       if (io->generic.in.flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
+               struct pvfs_filename *name;
+               NTSTATUS status;
+               struct pvfs_file_handle *h = f->handle;
+
+               status = pvfs_resolve_name_handle(pvfs, h);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               name = h->name;
+
+               io->generic.out.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
+               io->generic.out.create_time = name->dos.create_time;
+               io->generic.out.access_time = name->dos.access_time;
+               io->generic.out.write_time  = name->dos.write_time;
+               io->generic.out.change_time = name->dos.change_time;
+               io->generic.out.alloc_size  = name->dos.alloc_size;
+               io->generic.out.size        = name->st.st_size;
+               io->generic.out.file_attr   = name->dos.attrib;         
+       } else {
+               ZERO_STRUCT(io->generic.out);
        }
 
        talloc_free(f);
@@ -1408,9 +1731,15 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
                     struct ntvfs_request *req)
 {
-       struct pvfs_state *pvfs = ntvfs->private_data;
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
        struct pvfs_file *f, *next;
 
+       /* If pvfs is NULL, we never logged on, and no files are open. */
+       if(pvfs == NULL) {
+               return NT_STATUS_OK;
+       }
+
        for (f=pvfs->files.list;f;f=next) {
                next = f->next;
                if (f->ntvfs->session_info == req->session_info) {
@@ -1428,7 +1757,8 @@ NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
                   struct ntvfs_request *req)
 {
-       struct pvfs_state *pvfs = ntvfs->private_data;
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
        struct pvfs_file *f, *next;
 
        for (f=pvfs->files.list;f;f=next) {
@@ -1638,15 +1968,12 @@ NTSTATUS pvfs_can_update_file_size(struct pvfs_state *pvfs,
                          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
+        * this code previous set only SEC_FILE_WRITE_ATTRIBUTE, with
+        * a comment that this seemed to be wrong, but matched windows
+        * behaviour. It now appears that this windows behaviour is
+        * just a bug.
         */
-       access_mask     = SEC_FILE_WRITE_ATTRIBUTE;
+       access_mask     = SEC_FILE_WRITE_ATTRIBUTE | SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA;
        delete_on_close = false;
        break_to_none   = true;
 
@@ -1729,8 +2056,8 @@ bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *
        NTSTATUS status;
        bool del_on_close;
 
-       status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key, 
-                                        &del_on_close);
+       status = odb_get_file_infos(pvfs->odb_context, &h->odb_locking_key, 
+                                   &del_on_close, NULL);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
                return false;