smbd: Simplify open_file_ntcreate
[mat/samba.git] / source3 / smbd / open.c
index fc6a94c5e884311d4d6878199e1292c6ba610387..fa52fccc088595ef56beb612db3d0452a2acd9df 100644 (file)
@@ -76,6 +76,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
        struct security_descriptor *sd = NULL;
        uint32_t rejected_share_access;
        uint32_t rejected_mask = access_mask;
+       uint32_t do_not_check_mask = 0;
 
        rejected_share_access = access_mask & ~(conn->share_access);
 
@@ -143,10 +144,23 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
         * se_file_access_check() also takes care of
         * owner WRITE_DAC and READ_CONTROL.
         */
+       do_not_check_mask = FILE_READ_ATTRIBUTES;
+
+       /*
+        * Samba 3.6 and earlier granted execute access even
+        * if the ACL did not contain execute rights.
+        * Samba 4.0 is more correct and checks it.
+        * The compatibilty mode allows to skip this check
+        * to smoothen upgrades.
+        */
+       if (lp_acl_allow_execute_always(SNUM(conn))) {
+               do_not_check_mask |= FILE_EXECUTE;
+       }
+
        status = se_file_access_check(sd,
                                get_current_nttok(conn),
                                use_privs,
-                               (access_mask & ~FILE_READ_ATTRIBUTES),
+                               (access_mask & ~do_not_check_mask),
                                &rejected_mask);
 
        DEBUG(10,("smbd_check_access_rights: file %s requesting "
@@ -1038,14 +1052,6 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
                          "share entry with an open file\n");
        }
 
-       if ((share_entry->op_type == NO_OPLOCK) &&
-           (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK))
-       {
-               /* Someone has already written to it, but I haven't yet
-                * noticed */
-               return;
-       }
-
        if (((uint16)fsp->oplock_type) != share_entry->op_type) {
                goto panic;
        }
@@ -1079,6 +1085,26 @@ bool is_stat_open(uint32 access_mask)
                ((access_mask & ~stat_open_bits) == 0));
 }
 
