]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librgw_file_xattr.cc
import ceph quincy 17.2.4
[ceph.git] / ceph / src / test / librgw_file_xattr.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2015 Red Hat, Inc.
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include <stdint.h>
16 #include <tuple>
17 #include <iostream>
18 #include <vector>
19 #include <map>
20 #include <random>
21 #include <boost/algorithm/string.hpp>
22 #include "xxhash.h"
23
24 #include "include/rados/librgw.h"
25 #include "include/rados/rgw_file.h"
26 #include "rgw/rgw_file.h"
27
28 #include "gtest/gtest.h"
29 #include "common/ceph_argparse.h"
30 #include "common/errno.h"
31 #include "common/debug.h"
32 #include "global/global_init.h"
33 #include "include/ceph_assert.h"
34
35 #define dout_context g_ceph_context
36 #define dout_subsys ceph_subsys_rgw
37
38 using namespace std;
39
40 namespace {
41
42 using namespace rgw;
43
44 string uid("testuser");
45 string access_key("");
46 string secret_key("");
47
48 librgw_t rgw_h = nullptr;
49 struct rgw_fs *fs = nullptr;
50
51 uint32_t owner_uid = 867;
52 uint32_t owner_gid = 5309;
53 uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
54
55 string bucket_name{"v4recov"};
56 string object_path{"node0/clientids"};
57
58 string key1{"black"};
59 string val1{"metallic"};
60
61 struct rgw_file_handle *bucket_fh = nullptr;
62 struct rgw_file_handle *object_fh = nullptr;
63
64 typedef std::tuple<string,uint64_t, struct rgw_file_handle*> fid_type;
65 std::vector<fid_type> fids;
66
67 class obj_rec
68 {
69 public:
70 string name;
71 struct rgw_file_handle* fh;
72 struct rgw_file_handle* parent_fh;
73 RGWFileHandle* rgw_fh; // alias into fh
74
75 struct state {
76 bool readdir;
77 state() : readdir(false) {}
78 } state;
79
80 obj_rec(string _name, struct rgw_file_handle* _fh,
81 struct rgw_file_handle* _parent_fh, RGWFileHandle* _rgw_fh)
82 : name(std::move(_name)), fh(_fh), parent_fh(_parent_fh),
83 rgw_fh(_rgw_fh) {}
84
85 void clear() {
86 fh = nullptr;
87 rgw_fh = nullptr;
88 }
89
90 void sync() {
91 if (fh)
92 rgw_fh = get_rgwfh(fh);
93 }
94
95 friend ostream& operator<<(ostream& os, const obj_rec& rec);
96 };
97
98 [[maybe_unused]] ostream& operator<<(ostream& os, const obj_rec& rec)
99 {
100 RGWFileHandle* rgw_fh = rec.rgw_fh;
101 if (rgw_fh) {
102 const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
103 os << rec.rgw_fh->full_object_name()
104 << " (" << rec.rgw_fh->object_name() << "): "
105 << type;
106 }
107 return os;
108 }
109
110 typedef std::vector<obj_rec> obj_vec;
111 obj_vec ovec;
112
113 bool do_stat = false;
114 bool do_create = false;
115 bool do_delete = false;
116 bool do_hexdump = false;
117 bool verbose = false;
118
119 struct {
120 int argc;
121 char **argv;
122 } saved_args;
123 }
124
125 TEST(LibRGW, INIT) {
126 int ret = librgw_create(&rgw_h, saved_args.argc, saved_args.argv);
127 ASSERT_EQ(ret, 0);
128 ASSERT_NE(rgw_h, nullptr);
129 }
130
131 TEST(LibRGW, MOUNT) {
132 int ret = rgw_mount(rgw_h, uid.c_str(), access_key.c_str(),
133 secret_key.c_str(), &fs, RGW_MOUNT_FLAG_NONE);
134 ASSERT_EQ(ret, 0);
135 ASSERT_NE(fs, nullptr);
136 }
137
138 TEST(LibRGW, LOOKUP_BUCKET) {
139 int ret = rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
140 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
141 ASSERT_EQ(ret, 0);
142 }
143
144 TEST(LibRGW, CREATE_BUCKET) {
145 if ((! bucket_fh) && do_create) {
146 struct stat st;
147
148 st.st_uid = owner_uid;
149 st.st_gid = owner_gid;
150 st.st_mode = 755;
151
152 int ret = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st, create_mask,
153 &bucket_fh, RGW_MKDIR_FLAG_NONE);
154 ASSERT_EQ(ret, 0);
155 }
156 }
157
158 TEST(LibRGW, CREATE_PATH) {
159
160 if (!bucket_fh)
161 return;
162
163 vector<string> segs;
164 boost::split(segs, object_path, boost::is_any_of("/"));
165
166 struct stat st;
167 st.st_uid = owner_uid;
168 st.st_gid = owner_gid;
169 st.st_mode = 755;
170
171 int ix, ret, sz = segs.size();
172 for (ix = 0; ix < sz; ++ix) {
173 auto& seg = segs[ix];
174 struct rgw_file_handle* parent_fh = (ix > 0) ? ovec[ix-1].fh : bucket_fh;
175 obj_rec dir{seg, nullptr, parent_fh, nullptr};
176 if (do_create) {
177 ret = rgw_mkdir(fs, dir.parent_fh, dir.name.c_str(), &st, create_mask,
178 &dir.fh, RGW_MKDIR_FLAG_NONE);
179 } else {
180 ret = rgw_lookup(fs, dir.parent_fh, dir.name.c_str(), &dir.fh,
181 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
182 }
183 ASSERT_EQ(ret, 0);
184 dir.sync();
185 ovec.push_back(dir);
186 if (verbose) {
187 std::cout << "create: " << dir.name << std::endl;
188 }
189 }
190 }
191
192 TEST(LibRGW, CHECK_PATH_REFS) {
193
194 if (!bucket_fh)
195 return;
196
197 int ix, sz = ovec.size();
198 for (ix = 0; ix < sz; ++ix) {
199 auto& dir = ovec[ix];
200 if (verbose) {
201 std::cout << "name: " << dir.name
202 << " refcnt: " << dir.rgw_fh->get_refcnt()
203 << std::endl;
204 }
205 if (ix == 0) {
206 // sentinel, +1 parent, +1 path
207 ASSERT_EQ(dir.rgw_fh->get_refcnt(), 3U);
208 }
209 if (ix == 1) {
210 // sentinel, +1 path
211 ASSERT_EQ(dir.rgw_fh->get_refcnt(), 2U);
212 }
213 }
214 }
215
216 TEST(LibRGW, SETXATTR1) {
217
218 if (!bucket_fh)
219 return;
220
221 auto& dir = ovec[ovec.size()-1];
222 rgw_xattrstr xattr_k = { const_cast<char*>(key1.c_str()),
223 uint32_t(key1.length()) };
224 rgw_xattrstr xattr_v = { const_cast<char*>(val1.c_str()),
225 uint32_t(val1.length()) };
226
227 rgw_xattr xattr = { xattr_k, xattr_v };
228 rgw_xattrlist xattrlist = { &xattr, 1 };
229
230 int ret = rgw_setxattrs(fs, dir.fh, &xattrlist, RGW_SETXATTR_FLAG_NONE);
231 ASSERT_EQ(ret, 0);
232 }
233
234 extern "C" {
235 static int getattr_cb(rgw_xattrlist *attrs, void *arg, uint32_t flags)
236 {
237 auto& attrmap =
238 *(static_cast<std::map<std::string, std::string>*>(arg));
239 for (uint32_t ix = 0; ix < attrs->xattr_cnt; ++ix) {
240 auto& xattr = attrs->xattrs[ix];
241 string k{xattr.key.val, xattr.key.len};
242 string v{xattr.val.val, xattr.val.len};
243 if (verbose) {
244 std::cout << __func__
245 << " attr k: " << k << " v: " << v
246 << std::endl;
247 }
248 attrmap.insert(std::map<std::string, std::string>::value_type(k, v));
249 }
250 return 0;
251 }
252 }
253
254 TEST(LibRGW, GETXATTR1) {
255
256 if (!bucket_fh)
257 return;
258
259 using std::get;
260 auto& dir = ovec[ovec.size()-1];
261
262 rgw_xattrstr xattr_k1 =
263 {const_cast<char*>(key1.c_str()), uint32_t(key1.length())};
264 rgw_xattrstr xattr_v1 = {nullptr, 0};
265
266 std::string key2 = "user.rgw.etag";
267 rgw_xattrstr xattr_k2 =
268 {const_cast<char*>(key2.c_str()), uint32_t(key2.length())};
269 rgw_xattrstr xattr_v2 = {nullptr, 0};
270
271 rgw_xattr xattrs[2] = {{xattr_k1, xattr_v1},
272 {xattr_k2, xattr_v2}};
273
274 /* XXX gcc won't accept static_cast here, don't see why */
275 rgw_xattrlist xattrlist = {reinterpret_cast<rgw_xattr*>(&xattrs), 2};
276
277 std::map<std::string, std::string> out_attrmap;
278
279 int ret = rgw_getxattrs(fs, dir.fh, &xattrlist, getattr_cb, &out_attrmap,
280 RGW_GETXATTR_FLAG_NONE);
281 ASSERT_EQ(ret, 0);
282 /* check exposed attrs */
283 ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
284 /* check our user attr */
285 ASSERT_TRUE(out_attrmap.find(key1) != out_attrmap.end());
286 }
287
288 TEST(LibRGW, LSXATTR1) {
289
290 if (!bucket_fh)
291 return;
292
293 using std::get;
294 auto& dir = ovec[ovec.size()-1];
295
296 rgw_xattrstr filter_prefix = { nullptr, 0}; // XXX ignored, for now
297
298 std::map<std::string, std::string> out_attrmap;
299
300 int ret = rgw_lsxattrs(fs, dir.fh, &filter_prefix, getattr_cb,
301 &out_attrmap, RGW_LSXATTR_FLAG_NONE);
302 ASSERT_EQ(ret, 0);
303
304 /* check exposed attrs */
305 ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
306 }
307
308 TEST(LibRGW, RMXATTR1) {
309
310 if (!bucket_fh)
311 return;
312
313 using std::get;
314 auto& dir = ovec[ovec.size()-1];
315
316 rgw_xattrstr xattr_k = { const_cast<char*>(key1.c_str()),
317 uint32_t(key1.length()) };
318 rgw_xattrstr xattr_v = { nullptr, 0 };
319
320 rgw_xattr xattr = { xattr_k, xattr_v };
321 rgw_xattrlist xattrlist = { &xattr, 1 };
322
323 int ret = rgw_rmxattrs(fs, dir.fh, &xattrlist, RGW_RMXATTR_FLAG_NONE);
324 ASSERT_EQ(ret, 0);
325 }
326
327 TEST(LibRGW, LSXATTR2) {
328
329 if (!bucket_fh)
330 return;
331
332 using std::get;
333 auto& dir = ovec[ovec.size()-1];
334
335 rgw_xattrstr filter_prefix = { nullptr, 0 }; // XXX ignored, for now
336
337 std::map<std::string, std::string> out_attrmap;
338
339 int ret = rgw_lsxattrs(fs, dir.fh, &filter_prefix, getattr_cb,
340 &out_attrmap, RGW_LSXATTR_FLAG_NONE);
341 ASSERT_EQ(ret, 0);
342 /* check exposed attrs */
343 ASSERT_TRUE(out_attrmap.find("user.rgw.etag") != out_attrmap.end());
344 /* verify deletion */
345 ASSERT_TRUE(out_attrmap.find(key1) == out_attrmap.end());
346 }
347
348 TEST(LibRGW, CLEANUP) {
349 int ret = 0;
350 if (object_fh) {
351 ret = rgw_fh_rele(fs, object_fh, 0 /* flags */);
352 ASSERT_EQ(ret, 0);
353 }
354 if (bucket_fh) {
355 ret = rgw_fh_rele(fs, bucket_fh, 0 /* flags */);
356 }
357 ASSERT_EQ(ret, 0);
358 }
359
360 TEST(LibRGW, UMOUNT) {
361 if (! fs)
362 return;
363
364 int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
365 ASSERT_EQ(ret, 0);
366 }
367
368 TEST(LibRGW, SHUTDOWN) {
369 librgw_shutdown(rgw_h);
370 }
371
372 int main(int argc, char *argv[])
373 {
374 auto args = argv_to_vec(argc, argv);
375 env_to_vec(args);
376
377 char* v = getenv("AWS_ACCESS_KEY_ID");
378 if (v) {
379 access_key = v;
380 }
381
382 v = getenv("AWS_SECRET_ACCESS_KEY");
383 if (v) {
384 secret_key = v;
385 }
386
387 string val;
388 for (auto arg_iter = args.begin(); arg_iter != args.end();) {
389 if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
390 (char*) nullptr)) {
391 access_key = val;
392 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
393 (char*) nullptr)) {
394 secret_key = val;
395 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
396 (char*) nullptr)) {
397 uid = val;
398 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
399 (char*) nullptr)) {
400 bucket_name = val;
401 } else if (ceph_argparse_flag(args, arg_iter, "--stat",
402 (char*) nullptr)) {
403 do_stat = true;
404 } else if (ceph_argparse_flag(args, arg_iter, "--create",
405 (char*) nullptr)) {
406 do_create = true;
407 } else if (ceph_argparse_flag(args, arg_iter, "--delete",
408 (char*) nullptr)) {
409 do_delete = true;
410 } else if (ceph_argparse_flag(args, arg_iter, "--hexdump",
411 (char*) nullptr)) {
412 do_hexdump = true;
413 } else if (ceph_argparse_flag(args, arg_iter, "--verbose",
414 (char*) nullptr)) {
415 verbose = true;
416 } else {
417 ++arg_iter;
418 }
419 }
420
421 /* dont accidentally run as anonymous */
422 if ((access_key == "") ||
423 (secret_key == "")) {
424 std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
425 return EPERM;
426 }
427
428 saved_args.argc = argc;
429 saved_args.argv = argv;
430
431 ::testing::InitGoogleTest(&argc, argv);
432 return RUN_ALL_TESTS();
433 }