From: Rishabh Dave Date: Wed, 19 Nov 2025 17:12:35 +0000 (+0530) Subject: tests/libcephfs: add tests for snap metadata mutations X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=446bd6af1123884912ffbb743950c99c6b699267;p=ceph.git tests/libcephfs: add tests for snap metadata mutations Signed-off-by: Rishabh Dave --- diff --git a/src/test/libcephfs/multiclient.cc b/src/test/libcephfs/multiclient.cc index a7155853f57..371b82b7bb6 100644 --- a/src/test/libcephfs/multiclient.cc +++ b/src/test/libcephfs/multiclient.cc @@ -16,6 +16,7 @@ #include "gtest/gtest.h" #include "include/compat.h" #include "include/cephfs/libcephfs.h" +#include "include/ceph_fs.h" #include #include #include @@ -179,3 +180,68 @@ TEST(LibCephFS, MulticlientRevokeCaps) { thread1.join(); thread2.join(); } + + +// Test that client #2 can successfully read snap metadata mutation made by +// client #1. +TEST(LibCephFS, SnapMdMutate) { + struct ceph_mount_info *cmount, *cmount2; + + ASSERT_EQ(ceph_create(&cmount, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0); + ASSERT_EQ(ceph_mount(cmount, NULL), 0); + + ASSERT_EQ(ceph_create(&cmount2, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount2, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount2, NULL), 0); + ASSERT_EQ(ceph_mount(cmount2, NULL), 0); + + char dir_path[64]; + char snap_name[64]; + char snap_path[PATH_MAX]; + sprintf(dir_path, "/dir0_%d-5", getpid()); + sprintf(snap_name, "snap_%d_5", getpid()); + sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name); + + ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755)); + // snapshot with custom metadata + struct snap_metadata snap_meta[] = {{"foo", "bar"}, + {"this", "that"}, + {"abcde", "12345"}}; + ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, + std::size(snap_meta))); + + // actual test - + ASSERT_EQ(0, ceph_do_snap_md_op(cmount, snap_path, "foo", "bar123", + CEPH_SNAP_MD_OP_CREATE)); + + struct snap_info info; + ASSERT_EQ(0, ceph_get_snap_info(cmount2, snap_path, &info)); + ASSERT_GT(info.id, 1); + ASSERT_EQ(info.nr_snap_metadata, 3); + + // verify snap metadata + struct snap_metadata snap_meta2[] = {{"foo", "bar123"}, {"this", "that"}, + {"abcde", "12345"}}; + for (size_t i = 0; i < info.nr_snap_metadata; ++i) { + auto k = std::string(info.snap_metadata[i].key); + auto v = std::string(info.snap_metadata[i].value); + + bool found = false; + for (size_t j = 0; j < std::size(snap_meta2); ++j) { + if (k == snap_meta2[j].key and v == snap_meta2[j].value) { + found = true; + break; + } + } + + ASSERT_EQ(found, true); + } + + // teardown + ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name)); + ASSERT_EQ(0, ceph_rmdir(cmount, dir_path)); + ceph_shutdown(cmount); + ceph_shutdown(cmount2); +} diff --git a/src/test/libcephfs/test.cc b/src/test/libcephfs/test.cc index 7d3aed44a2b..2347cc45670 100644 --- a/src/test/libcephfs/test.cc +++ b/src/test/libcephfs/test.cc @@ -17,6 +17,7 @@ #include "include/filepath.h" #include "gtest/gtest.h" #include "include/cephfs/libcephfs.h" +#include "include/ceph_fs.h" #include "mds/mdstypes.h" #include "include/stat.h" #include @@ -2799,6 +2800,431 @@ TEST(LibCephFS, SnapInfo) { ceph_shutdown(cmount); } +// Test that ceph_do_snap_md_op() in create mode successfully adds new k-v +// pairs to snapshot metadata. +TEST(LibCephFS, SnapMdCreateAdds) { + struct ceph_mount_info *cmount; + ASSERT_EQ(ceph_create(&cmount, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0); + ASSERT_EQ(do_ceph_mount(cmount, NULL), 0); + + char dir_path[64]; + char snap_name[64]; + char snap_path[PATH_MAX]; + sprintf(dir_path, "/dir0_%d-2", getpid()); + sprintf(snap_name, "snap_%d_2", getpid()); + sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name); + + ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755)); + // snapshot with custom metadata + struct snap_metadata snap_meta[] = {{"foo", "bar"}, + {"this", "that"}, + {"abcde", "12345"}}; + ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, + std::size(snap_meta))); + + // actual test. + ASSERT_EQ(0, ceph_do_snap_md_op(cmount, snap_path, "foo2", "bar2", + CEPH_SNAP_MD_OP_CREATE)); + + // get latest snap metadata + struct snap_info info; + ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info)); + ASSERT_GT(info.id, 1); + ASSERT_EQ(info.nr_snap_metadata, 4); + + // verify snap metadata + struct snap_metadata snap_meta2[] = {{"foo", "bar"}, {"foo2", "bar2"}, + {"this", "that"}, {"abcde", "12345"}}; + for (size_t i = 0; i < info.nr_snap_metadata; ++i) { + auto k = std::string(info.snap_metadata[i].key); + auto v = std::string(info.snap_metadata[i].value); + + bool found = false; + for (size_t j = 0; j < std::size(snap_meta2); ++j) { + if (k == snap_meta2[j].key and v == snap_meta2[j].value) { + found = true; + break; + } + } + + ASSERT_EQ(found, true); + } + + // teardown + ceph_free_snap_info_buffer(&info); + ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name)); + ASSERT_EQ(0, ceph_rmdir(cmount, dir_path)); + ceph_shutdown(cmount); +} + + +// Test that ceph_do_snap_md_op() in create mode successfully updates new k-v +// pairs to snapshot metadata. +TEST(LibCephFS, SnapMdCreateUpdates) { + struct ceph_mount_info *cmount; + ASSERT_EQ(ceph_create(&cmount, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0); + ASSERT_EQ(do_ceph_mount(cmount, NULL), 0); + + char dir_path[64]; + char snap_name[64]; + char snap_path[PATH_MAX]; + sprintf(dir_path, "/dir0_%d-2", getpid()); + sprintf(snap_name, "snap_%d_2", getpid()); + sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name); + + ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755)); + // snapshot with custom metadata + struct snap_metadata snap_meta[] = {{"foo", "bar"}, + {"this", "that"}, + {"abcde", "12345"}}; + ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, + std::size(snap_meta))); + + // actual test. + ASSERT_EQ(0, ceph_do_snap_md_op(cmount, snap_path, "foo", "bar2", + CEPH_SNAP_MD_OP_CREATE)); + + // get latest snap metadata + struct snap_info info; + ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info)); + ASSERT_GT(info.id, 1); + ASSERT_EQ(info.nr_snap_metadata, 3); + + // verify snap metadata + struct snap_metadata snap_meta2[] = {{"foo", "bar2"}, {"this", "that"}, + {"abcde", "12345"}}; + for (size_t i = 0; i < info.nr_snap_metadata; ++i) { + auto k = std::string(info.snap_metadata[i].key); + auto v = std::string(info.snap_metadata[i].value); + + bool found = false; + for (size_t j = 0; j < std::size(snap_meta2); ++j) { + if (k == snap_meta2[j].key and v == snap_meta2[j].value) { + found = true; + break; + } + } + + ASSERT_EQ(found, true); + } + + // teardown + ceph_free_snap_info_buffer(&info); + ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name)); + ASSERT_EQ(0, ceph_rmdir(cmount, dir_path)); + ceph_shutdown(cmount); +} + + +// Test that k-v pair is added successfully by ceph_do_snap_md_op() in +// EXCL mode. +TEST(LibCephFS, SnapMdExclAdds) { + struct ceph_mount_info *cmount; + ASSERT_EQ(ceph_create(&cmount, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0); + ASSERT_EQ(do_ceph_mount(cmount, NULL), 0); + + char dir_path[64]; + char snap_name[64]; + char snap_path[PATH_MAX]; + sprintf(dir_path, "/dir0_%d-1", getpid()); + sprintf(snap_name, "snap_%d_1", getpid()); + sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name); + + ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755)); + // snapshot with custom metadata + struct snap_metadata snap_meta[] = {{"foo", "bar"}, {"this", "that"}, + {"abcde", "12345"}}; + ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, + std::size(snap_meta))); + + // actual test. + ASSERT_EQ(0, + ceph_do_snap_md_op(cmount, snap_path, "foo2", "bar2", + CEPH_SNAP_MD_OP_CREATE | CEPH_SNAP_MD_OP_EXCL)); + + // get latest snap metadata + struct snap_info info; + ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info)); + ASSERT_GT(info.id, 1); + ASSERT_EQ(info.nr_snap_metadata, 4); + + // verify snap metadata + struct snap_metadata snap_meta2[] = {{"foo", "bar"}, {"foo2", "bar2"}, + {"this", "that"}, {"abcde", "12345"}}; + for (size_t i = 0; i < info.nr_snap_metadata; ++i) { + auto k = std::string(info.snap_metadata[i].key); + auto v = std::string(info.snap_metadata[i].value); + + bool found = false; + for (size_t j = 0; j < std::size(snap_meta2); ++j) { + if (k == snap_meta2[j].key and v == snap_meta2[j].value) { + found = true; + break; + } + } + + ASSERT_EQ(found, true); + } + + // teardown + ceph_free_snap_info_buffer(&info); + ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name)); + ASSERT_EQ(0, ceph_rmdir(cmount, dir_path)); + ceph_shutdown(cmount); +} + + +// Test that k-v pair update is rejected successfully by ceph_do_snap_md_op() in +// EXCL mode. +TEST(LibCephFS, SnapMdExclRejectsUpdate) { + struct ceph_mount_info *cmount; + ASSERT_EQ(ceph_create(&cmount, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0); + ASSERT_EQ(do_ceph_mount(cmount, NULL), 0); + + char dir_path[64]; + char snap_name[64]; + char snap_path[PATH_MAX]; + sprintf(dir_path, "/dir0_%d-1", getpid()); + sprintf(snap_name, "snap_%d_1", getpid()); + sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name); + + ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755)); + // snapshot with custom metadata + struct snap_metadata snap_meta[] = {{"foo", "bar"}, {"this", "that"}, + {"abcde", "12345"}}; + ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, + std::size(snap_meta))); + + // actual test. + ASSERT_EQ(-EINVAL, + ceph_do_snap_md_op(cmount, snap_path, "foo", "bar2", + CEPH_SNAP_MD_OP_CREATE | CEPH_SNAP_MD_OP_EXCL)); + + // get latest snap metadata + struct snap_info info; + ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info)); + ASSERT_GT(info.id, 1); + ASSERT_EQ(info.nr_snap_metadata, 3); + + // verify snap metadata + for (size_t i = 0; i < info.nr_snap_metadata; ++i) { + auto k = std::string(info.snap_metadata[i].key); + auto v = std::string(info.snap_metadata[i].value); + + bool found = false; + for (size_t j = 0; j < std::size(snap_meta); ++j) { + if (k == snap_meta[j].key and v == snap_meta[j].value) { + found = true; + break; + } + } + + ASSERT_EQ(found, true); + } + + // teardown + ceph_free_snap_info_buffer(&info); + ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name)); + ASSERT_EQ(0, ceph_rmdir(cmount, dir_path)); + ceph_shutdown(cmount); +} + + +// Test that ceph_do_snap_md_op() in remove mode successfully removes k-v +// pairs from snapshot metadata. +TEST(LibCephFS, SnapMdRemove) { + struct ceph_mount_info *cmount; + ASSERT_EQ(ceph_create(&cmount, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0); + ASSERT_EQ(do_ceph_mount(cmount, NULL), 0); + + char dir_path[64]; + char snap_name[64]; + char snap_path[PATH_MAX]; + sprintf(dir_path, "/dir0_%d-3", getpid()); + sprintf(snap_name, "snap_%d_3", getpid()); + sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name); + + ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755)); + // snapshot with custom metadata + struct snap_metadata snap_meta[] = {{"foo", "bar"}, + {"this", "that"}, + {"abcde", "12345"}}; + ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, + std::size(snap_meta))); + + // actual test. + ASSERT_EQ(0, ceph_do_snap_md_op(cmount, snap_path, "foo", "bar", + CEPH_SNAP_MD_OP_REMOVE)); + + // get latest snap metadata + struct snap_info info; + ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info)); + ASSERT_GT(info.id, 1); + ASSERT_EQ(info.nr_snap_metadata, 2); + + // verify snap metadata + for (size_t i = 0; i < info.nr_snap_metadata; ++i) { + auto k = std::string(info.snap_metadata[i].key); + auto v = std::string(info.snap_metadata[i].value); + + assert(k != "foo"); + assert(v != "bar"); + + bool found = false; + for (size_t j = 1; j < std::size(snap_meta); ++j) { + if (k == snap_meta[j].key and v == snap_meta[j].value) { + found = true; + break; + } + } + + ASSERT_EQ(found, true); + } + + // teardown + ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name)); + ASSERT_EQ(0, ceph_rmdir(cmount, dir_path)); + ceph_shutdown(cmount); +} + +// Test all snap MD operations with empty strings. +TEST(LibCephFS, SnapMdOpEmptyStrings) { + struct ceph_mount_info *cmount; + ASSERT_EQ(ceph_create(&cmount, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0); + ASSERT_EQ(do_ceph_mount(cmount, NULL), 0); + + char dir_path[64]; + char snap_name[64]; + char snap_path[PATH_MAX]; + sprintf(dir_path, "/dir0_%d-4", getpid()); + sprintf(snap_name, "snap_%d_4", getpid()); + sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name); + + ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755)); + // snapshot with custom metadata + struct snap_metadata snap_meta[] = {{"foo", "bar"}, {"this", "that"}, + {"abcde", "12345"}}; + ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, + std::size(snap_meta))); + + // actual testing begins... + ASSERT_EQ(0, ceph_do_snap_md_op(cmount, snap_path, "", "", + CEPH_SNAP_MD_OP_CREATE)); + ASSERT_EQ(0, ceph_do_snap_md_op(cmount, snap_path, "", "1", + CEPH_SNAP_MD_OP_CREATE)); + + ASSERT_EQ(0, + ceph_do_snap_md_op(cmount, snap_path, "1", "", + CEPH_SNAP_MD_OP_CREATE | CEPH_SNAP_MD_OP_EXCL)); + + ASSERT_EQ(0, ceph_do_snap_md_op(cmount, snap_path, "", "", + CEPH_SNAP_MD_OP_REMOVE)); + ASSERT_EQ(0, ceph_do_snap_md_op(cmount, snap_path, "1", "", + CEPH_SNAP_MD_OP_REMOVE)); + + // get latest snap metadata + struct snap_info info; + ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info)); + ASSERT_GT(info.id, 1); + ASSERT_EQ(info.nr_snap_metadata, 3); + + // verify snap metadata + for (size_t i = 0; i < info.nr_snap_metadata; ++i) { + auto k = std::string(info.snap_metadata[i].key); + auto v = std::string(info.snap_metadata[i].value); + + bool found = false; + for (size_t j = 0; j < std::size(snap_meta); ++j) { + if (k == snap_meta[j].key and v == snap_meta[j].value) { + found = true; + break; + } + } + + ASSERT_EQ(found, true); + } + + // teardown + ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name)); + ASSERT_EQ(0, ceph_rmdir(cmount, dir_path)); + ceph_shutdown(cmount); +} + +// Test that ceph_do_snap_md_op() disallows inapproriate ops depending on +// mode. +TEST(LibCephFS, SnapMdNegTest) { + struct ceph_mount_info *cmount; + ASSERT_EQ(ceph_create(&cmount, NULL), 0); + ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0); + ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0); + ASSERT_EQ(do_ceph_mount(cmount, NULL), 0); + + char dir_path[64]; + char snap_name[64]; + char snap_path[PATH_MAX]; + sprintf(dir_path, "/dir0_%d-5", getpid()); + sprintf(snap_name, "snap_%d_5", getpid()); + sprintf(snap_path, "%s/.snap/%s", dir_path, snap_name); + + ASSERT_EQ(0, ceph_mkdir(cmount, dir_path, 0755)); + // snapshot with custom metadata + struct snap_metadata snap_meta[] = {{"foo", "bar"}, + {"this", "that"}, + {"abcde", "12345"}}; + ASSERT_EQ(0, ceph_mksnap(cmount, dir_path, snap_name, 0755, snap_meta, + std::size(snap_meta))); + + // actual test - + // attempt create in EXCL mode. + ASSERT_EQ(-EINVAL, + ceph_do_snap_md_op(cmount, snap_path, "foo", "bar2", + CEPH_SNAP_MD_OP_CREATE | CEPH_SNAP_MD_OP_EXCL)); + // attempt remove for an unexisting key. + ASSERT_EQ(-EINVAL, ceph_do_snap_md_op(cmount, snap_path, "foo2", "bar2", + CEPH_SNAP_MD_OP_REMOVE)); + // run ceph_do_snap_md_op() in invalid mode/with invalid op_flag. + ASSERT_EQ(-EINVAL, ceph_do_snap_md_op(cmount, snap_path, "foo123", "bar123", + 5)); + + struct snap_info info; + ASSERT_EQ(0, ceph_get_snap_info(cmount, snap_path, &info)); + ASSERT_GT(info.id, 1); + ASSERT_EQ(info.nr_snap_metadata, 3); + + // verify snap metadata + for (size_t i = 0; i < info.nr_snap_metadata; ++i) { + auto k = std::string(info.snap_metadata[i].key); + auto v = std::string(info.snap_metadata[i].value); + + bool found = false; + for (size_t j = 0; j < std::size(snap_meta); ++j) { + if (k == snap_meta[j].key and v == snap_meta[j].value) { + found = true; + break; + } + } + + ASSERT_EQ(found, true); + } + + // teardown + ASSERT_EQ(0, ceph_rmsnap(cmount, dir_path, snap_name)); + ASSERT_EQ(0, ceph_rmdir(cmount, dir_path)); + ceph_shutdown(cmount); +} + TEST(LibCephFS, LookupInoMDSDir) { struct ceph_mount_info *cmount; ASSERT_EQ(ceph_create(&cmount, NULL), 0);