vfs3: Pass "lease" through SMB_VFS_CREATE_FILE
[metze/samba/wip.git] / source3 / smbd / open.c
index b7586aa1777b7308a7d9ac7346926e95e2b28e5c..4157280b084f49dfed9ef65c5ec4fa629a823b67 100644 (file)
@@ -29,6 +29,7 @@
 #include "../librpc/gen_ndr/ndr_security.h"
 #include "../librpc/gen_ndr/open_files.h"
 #include "../librpc/gen_ndr/idmap.h"
+#include "../librpc/gen_ndr/ioctl.h"
 #include "passdb/lookup_sid.h"
 #include "auth.h"
 #include "serverid.h"
@@ -301,6 +302,46 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+ Ensure when opening a base file for a stream open that we have permissions
+ to do so given the access mask on the base file.
+****************************************************************************/
+
+static NTSTATUS check_base_file_access(struct connection_struct *conn,
+                               struct smb_filename *smb_fname,
+                               uint32_t access_mask)
+{
+       NTSTATUS status;
+
+       status = smbd_calculate_access_mask(conn, smb_fname,
+                                       false,
+                                       access_mask,
+                                       &access_mask);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("smbd_calculate_access_mask "
+                       "on file %s returned %s\n",
+                       smb_fname_str_dbg(smb_fname),
+                       nt_errstr(status)));
+               return status;
+       }
+
+       if (access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) {
+               uint32_t dosattrs;
+               if (!CAN_WRITE(conn)) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+               dosattrs = dos_mode(conn, smb_fname);
+               if (IS_DOS_READONLY(dosattrs)) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+       }
+
+       return smbd_check_access_rights(conn,
+                                       smb_fname,
+                                       false,
+                                       access_mask);
+}
+
 /****************************************************************************
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
@@ -319,7 +360,7 @@ NTSTATUS fd_open(struct connection_struct *conn,
         * client should be doing this.
         */
 
-       if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
+       if (fsp->posix_open || !lp_follow_symlinks(SNUM(conn))) {
                flags |= O_NOFOLLOW;
        }
 #endif
@@ -799,8 +840,11 @@ static NTSTATUS open_file(files_struct *fsp,
                        }
                }
 
