]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbtorture: Directory Leases vs hardlink
authorRalph Boehme <slow@samba.org>
Mon, 21 Oct 2024 10:33:08 +0000 (12:33 +0200)
committerRalph Boehme <slow@samba.org>
Tue, 5 Nov 2024 14:39:31 +0000 (14:39 +0000)
Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
source4/torture/smb2/lease.c

index 118306f78efe79588be4a1d803aa2562826a2670..76f8d9cbac254c8edefd35311dfdc25ba9007fa8 100644 (file)
@@ -6769,6 +6769,258 @@ done:
        return ret;
 }
 
+static struct dlt_hardlink {
+       const char *testname;
+       bool expect_srcdir_break;
+       bool expect_dstdir_break;
+       const char *srcdir;
+       const char *dstdir;
+       uint64_t srcdir_leasekey;
+       uint64_t dstdir_leasekey;
+       uint64_t parent_leasekey;
+       const char *srcfname;
+       const char *dstfname;
+} dlt_hardlinks[] = {
+       {
+               .testname = "samedir-correct-parent-leaskey",
+               .expect_srcdir_break = false,
+               .expect_dstdir_break = false,
+               .srcdir = "test_dirlease_rename_dir",
+               .dstdir = "test_dirlease_rename_dir",
+               .srcdir_leasekey = DLEASE1,
+               .dstdir_leasekey = DLEASE2,
+               .parent_leasekey = DLEASE1,
+               .srcfname = "test_dirlease_rename_dir\\srcfile",
+               .dstfname = "test_dirlease_rename_dir\\dstfile",
+       }, {
+               .testname = "samedir-wrong-parent-leaskey",
+               .expect_srcdir_break = true,
+               .expect_dstdir_break = false,
+               .srcdir = "test_dirlease_rename_dir",
+               .dstdir = "test_dirlease_rename_dir",
+               .srcdir_leasekey = DLEASE1,
+               .dstdir_leasekey = DLEASE2,
+               .parent_leasekey = DLEASE3,
+               .srcfname = "test_dirlease_rename_dir\\srcfile",
+               .dstfname = "test_dirlease_rename_dir\\dstfile",
+       }, {
+               .testname = "samedir-no-parent-leaskey",
+               .expect_srcdir_break = true,
+               .expect_dstdir_break = false,
+               .srcdir = "test_dirlease_rename_dir",
+               .dstdir = "test_dirlease_rename_dir",
+               .srcdir_leasekey = DLEASE1,
+               .dstdir_leasekey = DLEASE2,
+               .parent_leasekey = 0,
+               .srcfname = "test_dirlease_rename_dir\\srcfile",
+               .dstfname = "test_dirlease_rename_dir\\dstfile",
+       }, {
+               .testname = "otherdir-correct-srcparent-leaskey",
+               .expect_srcdir_break = false,
+               .expect_dstdir_break = true,
+               .srcdir = "test_dirlease_rename_dir",
+               .dstdir = "test_dirlease_rename_dir2",
+               .srcdir_leasekey = DLEASE1,
+               .dstdir_leasekey = DLEASE2,
+               .parent_leasekey = DLEASE1,
+               .srcfname = "test_dirlease_rename_dir\\srcfile",
+               .dstfname = "test_dirlease_rename_dir2\\dstfile",
+       }, {
+               .testname = "otherdir-correct-dstparent-leaskey",
+               .expect_srcdir_break = false,
+               .expect_dstdir_break = false,
+               .srcdir = "test_dirlease_rename_dir",
+               .dstdir = "test_dirlease_rename_dir2",
+               .srcdir_leasekey = DLEASE1,
+               .dstdir_leasekey = DLEASE2,
+               .parent_leasekey = DLEASE2,
+               .srcfname = "test_dirlease_rename_dir\\srcfile",
+               .dstfname = "test_dirlease_rename_dir2\\dstfile",
+       }, {
+               .testname = "otherdir-wrong-parent-leaskey",
+               .expect_srcdir_break = false,
+               .expect_dstdir_break = true,
+               .srcdir = "test_dirlease_rename_dir",
+               .dstdir = "test_dirlease_rename_dir2",
+               .srcdir_leasekey = DLEASE1,
+               .dstdir_leasekey = DLEASE2,
+               .parent_leasekey = DLEASE3,
+               .srcfname = "test_dirlease_rename_dir\\srcfile",
+               .dstfname = "test_dirlease_rename_dir2\\dstfile",
+       }, {
+               .testname = "otherdir-no-parent-leaskey",
+               .expect_srcdir_break = false,
+               .expect_dstdir_break = true,
+               .srcdir = "test_dirlease_rename_dir",
+               .dstdir = "test_dirlease_rename_dir2",
+               .srcdir_leasekey = DLEASE1,
+               .dstdir_leasekey = DLEASE2,
+               .parent_leasekey = 0,
+               .srcfname = "test_dirlease_rename_dir\\srcfile",
+               .dstfname = "test_dirlease_rename_dir2\\dstfile",
+       }, {
+               .testname = NULL,
+       }
+};
+
+static bool test_hardlink_one(struct torture_context *tctx,
+                           struct smb2_tree *tree,
+                           struct dlt_hardlink *t)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       struct smb2_create c;
+       struct smb2_lease dirlease1;
+       struct smb2_lease dirlease2;
+       struct smb2_handle dirh1 = {};
+       struct smb2_handle dirh2 = {};
+       struct smb2_lease lease1;
+       struct smb2_handle h1 = {};
+       struct smb2_lease_break_ack ack = {};
+       bool samedir = strequal(t->srcdir, t->dstdir);
+       union smb_setfileinfo sfinfo = {};
+       NTSTATUS status;
+       bool ret = true;
+
+       torture_comment(tctx, "\nHardlink subtest: %s\n"
+                       "==================================\n",
+                       t->testname);
+
+       smb2_deltree(tree, t->srcdir);
+       smb2_deltree(tree, t->dstdir);
+       torture_reset_lease_break_info(tctx, &lease_break_info);
+
+
+       /* Get an RH directory lease on the src directory */
+       smb2_lease_v2_create_share(&c, &dirlease1, true, t->srcdir,
+                                  smb2_util_share_access("RWD"),
+                                  t->srcdir_leasekey, NULL,
+                                  smb2_util_lease_state("RHW"), 0);
+       c.in.desired_access &= ~DELETE_ACCESS;
+       status = smb2_create(tree, mem_ctx, &c);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
+       dirh1 = c.out.file.handle;
+       CHECK_LEASE_V2(&c, "RH", true, t->srcdir_leasekey, 0, 0, ++dirlease1.lease_epoch);
+
+       if (!samedir) {
+               /* Get an RH directory lease on the dst directory */
+               smb2_lease_v2_create_share(&c, &dirlease2, true, t->dstdir,
+                                          smb2_util_share_access("RWD"),
+                                          t->dstdir_leasekey, NULL,
+                                          smb2_util_lease_state("RHW"), 0);
+               c.in.desired_access &= ~DELETE_ACCESS;
+               status = smb2_create(tree, mem_ctx, &c);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_create failed\n");
+               dirh2 = c.out.file.handle;
+               CHECK_LEASE_V2(&c, "RH", true, t->dstdir_leasekey, 0, 0, ++dirlease2.lease_epoch);
+       }
+
+       /* Create the to be hardlinkd file */
+       smb2_lease_v2_create_share(&c, &lease1, false, t->srcfname,
+                                  smb2_util_share_access("RWD"),
+                                  LEASE4, &t->srcdir_leasekey,
+                                  smb2_util_lease_state("RHW"),
+                                  0x33);
+       status = smb2_create(tree, mem_ctx, &c);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
+       h1 = c.out.file.handle;
+       smb2_util_close(tree, h1);
+       ZERO_STRUCT(h1);
+
+       /* Open the testfile, possibly with a bad parent leasekey */
+       smb2_lease_v2_create_share(&c, &lease1, false, t->srcfname,
+                                  smb2_util_share_access("RWD"),
+                                  LEASE4,
+                                  t->parent_leasekey != 0 ? &t->parent_leasekey : NULL,
+                                  smb2_util_lease_state("RHW"),
+                                  0x33);
+       status = smb2_create(tree, mem_ctx, &c);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_create failed\n");
+       h1 = c.out.file.handle;
+
+       sfinfo.generic.level = RAW_SFILEINFO_LINK_INFORMATION;
+       sfinfo.generic.in.file.handle = h1;
+       sfinfo.link_information.in.new_name = t->dstfname;
+
+       status = smb2_setinfo_file(tree, &sfinfo);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                       "smb2_setinfo_file failed\n");
+       if (t->expect_srcdir_break) {
+               /* Delay the ack in order to be able to possibly process two breaks */
+               lease_break_info.lease_skip_ack = true;
+               CHECK_BREAK_INFO_V2(tree->session->transport,
+                                   "RH", "",
+                                   t->srcdir_leasekey,
+                                   ++dirlease1.lease_epoch);
+               ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
+               ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
+               torture_reset_lease_break_info(tctx, &lease_break_info);
+               if (!t->expect_dstdir_break) {
+                       CHECK_NO_BREAK(tctx);
+               }
+       }
+       if (t->expect_dstdir_break) {
+               CHECK_BREAK_INFO_V2(tree->session->transport,
+                                   "RH", "",
+                                   t->dstdir_leasekey,
+                                   ++dirlease2.lease_epoch);
+               torture_reset_lease_break_info(tctx, &lease_break_info);
+               if (!t->expect_srcdir_break) {
+                       CHECK_NO_BREAK(tctx);
+               }
+       }
+       if (!t->expect_srcdir_break && !t->expect_dstdir_break) {
+               CHECK_NO_BREAK(tctx);
+       }
+
+       if (t->expect_srcdir_break) {
+               /* ack the first lease break. */
+               status = smb2_lease_break_ack(tree, &ack);
+               torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                                               "smb2_lease_break_ack failed\n");
+               CHECK_LEASE_BREAK_ACK(&ack, "", t->srcdir_leasekey);
+       }
+
+
+done:
+       if (!smb2_util_handle_empty(h1)) {
+               smb2_util_close(tree, h1);
+       }
+       if (!smb2_util_handle_empty(dirh1)) {
+               smb2_util_close(tree, dirh1);
+       }
+       if (!smb2_util_handle_empty(dirh2)) {
+               smb2_util_close(tree, dirh2);
+       }
+       smb2_deltree(tree, t->srcdir);
+       smb2_deltree(tree, t->dstdir);
+       return ret;
+}
+
+static bool test_hardlink(struct torture_context *tctx,
+                       struct smb2_tree *tree)
+{
+       struct dlt_hardlink *t = NULL;
+       bool ret = true;
+
+       tree->session->transport->lease.handler = torture_lease_handler;
+       tree->session->transport->lease.private_data = tree;
+       torture_reset_lease_break_info(tctx, &lease_break_info);
+
+       for (t = dlt_hardlinks; t->testname != NULL; t++) {
+               ret = test_hardlink_one(tctx, tree, t);
+               torture_assert_goto(tctx, ret, ret, done,
+                                   talloc_asprintf(tctx, "%s failed\n",
+                                                   t->testname));
+       }
+
+done:
+       return ret;
+}
+
 struct torture_suite *torture_smb2_dirlease_init(TALLOC_CTX *ctx)
 {
        struct torture_suite *suite =
@@ -6787,5 +7039,6 @@ struct torture_suite *torture_smb2_dirlease_init(TALLOC_CTX *ctx)
        torture_suite_add_2smb2_test(suite, "setatime", test_dirlease_setatime);
        torture_suite_add_1smb2_test(suite, "rename", test_rename);
        torture_suite_add_2smb2_test(suite, "overwrite", test_overwrite);
+       torture_suite_add_1smb2_test(suite, "hardlink", test_hardlink);
        return suite;
 }