+static bool has_delete_on_close(struct share_mode_lock *lck,
+                               uint32_t name_hash)
+{
+       struct share_mode_data *d = lck->data;
+       uint32_t i;
+
+       if (d->num_share_modes == 0) {
+               return false;
+       }
+       if (!is_delete_on_close_set(lck, name_hash)) {
+               return false;
+       }
+       for (i=0; i<d->num_share_modes; i++) {
+               if (!share_mode_stale_pid(d, i)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
 /****************************************************************************
  Deal with share modes
  Invarient: Share mode must be locked on entry and exit.
@@ -1087,11 +1113,8 @@ bool is_stat_open(uint32 access_mask)
 
 static NTSTATUS open_mode_check(connection_struct *conn,
                                struct share_mode_lock *lck,
-                               uint32_t name_hash,
                                uint32 access_mask,
-                               uint32 share_access,
-                               uint32 create_options,
-                               bool *file_existed)
+                               uint32 share_access)
 {
        int i;
 
@@ -1099,25 +1122,6 @@ static NTSTATUS open_mode_check(connection_struct *conn,
                return NT_STATUS_OK;
        }
 
-       /* A delete on close prohibits everything */
-
-       if (is_delete_on_close_set(lck, name_hash)) {
-               /*
-                * Check the delete on close token
-                * is valid. It could have been left
-                * after a server crash.
-                */
-               for(i = 0; i < lck->data->num_share_modes; i++) {
-                       if (!share_mode_stale_pid(lck->data, i)) {
-
-                               *file_existed = true;
-
-                               return NT_STATUS_DELETE_PENDING;
-                       }
-               }
-               return NT_STATUS_OK;
-       }
-
        if (is_stat_open(access_mask)) {
                /* Stat open that doesn't trigger oplock breaks or share mode
                 * checks... ! JRA. */
@@ -1151,16 +1155,10 @@ static NTSTATUS open_mode_check(connection_struct *conn,
                                continue;
                        }
 
-                       *file_existed = true;
-
                        return NT_STATUS_SHARING_VIOLATION;
                }
        }
 
-       if (lck->data->num_share_modes != 0) {
-               *file_existed = true;
-       }
-
        return NT_STATUS_OK;
 }
 
@@ -1186,8 +1184,7 @@ static NTSTATUS send_break_message(files_struct *fsp,
 
        status = messaging_send_buf(fsp->conn->sconn->msg_ctx, exclusive->pid,
                                    MSG_SMB_BREAK_REQUEST,
-                                   (uint8 *)msg,
-                                   MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+                                   (uint8 *)msg, sizeof(msg));
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(3, ("Could not send oplock break message: %s\n",
                          nt_errstr(status)));
@@ -1205,31 +1202,27 @@ static NTSTATUS send_break_message(files_struct *fsp,
  * Do internal consistency checks on the share mode for a file.
  */
 
-static void find_oplock_types(files_struct *fsp,
-                               int oplock_request,
-                               const struct share_mode_lock *lck,
-                               struct share_mode_entry **pp_batch,
-                               struct share_mode_entry **pp_ex_or_batch,
-                               bool *got_level2,
-                               bool *got_no_oplock)
+static bool validate_oplock_types(files_struct *fsp,
+                                 int oplock_request,
+                                 struct share_mode_lock *lck)
 {
-       int i;
-
-       *pp_batch = NULL;
-       *pp_ex_or_batch = NULL;
-       *got_level2 = false;
-       *got_no_oplock = false;
+       struct share_mode_data *d = lck->data;
+       bool batch = false;
+       bool ex_or_batch = false;
+       bool level2 = false;
+       bool no_oplock = false;
+       uint32_t i;
 
        /* Ignore stat or internal opens, as is done in
                delay_for_batch_oplocks() and
                delay_for_exclusive_oplocks().
         */
        if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
-               return;
+               return true;
        }
 
-       for (i=0; i<lck->data->num_share_modes; i++) {
-               struct share_mode_entry *e = &lck->data->share_modes[i];
+       for (i=0; i<d->num_share_modes; i++) {
+               struct share_mode_entry *e = &d->share_modes[i];
 
                if (!is_valid_share_mode_entry(e)) {
                        continue;
@@ -1244,67 +1237,94 @@ static void find_oplock_types(files_struct *fsp,
 
                if (BATCH_OPLOCK_TYPE(e->op_type)) {
                        /* batch - can only be one. */
-                       if (share_mode_stale_pid(lck->data, i)) {
+                       if (share_mode_stale_pid(d, i)) {
                                DEBUG(10, ("Found stale batch oplock\n"));
                                continue;
                        }
-                       if (*pp_ex_or_batch || *pp_batch || *got_level2 || *got_no_oplock) {
-                               smb_panic("Bad batch oplock entry.");
+                       if (ex_or_batch || batch || level2 || no_oplock) {
+                               DEBUG(0, ("Bad batch oplock entry %u.",
+                                         (unsigned)i));
+                               return false;
                        }
-                       *pp_batch = e;
+                       batch = true;
                }
 
                if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
-                       if (share_mode_stale_pid(lck->data, i)) {
+                       if (share_mode_stale_pid(d, i)) {
                                DEBUG(10, ("Found stale duplicate oplock\n"));
                                continue;
                        }
                        /* Exclusive or batch - can only be one. */
-                       if (*pp_ex_or_batch || *got_level2 || *got_no_oplock) {
-                               smb_panic("Bad exclusive or batch oplock entry.");
+                       if (ex_or_batch || level2 || no_oplock) {
+                               DEBUG(0, ("Bad exclusive or batch oplock "
+                                         "entry %u.", (unsigned)i));
+                               return false;
                        }
-                       *pp_ex_or_batch = e;
+                       ex_or_batch = true;
                }
 
                if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
-                       if (*pp_batch || *pp_ex_or_batch) {
-                               if (share_mode_stale_pid(lck->data, i)) {
+                       if (batch || ex_or_batch) {
+                               if (share_mode_stale_pid(d, i)) {
                                        DEBUG(10, ("Found stale LevelII "
                                                   "oplock\n"));
                                        continue;
                                }
-                               smb_panic("Bad levelII oplock entry.");
+                               DEBUG(0, ("Bad levelII oplock entry %u.",
+                                         (unsigned)i));
+                               return false;
                        }
-                       *got_level2 = true;
+                       level2 = true;
                }
 
                if (e->op_type == NO_OPLOCK) {
-                       if (*pp_batch || *pp_ex_or_batch) {
-                               if (share_mode_stale_pid(lck->data, i)) {
+                       if (batch || ex_or_batch) {
+                               if (share_mode_stale_pid(d, i)) {
                                        DEBUG(10, ("Found stale NO_OPLOCK "
                                                   "entry\n"));
                                        continue;
                                }
-                               smb_panic("Bad no oplock entry.");
+                               DEBUG(0, ("Bad no oplock entry %u.",
+                                         (unsigned)i));
+                               return false;
                        }
-                       *got_no_oplock = true;
+                       no_oplock = true;
                }
        }
+
+       remove_stale_share_mode_entries(d);
+
+       if ((batch || ex_or_batch) && (d->num_share_modes != 1)) {
+               DEBUG(1, ("got batch (%d) or ex (%d) non-exclusively (%d)\n",
+                         (int)batch, (int)ex_or_batch,
+                         (int)d->num_share_modes));
+               return false;
+       }
+
+       return true;
 }
 
-static bool delay_for_batch_oplocks(files_struct *fsp,
-                                       uint64_t mid,
-                                       int oplock_request,
-                                       struct share_mode_entry *batch_entry)
+static bool delay_for_oplock(files_struct *fsp,
+                            uint64_t mid,
+                            int oplock_request,
+                            struct share_mode_lock *lck,
+                            bool have_sharing_violation)
 {
+       struct share_mode_data *d = lck->data;
+       struct share_mode_entry *entry;
+
        if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
                return false;
        }
-       if (batch_entry == NULL) {
+       if (lck->data->num_share_modes != 1) {
+               /*
+                * More than one. There can't be any exclusive or batch left.
+                */
                return false;
        }
+       entry = &d->share_modes[0];
 
-       if (server_id_is_disconnected(&batch_entry->pid)) {
+       if (server_id_is_disconnected(&entry->pid)) {
                /*
                 * TODO: clean up.
                 * This could be achieved by sending a break message
@@ -1317,33 +1337,31 @@ static bool delay_for_batch_oplocks(files_struct *fsp,
                return false;
        }
 
-       /* Found a batch oplock */
-       send_break_message(fsp, batch_entry, mid, oplock_request);
-       return true;
-}
-
-static bool delay_for_exclusive_oplocks(files_struct *fsp,
-                                       uint64_t mid,
-                                       int oplock_request,
-                                       struct share_mode_entry *ex_entry)
-{
-       if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
-               return false;
+       if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
+               if (share_mode_stale_pid(d, 0)) {
+                       return false;
+               }
+               send_break_message(fsp, entry, mid, oplock_request);
+               return true;
        }
-       if (ex_entry == NULL) {
+       if (have_sharing_violation) {
+               /*
+                * Non-batch exclusive is not broken if we have a sharing
+                * violation
+                */
                return false;
        }
-
-       if (server_id_is_disconnected(&ex_entry->pid)) {
+       if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
                /*
-                * since only durable handles can get disconnected,
-                * and we can only get durable handles with batch oplocks,
-                * this should actually never be reached...
+                * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
                 */
                return false;
        }
+       if (share_mode_stale_pid(d, 0)) {
+               return false;
+       }
 
-       send_break_message(fsp, ex_entry, mid, oplock_request);
+       send_break_message(fsp, entry, mid, oplock_request);
        return true;
 }
 
@@ -1355,16 +1373,17 @@ static bool file_has_brlocks(files_struct *fsp)
        if (!br_lck)
                return false;
 
-       return br_lck->num_locks > 0 ? true : false;
+       return (brl_num_locks(br_lck) > 0);
 }
 
 static void grant_fsp_oplock_type(files_struct *fsp,
-                               int oplock_request,
-                               bool got_level2_oplock,
-                               bool got_a_none_oplock)
+                                 struct share_mode_lock *lck,
+                                 int oplock_request)
 {
        bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
                            lp_level2_oplocks(SNUM(fsp->conn));
+       bool got_level2_oplock, got_a_none_oplock;
+       uint32_t i;
 
        /* Start by granting what the client asked for,
           but ensure no SAMBA_PRIVATE bits can be set. */
@@ -1391,29 +1410,29 @@ static void grant_fsp_oplock_type(files_struct *fsp,
                return;
        }
 
+       got_level2_oplock = false;
+       got_a_none_oplock = false;
+
+       for (i=0; i<lck->data->num_share_modes; i++) {
+               int op_type = lck->data->share_modes[i].op_type;
+
+               if (LEVEL_II_OPLOCK_TYPE(op_type)) {
+                       got_level2_oplock = true;
+               }
+               if (op_type == NO_OPLOCK) {
+                       got_a_none_oplock = true;
+               }
+       }
+
        /*
         * Match what was requested (fsp->oplock_type) with
         * what was found in the existing share modes.
         */
 
-       if (got_a_none_oplock) {
-               fsp->oplock_type = NO_OPLOCK;
-       } else if (got_level2_oplock) {
-               if (fsp->oplock_type == NO_OPLOCK ||
-                               fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) {
-                       /* Store a level2 oplock, but don't tell the client */
-                       fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
-               } else {
+       if (got_level2_oplock || got_a_none_oplock) {
+               if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
                        fsp->oplock_type = LEVEL_II_OPLOCK;
                }
-       } else {
-               /* All share_mode_entries are placeholders or deferred.
-                * Silently upgrade to fake levelII if the client didn't
-                * ask for an oplock. */
-               if (fsp->oplock_type == NO_OPLOCK) {
-                       /* Store a level2 oplock, but don't tell the client */
-                       fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
-               }
        }
 
        /*
@@ -1421,7 +1440,20 @@ static void grant_fsp_oplock_type(files_struct *fsp,
         * or if we've turned them off.
         */
        if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
-               fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
+               fsp->oplock_type = NO_OPLOCK;
+       }
+
+       if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
+               /*
+                * We're the first level2 oplock. Indicate that in brlock.tdb.
+                */
+               struct byte_range_lock *brl;
+
+               brl = brl_get_locks(talloc_tos(), fsp);
+               if (brl != NULL) {
+                       brl_set_have_read_oplocks(brl, true);
+                       TALLOC_FREE(brl);
+               }
        }
 
        DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
@@ -1962,10 +1994,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        NTSTATUS status;
        char *parent_dir;
        SMB_STRUCT_STAT saved_stat = smb_fname->st;
-       struct share_mode_entry *batch_entry = NULL;
-       struct share_mode_entry *exclusive_entry = NULL;
-       bool got_level2_oplock = false;
-       bool got_a_none_oplock = false;
        struct timespec old_write_time;
        struct file_id id;
 
@@ -2306,12 +2334,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        return NT_STATUS_SHARING_VIOLATION;
                }
 
-               find_oplock_types(fsp, 0, lck, &batch_entry, &exclusive_entry,
-                                 &got_level2_oplock, &got_a_none_oplock);
+               if (!validate_oplock_types(fsp, 0, lck)) {
+                       smb_panic("validate_oplock_types failed");
+               }
 
-               if (delay_for_batch_oplocks(fsp, req->mid, 0, batch_entry) ||
-                   delay_for_exclusive_oplocks(fsp, req->mid, 0,
-                                               exclusive_entry)) {
+               if (delay_for_oplock(fsp, req->mid, 0, lck, false)) {
                        schedule_defer_open(lck, request_time, req);
                        TALLOC_FREE(lck);
                        DEBUG(10, ("Sent oplock break request to kernel "
@@ -2395,53 +2422,39 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        /* Get the types we need to examine. */
-       find_oplock_types(fsp,
-                         oplock_request,
-                         lck,
-                         &batch_entry,
-                         &exclusive_entry,
-                         &got_level2_oplock,
-                         &got_a_none_oplock);
-
-       /* First pass - send break only on batch oplocks. */
-       if ((req != NULL) &&
-           delay_for_batch_oplocks(fsp,
-                                   req->mid,
-                                   oplock_request,
-                                   batch_entry)) {
-               schedule_defer_open(lck, request_time, req);
+       if (!validate_oplock_types(fsp, oplock_request, lck)) {
+               smb_panic("validate_oplock_types failed");
+       }
+
+       if (has_delete_on_close(lck, fsp->name_hash)) {
                TALLOC_FREE(lck);
                fd_close(fsp);
-               return NT_STATUS_SHARING_VIOLATION;
+               return NT_STATUS_DELETE_PENDING;
        }
 
-       status = open_mode_check(conn, lck, fsp->name_hash,
-                                access_mask, share_access,
-                                create_options, &file_existed);
+       status = open_mode_check(conn, lck,
+                                access_mask, share_access);
 
-       if (NT_STATUS_IS_OK(status)) {
-               /* We might be going to allow this open. Check oplock
-                * status again. */
-               /* Second pass - send break for both batch or
-                * exclusive oplocks. */
-               if ((req != NULL) &&
-                   delay_for_exclusive_oplocks(
-                           fsp,
-                           req->mid,
-                           oplock_request,
-                           exclusive_entry)) {
-                       schedule_defer_open(lck, request_time, req);
-                       TALLOC_FREE(lck);
-                       fd_close(fsp);
-                       return NT_STATUS_SHARING_VIOLATION;
-               }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+           (lck->data->num_share_modes > 0)) {
+               /*
+                * This comes from ancient times out of open_mode_check. I
+                * have no clue whether this is still necessary. I can't think
+                * of a case where this would actually matter further down in
+                * this function. I leave it here for further investigation
+                * :-)
+                */
+               file_existed = true;
        }
 
-       if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
-               /* DELETE_PENDING is not deferred for a second */
+       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);
                TALLOC_FREE(lck);
                fd_close(fsp);
-               return status;
+               return NT_STATUS_SHARING_VIOLATION;
        }
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -2571,10 +2584,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                return status;
        }
 
-       grant_fsp_oplock_type(fsp,
-                             oplock_request,
-                             got_level2_oplock,
-                             got_a_none_oplock);
+       grant_fsp_oplock_type(fsp, lck, oplock_request);
 
        /*
         * We have the share entry *locked*.....
@@ -2663,15 +2673,18 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        status = set_file_oplock(fsp, fsp->oplock_type);
        if (!NT_STATUS_IS_OK(status)) {
                /*
-                * Could not get the kernel oplock or there are byte-range
-                * locks on the file.
+                * Could not get the kernel oplock
                 */
                fsp->oplock_type = NO_OPLOCK;
        }
 
-       set_share_mode(lck, fsp, get_current_uid(conn),
-                       req ? req->mid : 0,
-                      fsp->oplock_type);
+       if (!set_share_mode(lck, fsp, get_current_uid(conn),
+                           req ? req->mid : 0,
+                           fsp->oplock_type)) {
+               TALLOC_FREE(lck);
+               fd_close(fsp);
+               return NT_STATUS_NO_MEMORY;
+       }
 
        /* Handle strange delete on close create semantics. */
        if (create_options & FILE_DELETE_ON_CLOSE) {
@@ -3150,9 +3163,15 @@ static NTSTATUS open_directory(connection_struct *conn,
                return NT_STATUS_SHARING_VIOLATION;
        }
 
-       status = open_mode_check(conn, lck, fsp->name_hash,
-                               access_mask, share_access,
-                                create_options, &dir_existed);
+       if (has_delete_on_close(lck, fsp->name_hash)) {
+               TALLOC_FREE(lck);
+               fd_close(fsp);
+               file_free(req, fsp);
+               return NT_STATUS_DELETE_PENDING;
+       }
+
+       status = open_mode_check(conn, lck,
+                                access_mask, share_access);
 
        if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(lck);
@@ -3161,14 +3180,20 @@ static NTSTATUS open_directory(connection_struct *conn,
                return status;
        }
 
-       set_share_mode(lck, fsp, get_current_uid(conn),
-                       req ? req->mid : 0, NO_OPLOCK);
+       if (!set_share_mode(lck, fsp, get_current_uid(conn),
+                           req ? req->mid : 0, NO_OPLOCK)) {
+               TALLOC_FREE(lck);
+               fd_close(fsp);
+               file_free(req, fsp);
+               return NT_STATUS_NO_MEMORY;
+       }
 
        /* For directories the delete on close bit at open time seems
           always to be honored on close... See test 19 in Samba4 BASE-DELETE. */
        if (create_options & FILE_DELETE_ON_CLOSE) {
                status = can_set_delete_on_close(fsp, 0);
                if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_DIRECTORY_NOT_EMPTY)) {
+                       del_share_mode(lck, fsp);
                        TALLOC_FREE(lck);
                        fd_close(fsp);
                        file_free(req, fsp);