-               /* Actually do the open */
-               status = fd_open_atomic(conn, fsp, local_flags,
+               /*
+                * Actually do the open - if O_TRUNC is needed handle it
+                * below under the share mode lock.
+                */
+               status = fd_open_atomic(conn, fsp, local_flags & ~O_TRUNC,
                                unx_mode, p_file_created);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
@@ -830,7 +874,7 @@ static NTSTATUS open_file(files_struct *fsp,
                           in the stat struct in fsp->fsp_name. */
 
                        /* Inherit the ACL if required */
-                       if (lp_inherit_perms(SNUM(conn))) {
+                       if (lp_inherit_permissions(SNUM(conn))) {
                                inherit_access_posix_acl(conn, parent_dir,
                                                         smb_fname->base_name,
                                                         unx_mode);
@@ -1039,6 +1083,11 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
                return;
        }
 
+       if (share_entry->share_file_id == 0) {
+               /* INTERNAL_OPEN_ONLY */
+               return;
+       }
+
        if (!is_valid_share_mode_entry(share_entry)) {
                return;
        }
@@ -1107,7 +1156,7 @@ static bool has_delete_on_close(struct share_mode_lock *lck,
 
 /****************************************************************************
  Deal with share modes
- Invarient: Share mode must be locked on entry and exit.
+ Invariant: Share mode must be locked on entry and exit.
  Returns -1 on error, or number of share modes on success (may be zero).
 ****************************************************************************/
 
@@ -1168,19 +1217,21 @@ static NTSTATUS open_mode_check(connection_struct *conn,
  */
 
 static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
-                                  struct share_mode_entry *exclusive,
-                                  uint64_t mid)
+                                  const struct share_mode_entry *exclusive,
+                                  uint16_t break_to)
 {
        NTSTATUS status;
        char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
 
        DEBUG(10, ("Sending break request to PID %s\n",
                   procid_str_static(&exclusive->pid)));
-       exclusive->op_mid = mid;
 
        /* Create the message. */
        share_mode_entry_to_message(msg, exclusive);
 
+       /* Overload entry->op_type */
+       SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
+
        status = messaging_send_buf(msg_ctx, exclusive->pid,
                                    MSG_SMB_BREAK_REQUEST,
                                    (uint8 *)msg, sizeof(msg));
@@ -1292,15 +1343,16 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
 }
 
 static bool delay_for_oplock(files_struct *fsp,
-                            uint64_t mid,
                             int oplock_request,
                             struct share_mode_lock *lck,
-                            bool have_sharing_violation)
+                            bool have_sharing_violation,
+                            uint32_t create_disposition)
 {
        struct share_mode_data *d = lck->data;
        struct share_mode_entry *entry;
        uint32_t num_non_stat_opens = 0;
        uint32_t i;
+       uint16_t break_to;
 
        if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
                return false;
@@ -1345,11 +1397,21 @@ static bool delay_for_oplock(files_struct *fsp,
                return false;
        }
 
+       switch (create_disposition) {
+       case FILE_SUPERSEDE:
+       case FILE_OVERWRITE_IF:
+               break_to = NO_OPLOCK;
+               break;
+       default:
+               break_to = LEVEL_II_OPLOCK;
+               break;
+       }
+
        if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
                if (share_mode_stale_pid(d, 0)) {
                        return false;
                }
-               send_break_message(fsp->conn->sconn->msg_ctx, entry, mid);
+               send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
                return true;
        }
        if (have_sharing_violation) {
@@ -1359,6 +1421,17 @@ static bool delay_for_oplock(files_struct *fsp,
                 */
                return false;
        }
+       if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
+           (break_to == NO_OPLOCK)) {
+               if (share_mode_stale_pid(d, 0)) {
+                       return false;
+               }
+               DEBUG(10, ("Asynchronously breaking level2 oplock for "
+                          "create_disposition=%u\n",
+                          (unsigned)create_disposition));
+               send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
+               return false;
+       }
        if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
                /*
                 * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
@@ -1369,7 +1442,7 @@ static bool delay_for_oplock(files_struct *fsp,
                return false;
        }
 
-       send_break_message(fsp->conn->sconn->msg_ctx, entry, mid);
+       send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
        return true;
 }
 
@@ -1494,23 +1567,28 @@ static void defer_open(struct share_mode_lock *lck,
                       struct smb_request *req,
                       struct deferred_open_record *state)
 {
+       struct deferred_open_record *open_rec;
+
        DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred "
                  "open entry for mid %llu\n",
                  (unsigned int)request_time.tv_sec,
                  (unsigned int)request_time.tv_usec,
                  (unsigned long long)req->mid));
 
-       if (!push_deferred_open_message_smb(req, request_time, timeout,
-                                      state->id, (char *)state, sizeof(*state))) {
+       open_rec = talloc(NULL, struct deferred_open_record);
+       if (open_rec == NULL) {
                TALLOC_FREE(lck);
-               exit_server("push_deferred_open_message_smb failed");
+               exit_server("talloc failed");
        }
+
+       *open_rec = *state;
+
        if (lck) {
                struct defer_open_state *watch_state;
                struct tevent_req *watch_req;
                bool ret;
 
-               watch_state = talloc(req->sconn, struct defer_open_state);
+               watch_state = talloc(open_rec, struct defer_open_state);
                if (watch_state == NULL) {
                        exit_server("talloc failed");
                }
@@ -1534,6 +1612,12 @@ static void defer_open(struct share_mode_lock *lck,
                        timeval_sum(&request_time, &timeout));
                SMB_ASSERT(ret);
        }
+
+       if (!push_deferred_open_message_smb(req, request_time, timeout,
+                                           state->id, open_rec)) {
+               TALLOC_FREE(lck);
+               exit_server("push_deferred_open_message_smb failed");
+       }
 }
 
 static void defer_open_done(struct tevent_req *req)
@@ -1673,6 +1757,7 @@ static NTSTATUS fcb_or_dos_open(struct smb_request *req,
 }
 
 static void schedule_defer_open(struct share_mode_lock *lck,
+                               struct file_id id,
                                struct timeval request_time,
                                struct smb_request *req)
 {
@@ -1703,7 +1788,7 @@ static void schedule_defer_open(struct share_mode_lock *lck,
 
        state.delayed_for_oplocks = True;
        state.async_open = false;
-       state.id = lck->data->id;
+       state.id = id;
 
        if (!request_timed_out(request_time, timeout)) {
                defer_open(lck, request_time, timeout, req, &state);
@@ -1859,11 +1944,9 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
  Return true if this is a state pointer to an asynchronous create.
 ****************************************************************************/
 
-bool is_deferred_open_async(const void *ptr)
+bool is_deferred_open_async(const struct deferred_open_record *rec)
 {
-       const struct deferred_open_record *state = (const struct deferred_open_record *)ptr;
-
-       return state->async_open;
+       return rec->async_open;
 }
 
 static bool clear_ads(uint32_t create_disposition)
@@ -2059,9 +2142,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                   create_options, (unsigned int)unx_mode, oplock_request,
                   (unsigned int)private_flags));
 
