check for some more invalid bits in smb2 create
[kai/samba.git] / source4 / ntvfs / posix / pvfs_open.c
index 47b44b9634ee6c8263e7ccc9e2988afdc1a47177..328f064a573a0cde1bcc51b57f6c701ebda5f615 100644 (file)
@@ -182,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,
@@ -196,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;
@@ -262,7 +276,6 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        f->handle->position          = 0;
        f->handle->mode              = 0;
        f->handle->oplock            = NULL;
-       f->handle->sticky_write_time = false;
        f->handle->open_completed    = false;
 
        if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
@@ -290,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, 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);
@@ -344,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, 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;
@@ -403,16 +429,6 @@ 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);
-               }
-       }
-       
        if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
            h->name->stream_name) {
                NTSTATUS status;
@@ -546,11 +562,15 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        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_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);
 
@@ -587,7 +607,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);
        }
@@ -663,21 +683,18 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                allow_level_II_oplock = true;
        }
 
-       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, allow_level_II_oplock,
-                              oplock_level, &oplock_granted);
-       talloc_free(lck);
+       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;
@@ -697,9 +714,20 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        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
@@ -801,7 +829,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,
@@ -836,7 +864,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;
        }
 
@@ -853,8 +887,6 @@ NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
 
        talloc_steal(r, wait_handle);
 
-       talloc_steal(pvfs, r);
-
        return NT_STATUS_OK;
 }
 
@@ -869,8 +901,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) {
@@ -878,6 +916,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);
@@ -886,6 +934,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
@@ -1005,6 +1054,7 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
        struct pvfs_state *pvfs = ntvfs->private_data;
        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)) {
@@ -1025,12 +1075,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);
 }
 
 /*
@@ -1062,6 +1128,34 @@ 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 | SEC_STD_SYNCHRONIZE)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
+                                         FILE_ATTRIBUTE_VOLUME)) {
+               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);
@@ -1069,6 +1163,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)) {
@@ -1079,16 +1187,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) {
@@ -1197,7 +1295,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        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
@@ -1253,11 +1350,9 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
        }
 
        /* see if we are allowed to open at the same time as existing opens */
-       status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
-                              share_access, access_mask, del_on_close,
-                              io->generic.in.open_disposition,
-                              false, allow_level_II_oplock,
-                              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
@@ -1276,18 +1371,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 {
@@ -1295,14 +1378,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 */
@@ -1401,10 +1516,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);