#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 <errno.h>
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);