-       if ((req == NULL) && ((oplock_request & INTERNAL_OPEN_ONLY) == 0)) {
-               DEBUG(0, ("No smb request but not an internal only open!\n"));
-               return NT_STATUS_INTERNAL_ERROR;
+       if (req == NULL) {
+               /* Ensure req == NULL means INTERNAL_OPEN_ONLY */
+               SMB_ASSERT(((oplock_request & INTERNAL_OPEN_ONLY) != 0));
+       } else {
+               /* And req != NULL means no INTERNAL_OPEN_ONLY */
+               SMB_ASSERT(((oplock_request & INTERNAL_OPEN_ONLY) == 0));
        }
 
        /*
@@ -2069,10 +2155,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         */
 
        if (req) {
-               void *ptr;
+               struct deferred_open_record *open_rec;
                if (get_deferred_open_message_state(req,
                                &request_time,
-                               &ptr)) {
+                               &open_rec)) {
                        /* Remember the absolute time of the original
                           request with this mid. We'll use it later to
                           see if this has timed out. */
@@ -2080,7 +2166,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        /* If it was an async create retry, the file
                           didn't exist. */
 
-                       if (is_deferred_open_async(ptr)) {
+                       if (is_deferred_open_async(open_rec)) {
                                SET_STAT_INVALID(smb_fname->st);
                                file_existed = false;
                        }
@@ -2346,8 +2432,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        smb_panic("validate_oplock_types failed");
                }
 
-               if (delay_for_oplock(fsp, req->mid, 0, lck, false)) {
-                       schedule_defer_open(lck, request_time, req);
+               if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
+                       schedule_defer_open(lck, fsp->file_id, request_time, req);
                        TALLOC_FREE(lck);
                        DEBUG(10, ("Sent oplock break request to kernel "
                                   "oplock holder\n"));
@@ -2360,7 +2446,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 */
                state.delayed_for_oplocks = false;
                state.async_open = false;
-               state.id = lck->data->id;
+               state.id = fsp->file_id;
                defer_open(lck, request_time, timeval_set(0, 0), req, &state);
                TALLOC_FREE(lck);
                DEBUG(10, ("No Samba oplock around after EWOULDBLOCK. "
@@ -2457,9 +2543,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
        if ((req != NULL) &&
            delay_for_oplock(
-                   fsp, req->mid, oplock_request, lck,
-                   NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION))) {
-               schedule_defer_open(lck, request_time, req);
+                   fsp, oplock_request, lck,
+                   NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
+                   create_disposition)) {
+               schedule_defer_open(lck, fsp->file_id, request_time, req);
                TALLOC_FREE(lck);
                fd_close(fsp);
                return NT_STATUS_SHARING_VIOLATION;
@@ -2592,6 +2679,21 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                return status;
        }
 
+       /* Should we atomically (to the client at least) truncate ? */
+       if ((!new_file_created) &&
+           (flags2 & O_TRUNC) &&
+           (!S_ISFIFO(fsp->fsp_name->st.st_ex_mode))) {
+               int ret;
+
+               ret = vfs_set_filelen(fsp, 0);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix(errno);
+                       TALLOC_FREE(lck);
+                       fd_close(fsp);
+                       return status;
+               }
+       }
+
        grant_fsp_oplock_type(fsp, lck, oplock_request);
 
        /*
@@ -2678,7 +2780,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         * file structs.
         */
 
-       status = set_file_oplock(fsp, fsp->oplock_type);
+       status = set_file_oplock(fsp);
        if (!NT_STATUS_IS_OK(status)) {
                /*
                 * Could not get the kernel oplock
@@ -2779,42 +2881,20 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                                  (unsigned int)new_unx_mode));
        }
 
-       TALLOC_FREE(lck);
-
-       return NT_STATUS_OK;
-}
-
-
-/****************************************************************************
- Open a file for for write to ensure that we can fchmod it.
-****************************************************************************/
+       {
+               /*
+                * Deal with other opens having a modified write time.
+                */
+               struct timespec write_time = get_share_mode_write_time(lck);
 
-NTSTATUS open_file_fchmod(connection_struct *conn,
-                         struct smb_filename *smb_fname,
-                         files_struct **result)
-{
-       if (!VALID_STAT(smb_fname->st)) {
-               return NT_STATUS_INVALID_PARAMETER;
+               if (!null_timespec(write_time)) {
+                       update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
+               }
        }
 
-        return SMB_VFS_CREATE_FILE(
-               conn,                                   /* conn */
-               NULL,                                   /* req */
-               0,                                      /* root_dir_fid */
-               smb_fname,                              /* fname */
-               FILE_WRITE_DATA,                        /* access_mask */
-               (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
-                   FILE_SHARE_DELETE),
-               FILE_OPEN,                              /* create_disposition*/
-               0,                                      /* create_options */
-               0,                                      /* file_attributes */
-               INTERNAL_OPEN_ONLY,                     /* oplock_request */
-               0,                                      /* allocation_size */
-               0,                                      /* private_flags */
-               NULL,                                   /* sd */
-               NULL,                                   /* ea_list */
-               result,                                 /* result */
-               NULL);                                  /* pinfo */
+       TALLOC_FREE(lck);
+
+       return NT_STATUS_OK;
 }
 
 static NTSTATUS mkdir_internal(connection_struct *conn,
@@ -2885,7 +2965,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                }
        }
 
-       if (lp_inherit_perms(SNUM(conn))) {
+       if (lp_inherit_permissions(SNUM(conn))) {
                inherit_access_posix_acl(conn, parent_dir,
                                         smb_dname->base_name, mode);
                need_re_stat = true;
@@ -3125,21 +3205,39 @@ static NTSTATUS open_directory(connection_struct *conn,
                return status;
        }
 
-       mtimespec = smb_dname->st.st_ex_mtime;
-
+       /* Don't store old timestamps for directory
+          handles in the internal database. We don't
+          update them in there if new objects
+          are creaded in the directory. Currently
+          we only update timestamps on file writes.
+          See bug #9870.
+       */
+       ZERO_STRUCT(mtimespec);
+
+       if (access_mask & (FILE_LIST_DIRECTORY|
+                          FILE_ADD_FILE|
+                          FILE_ADD_SUBDIRECTORY|
+                          FILE_TRAVERSE|
+                          DELETE_ACCESS|
+                          FILE_DELETE_CHILD)) {
 #ifdef O_DIRECTORY
-       status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
+               status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
 #else
-       /* POSIX allows us to open a directory with O_RDONLY. */
-       status = fd_open(conn, fsp, O_RDONLY, 0);
+               /* POSIX allows us to open a directory with O_RDONLY. */
+               status = fd_open(conn, fsp, O_RDONLY, 0);
 #endif
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(5, ("open_directory: Could not open fd for "
-                       "%s (%s)\n",
-                       smb_fname_str_dbg(smb_dname),
-                       nt_errstr(status)));
-               file_free(req, fsp);
-               return status;
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(5, ("open_directory: Could not open fd for "
+                               "%s (%s)\n",
+                               smb_fname_str_dbg(smb_dname),
+                               nt_errstr(status)));
+                       file_free(req, fsp);
+                       return status;
+               }
+       } else {
+               fsp->fh->fd = -1;
+               DEBUG(10, ("Not opening Directory %s\n",
+                       smb_fname_str_dbg(smb_dname)));
        }
 
        status = vfs_stat_fsp(fsp);
@@ -3215,6 +3313,18 @@ static NTSTATUS open_directory(connection_struct *conn,
                }
        }
 
+       {
+               /*
+                * Deal with other opens having a modified write time. Is this
+                * possible for directories?
+                */
+               struct timespec write_time = get_share_mode_write_time(lck);
+
+               if (!null_timespec(write_time)) {
+                       update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
+               }
+       }
+
        TALLOC_FREE(lck);
 
        if (pinfo) {
@@ -3242,6 +3352,7 @@ NTSTATUS create_directory(connection_struct *conn, struct smb_request *req,
                FILE_DIRECTORY_FILE,                    /* create_options */
                FILE_ATTRIBUTE_DIRECTORY,               /* file_attributes */
                0,                                      /* oplock_request */
+               NULL,                                   /* lease */
                0,                                      /* allocation_size */
                0,                                      /* private_flags */
                NULL,                                   /* sd */
@@ -3420,6 +3531,7 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
                         0,                     /* create_options */
                         FILE_ATTRIBUTE_NORMAL, /* file_attributes */
                         0,                     /* oplock_request */
+                        NULL,                  /* lease */
                         0,                     /* allocation_size */
                         NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE, /* private_flags */
                         NULL,                  /* sd */
@@ -3770,6 +3882,25 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                if (SMB_VFS_STAT(conn, smb_fname_base) == -1) {
                        DEBUG(10, ("Unable to stat stream: %s\n",
                                   smb_fname_str_dbg(smb_fname_base)));
+               } else {
+                       /*
+                        * https://bugzilla.samba.org/show_bug.cgi?id=10229
+                        * We need to check if the requested access mask
+                        * could be used to open the underlying file (if
+                        * it existed), as we're passing in zero for the
+                        * access mask to the base filename.
+                        */
+                       status = check_base_file_access(conn,
+                                                       smb_fname_base,
+                                                       access_mask);
+
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(10, ("Permission check "
+                                       "for base %s failed: "
+                                       "%s\n", smb_fname->base_name,
+                                       nt_errstr(status)));
+                               goto fail;
+                       }
                }
 
                /* Open the base file. */
@@ -3983,6 +4114,17 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                }
        }
 
+       if ((conn->fs_capabilities & FILE_FILE_COMPRESSION)
+        && (create_options & FILE_NO_COMPRESSION)
+        && (info == FILE_WAS_CREATED)) {
+               status = SMB_VFS_SET_COMPRESSION(conn, fsp, fsp,
+                                                COMPRESSION_FORMAT_NONE);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("failed to disable compression: %s\n",
+                                 nt_errstr(status)));
+               }
+       }
+
        DEBUG(10, ("create_file_unixpath: info=%d\n", info));
 
        *result = fsp;
@@ -4144,6 +4286,7 @@ NTSTATUS create_file_default(connection_struct *conn,
                             uint32_t create_options,
                             uint32_t file_attributes,
                             uint32_t oplock_request,
+                            struct smb2_lease *lease,
                             uint64_t allocation_size,
                             uint32_t private_flags,
                             struct security_descriptor *sd,