]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbd: implement H-lease breaks on parent directory of rename target
authorRalph Boehme <slow@samba.org>
Thu, 22 May 2025 09:42:13 +0000 (11:42 +0200)
committerRalph Boehme <slow@samba.org>
Wed, 28 May 2025 15:06:29 +0000 (15:06 +0000)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15861

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Bjoern Jacke <bjacke@samba.org>
selftest/knownfail
source3/smbd/smb2_setinfo.c

index 51aae99d6b48fd9ded957238ca87bafddb6d3a55..3b02dcef2de6cc4e5a520fa215326dec2009e707 100644 (file)
 ^samba4.smb2.ioctl-on-stream.ioctl-on-stream\(ad_dc_ntvfs\)
 ^samba3.smb2.dir.one
 ^samba3.smb2.dir.modify
-^samba3.smb2.oplock.batch20
 ^samba3.smb2.oplock.stream1
 ^samba3.smb2.streams.rename
 ^samba3.smb2.streams.rename2
index 86cd0eac0cac16d178030d2e4706d0409a450a32..b6256907c9c2ff39b255ecc941e2fba449c05c9a 100644 (file)
@@ -175,10 +175,12 @@ struct smbd_smb2_setinfo_state {
        struct files_struct *fsp;
        struct share_mode_lock *lck;
        struct files_struct *dstfsp;
+       struct files_struct *dst_parent_dirfsp;
        uint16_t file_info_level;
        DATA_BLOB data;
        bool delay;
        bool rename_dst_check_done;
+       bool rename_dst_parent_check_done;
 };
 
 static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req);
@@ -394,6 +396,9 @@ static void smbd_smb2_setinfo_lease_break_fsp_check(struct tevent_req *req);
 static void smbd_smb2_setinfo_lease_break_fsp_done(struct tevent_req *subreq);
 static void smbd_smb2_setinfo_rename_dst_check(struct tevent_req *req);
 static void smbd_smb2_setinfo_rename_dst_delay_done(struct tevent_req *subreq);
+static void smbd_smb2_setinfo_rename_dst_parent_check(struct tevent_req *req);
+static void smbd_smb2_setinfo_rename_dst_parent_delay_done(
+       struct tevent_req *subreq);
 
 static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req)
 {
@@ -413,6 +418,16 @@ static void smbd_smb2_setinfo_lease_break_check(struct tevent_req *req)
                return;
        }
 
+       smbd_smb2_setinfo_rename_dst_parent_check(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return;
+       }
+       if (state->delay) {
+               DBG_DEBUG("Waiting for h-lease breaks on rename destination "
+                         "parent directory\n");
+               return;
+       }
+
        smbd_smb2_setinfo_lease_break_fsp_check(req);
        if (!tevent_req_is_in_progress(req)) {
                return;
@@ -767,6 +782,169 @@ static void smbd_smb2_setinfo_rename_dst_delay_done(struct tevent_req *subreq)
        smbd_smb2_setinfo_lease_break_check(req);
 }
 
+static void smbd_smb2_setinfo_rename_dst_parent_check(struct tevent_req *req)
+{
+       struct smbd_smb2_setinfo_state *state = tevent_req_data(
+               req, struct smbd_smb2_setinfo_state);
+       struct tevent_req *subreq = NULL;
+       struct timeval timeout;
+       bool overwrite = false;
+       struct files_struct *fsp = state->fsp;
+       struct files_struct *dst_parent_dirfsp = NULL;
+       struct smb_filename *smb_fname_dst = NULL;
+       char *dst_original_lcomp = NULL;
+       NTSTATUS status;
+
+       if (state->rename_dst_parent_check_done) {
+               return;
+       }
+       state->rename_dst_parent_check_done = true;
+
+       if (state->file_info_level != SMB2_FILE_RENAME_INFORMATION_INTERNAL) {
+               return;
+       }
+       if (is_named_stream(fsp->fsp_name)) {
+               return;
+       }
+       status = smb2_parse_file_rename_information(state,
+                                                   fsp->conn,
+                                                   state->smb2req->smb1req,
+                                                   (char *)state->data.data,
+                                                   state->data.length,
+                                                   fsp,
+                                                   fsp->fsp_name,
+                                                   &overwrite,
+                                                   &dst_parent_dirfsp,
+                                                   &smb_fname_dst,
+                                                   &dst_original_lcomp);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       SMB_ASSERT(dst_parent_dirfsp != NULL);
+       state->dst_parent_dirfsp = dst_parent_dirfsp;
+
+       state->lck = get_existing_share_mode_lock(state,
+                                                 dst_parent_dirfsp->file_id);
+       if (state->lck == NULL) {
+               /* No opens around */
+               return;
+       }
+
+       timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT, 0);
+       timeout = timeval_sum(&state->smb2req->request_time, &timeout);
+
+       subreq = delay_for_handle_lease_break_send(state,
+                                                  state->ev,
+                                                  timeout,
+                                                  dst_parent_dirfsp,
+                                                  SEC_STD_DELETE,
+                                                  false,
+                                                  &state->lck);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       if (tevent_req_is_in_progress(subreq)) {
+               state->delay = true;
+               tevent_req_set_callback(
+                       subreq,
+                       smbd_smb2_setinfo_rename_dst_parent_delay_done,
+                       req);
+               return;
+       }
+
+       status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (state->lck != NULL) {
+               bool delete_open;
+               bool detete_pending;
+               bool ok;
+
+               ok = has_delete_opens(dst_parent_dirfsp,
+                                     state->lck,
+                                     &delete_open,
+                                     &detete_pending);
+               TALLOC_FREE(state->lck);
+               if (!ok) {
+                       tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+                       return;
+               }
+
+               if (detete_pending) {
+                       tevent_req_nterror(req, NT_STATUS_DELETE_PENDING);
+                       return;
+               }
+               if (delete_open) {
+                       tevent_req_nterror(req, NT_STATUS_SHARING_VIOLATION);
+                       return;
+               }
+       }
+
+       return;
+}
+
+static void smbd_smb2_setinfo_rename_dst_parent_delay_done(
+       struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct smbd_smb2_setinfo_state *state = tevent_req_data(
+               req, struct smbd_smb2_setinfo_state);
+       struct smbXsrv_session *session = state->smb2req->session;
+       NTSTATUS status;
+       bool ok;
+
+       status = delay_for_handle_lease_break_recv(subreq, state, &state->lck);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       /*
+        * Make sure we run as the user again
+        */
+       ok = change_to_user_and_service(state->fsp->conn,
+                                       session->global->session_wire_id);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return;
+       }
+
+       if (state->lck != NULL) {
+               bool delete_open;
+               bool detete_pending;
+
+               ok = has_delete_opens(state->dst_parent_dirfsp,
+                                     state->lck,
+                                     &delete_open,
+                                     &detete_pending);
+               TALLOC_FREE(state->lck);
+               if (!ok) {
+                       tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+                       return;
+               }
+
+               if (detete_pending) {
+                       tevent_req_nterror(req, NT_STATUS_DELETE_PENDING);
+                       return;
+               }
+               if (delete_open) {
+                       tevent_req_nterror(req, NT_STATUS_SHARING_VIOLATION);
+                       return;
+               }
+       }
+
+       /*
+        * We've finished breaking H-lease on the rename destination, now
+        * trigger the fsp check.
+        */
+       state->rename_dst_parent_check_done = true;
+       smbd_smb2_setinfo_lease_break_check(req);
+}
+
 static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req)
 {
        NTSTATUS status;