]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librgw_file_nfsns.cc
1731491ba720226d2bf28c684e508100b004797b
[ceph.git] / ceph / src / test / librgw_file_nfsns.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 <fstream>
19 #include <stack>
20
21 #include "include/rados/librgw.h"
22 #include "include/rados/rgw_file.h"
23 #include "rgw/rgw_file.h"
24 #include "rgw/rgw_lib_frontend.h" // direct requests
25
26 #include "gtest/gtest.h"
27 #include "common/ceph_argparse.h"
28 #include "common/debug.h"
29 #include "global/global_init.h"
30 #include "include/assert.h"
31
32 #define dout_subsys ceph_subsys_rgw
33
34 namespace {
35
36 using namespace rgw;
37 using std::get;
38 using std::string;
39
40 librgw_t rgw_h = nullptr;
41 string userid("testuser");
42 string access_key("");
43 string secret_key("");
44 struct rgw_fs *fs = nullptr;
45 CephContext* cct = nullptr;
46
47 uint32_t owner_uid = 867;
48 uint32_t owner_gid = 5309;
49
50 uint32_t magic_uid = 1701;
51 uint32_t magic_gid = 9876;
52
53 uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
54
55 string bucket_name("nfsroot");
56 string dirs1_bucket_name("bdirs1");
57 string readf_name("toyland");
58 string readf_out_name("rgwlib_readf.out");
59 std::string writef_name{"bigbird"};
60
61 int n_dirs1_dirs = 3;
62 int n_dirs1_objs = 2;
63
64 class obj_rec
65 {
66 public:
67 string name;
68 struct rgw_file_handle* fh;
69 struct rgw_file_handle* parent_fh;
70 RGWFileHandle* rgw_fh; // alias into fh
71
72 struct state {
73 bool readdir;
74 state() : readdir(false) {}
75 } state;
76
77 obj_rec(string _name, struct rgw_file_handle* _fh,
78 struct rgw_file_handle* _parent_fh, RGWFileHandle* _rgw_fh)
79 : name(std::move(_name)), fh(_fh), parent_fh(_parent_fh),
80 rgw_fh(_rgw_fh) {}
81
82 void clear() {
83 fh = nullptr;
84 rgw_fh = nullptr;
85 }
86
87 void sync() {
88 if (fh)
89 rgw_fh = get_rgwfh(fh);
90 }
91
92 friend ostream& operator<<(ostream& os, const obj_rec& rec);
93 };
94
95 ostream& operator<<(ostream& os, const obj_rec& rec)
96 {
97 RGWFileHandle* rgw_fh = rec.rgw_fh;
98 if (rgw_fh) {
99 const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
100 os << rec.rgw_fh->full_object_name()
101 << " (" << rec.rgw_fh->object_name() << "): "
102 << type;
103 }
104 return os;
105 }
106
107 std::stack<obj_rec> obj_stack;
108 std::deque<obj_rec> cleanup_queue;
109
110 typedef std::vector<obj_rec> obj_vec;
111 typedef std::tuple<obj_rec, obj_vec> dirs1_rec;
112 typedef std::vector<dirs1_rec> dirs1_vec;
113
114 dirs1_vec dirs_vec;
115
116 struct obj_rec_st
117 {
118 const obj_rec& obj;
119 const struct stat& st;
120
121 obj_rec_st(const obj_rec& _obj, const struct stat& _st)
122 : obj(_obj), st(_st) {}
123 };
124
125 ostream& operator<<(ostream& os, const obj_rec_st& rec)
126 {
127 RGWFileHandle* rgw_fh = rec.obj.rgw_fh;
128 if (rgw_fh) {
129 const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
130 os << rgw_fh->full_object_name()
131 << " (" << rgw_fh->object_name() << "): "
132 << type;
133 const struct stat& st = rec.st;
134 switch(uint8_t(rgw_fh->is_dir())) {
135 case 1:
136 os << " mode: " << st.st_mode;
137 os << " nlinks: " << st.st_nlink;
138 break;
139 case 0:
140 default:
141 os << " mode: " << st.st_mode;
142 os << " size: " << st.st_size;
143 // xxx
144 break;
145 }
146 }
147 return os;
148 }
149
150 bool do_hier1 = false;
151 bool do_dirs1 = false;
152 bool do_readf = false;
153 bool do_writef = false;
154 bool do_marker1 = false;
155 bool do_create = false;
156 bool do_delete = false;
157 bool do_rename = false;
158 bool do_setattr = false;
159 bool verbose = false;
160
161 string marker_dir("nfs_marker");
162 struct rgw_file_handle *bucket_fh = nullptr;
163 struct rgw_file_handle *marker_fh;
164 static constexpr int marker_nobjs = 2*1024;
165 std::deque<obj_rec> marker_objs;
166
167 using dirent_t = std::tuple<std::string, uint64_t>;
168 struct dirent_vec
169 {
170 std::vector<dirent_t> obj_names;
171 uint32_t count;
172 dirent_vec() : count(0) {}
173 };
174
175 obj_rec dirs1_b{dirs1_bucket_name, nullptr, nullptr, nullptr};
176
177 dirs1_vec renames_vec;
178
179 struct {
180 int argc;
181 char **argv;
182 } saved_args;
183 }
184
185 TEST(LibRGW, INIT) {
186 int ret = librgw_create(&rgw_h, saved_args.argc, saved_args.argv);
187 ASSERT_EQ(ret, 0);
188 ASSERT_NE(rgw_h, nullptr);
189 }
190
191 TEST(LibRGW, MOUNT) {
192 int ret = rgw_mount(rgw_h, userid.c_str(), access_key.c_str(),
193 secret_key.c_str(), &fs, RGW_MOUNT_FLAG_NONE);
194 ASSERT_EQ(ret, 0);
195 ASSERT_NE(fs, nullptr);
196
197 cct = static_cast<RGWLibFS*>(fs->fs_private)->get_context();
198 }
199
200 TEST(LibRGW, SETUP_HIER1)
201 {
202 if (do_hier1) {
203 (void) rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
204 RGW_LOOKUP_FLAG_NONE);
205 if (! bucket_fh) {
206 if (do_create) {
207 struct stat st;
208
209 st.st_uid = owner_uid;
210 st.st_gid = owner_gid;
211 st.st_mode = 755;
212
213 int rc = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st,
214 create_mask, &bucket_fh, RGW_MKDIR_FLAG_NONE);
215 ASSERT_EQ(rc, 0);
216 }
217 }
218
219 ASSERT_NE(bucket_fh, nullptr);
220
221 if (do_create) {
222 /* create objects directly */
223 std::vector<std::string> obj_names =
224 {"foo/bar/baz/quux",
225 "foo/f1",
226 "foo/f2",
227 "foo/bar/f1",
228 "foo/bar/d1/",
229 "foo/bar/baz/hungry",
230 "foo/bar/baz/hungry/",
231 "foo/bar/baz/momma",
232 "foo/bar/baz/bear/",
233 "foo/bar/baz/sasquatch",
234 "foo/bar/baz/sasquatch/",
235 "foo/bar/baz/frobozz"};
236
237 buffer::list bl; // empty object
238 RGWLibFS *fs_private = static_cast<RGWLibFS*>(fs->fs_private);
239
240 for (const auto& obj_name : obj_names) {
241 if (verbose) {
242 std::cout << "creating: " << bucket_name << ":" << obj_name
243 << std::endl;
244 }
245 RGWPutObjRequest req(cct, fs_private->get_user(), bucket_name, obj_name,
246 bl);
247 int rc = rgwlib.get_fe()->execute_req(&req);
248 int rc2 = req.get_ret();
249 ASSERT_EQ(rc, 0);
250 ASSERT_EQ(rc2, 0);
251 }
252 }
253 }
254 }
255
256 TEST(LibRGW, SETUP_DIRS1) {
257 if (do_dirs1) {
258 int rc;
259 struct stat st;
260
261 st.st_uid = owner_uid;
262 st.st_gid = owner_gid;
263 st.st_mode = 755;
264
265 dirs1_b.parent_fh = fs->root_fh;
266
267 (void) rgw_lookup(fs, dirs1_b.parent_fh, dirs1_bucket_name.c_str(),
268 &dirs1_b.fh, RGW_LOOKUP_FLAG_NONE);
269
270 if (! dirs1_b.fh) {
271 if (do_create) {
272 rc = rgw_mkdir(fs, dirs1_b.parent_fh, dirs1_b.name.c_str(), &st,
273 create_mask, &dirs1_b.fh, RGW_MKDIR_FLAG_NONE);
274 ASSERT_EQ(rc, 0);
275 } else {
276 /* no top-level dir and can't create it--skip remaining tests */
277 return;
278 }
279 }
280 dirs1_b.sync();
281
282 /* make top-level dirs */
283 int d_ix;
284 obj_vec ovec;
285 for (d_ix = 0; d_ix < n_dirs1_dirs; ++d_ix) {
286 std::string dname{"dir_"};
287 dname += to_string(d_ix);
288 obj_rec dir{dname, nullptr, dirs1_b.fh, nullptr};
289 ovec.clear();
290
291 (void) rgw_lookup(fs, dir.parent_fh, dir.name.c_str(), &dir.fh,
292 RGW_LOOKUP_FLAG_NONE);
293 if (! dir.fh) {
294 if (do_create) {
295 rc = rgw_mkdir(fs, dir.parent_fh, dir.name.c_str(), &st, create_mask,
296 &dir.fh, RGW_MKDIR_FLAG_NONE);
297 ASSERT_EQ(rc, 0);
298 }
299 }
300
301 ASSERT_NE(dir.fh, nullptr);
302 dir.sync();
303 ASSERT_NE(dir.rgw_fh, nullptr);
304 ASSERT_TRUE(dir.rgw_fh->is_dir());
305
306 int f_ix;
307 for (f_ix = 0; f_ix < n_dirs1_objs; ++f_ix) {
308 /* child dir */
309 std::string sdname{"sdir_"};
310 sdname += to_string(f_ix);
311 obj_rec sdir{sdname, nullptr, dir.fh, nullptr};
312
313 (void) rgw_lookup(fs, sdir.parent_fh, sdir.name.c_str(), &sdir.fh,
314 RGW_LOOKUP_FLAG_NONE);
315
316 if (! sdir.fh) {
317 if (do_create) {
318 rc = rgw_mkdir(fs, sdir.parent_fh, sdir.name.c_str(), &st,
319 create_mask, &sdir.fh, RGW_MKDIR_FLAG_NONE);
320 ASSERT_EQ(rc, 0);
321 }
322 }
323
324 ASSERT_NE(sdir.fh, nullptr); // suppress !lookup && !create case
325
326 sdir.sync();
327 ASSERT_TRUE(sdir.rgw_fh->is_dir());
328 ovec.push_back(sdir);
329
330 /* child file */
331 std::string sfname{"sfile_"};
332
333 sfname += to_string(f_ix);
334 obj_rec sf{sfname, nullptr, dir.fh, nullptr};
335
336 (void) rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
337 RGW_LOOKUP_FLAG_NONE);
338
339 if (! sf.fh) {
340 if (do_create) {
341 /* make a new file object (the hard way) */
342 rc = rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
343 RGW_LOOKUP_FLAG_CREATE);
344 ASSERT_EQ(rc, 0);
345 sf.sync();
346 ASSERT_TRUE(sf.rgw_fh->is_file());
347
348 /* because we made it the hard way, fixup attributes */
349 struct stat st;
350 st.st_uid = owner_uid;
351 st.st_gid = owner_gid;
352 st.st_mode = 644;
353 sf.rgw_fh->create_stat(&st, create_mask);
354
355 /* open handle */
356 rc = rgw_open(fs, sf.fh, 0 /* posix flags */, 0 /* flags */);
357 ASSERT_EQ(rc, 0);
358 ASSERT_TRUE(sf.rgw_fh->is_open());
359 /* stage seq write */
360 size_t nbytes;
361 string data = "data for " + sf.name;
362 rc = rgw_write(fs, sf.fh, 0, data.length(), &nbytes,
363 (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
364 ASSERT_EQ(rc, 0);
365 ASSERT_EQ(nbytes, data.length());
366 /* commit write transaction */
367 rc = rgw_close(fs, sf.fh, 0 /* flags */);
368 ASSERT_EQ(rc, 0);
369 }
370 } else {
371 sf.sync();
372 ASSERT_TRUE(sf.rgw_fh->is_file());
373 }
374
375 if (sf.fh)
376 ovec.push_back(sf);
377 }
378 dirs_vec.push_back(dirs1_rec{dir, ovec});
379 }
380 } /* dirs1 top-level !exist */
381 }
382
383 TEST(LibRGW, SETATTR) {
384 if (do_dirs1) {
385 if (do_setattr) {
386
387 int rc;
388 struct stat st;
389
390 st.st_uid = owner_uid;
391 st.st_gid = owner_gid;
392 st.st_mode = 755;
393
394 std::string dname{"dir_0"};
395 obj_rec dir{dname, nullptr, dirs1_b.fh, nullptr};
396
397 /* dir_0 MUST exist and MUST be resident */
398 (void) rgw_lookup(fs, dir.parent_fh, dir.name.c_str(), &dir.fh,
399 RGW_LOOKUP_FLAG_NONE);
400
401 ASSERT_NE(dir.fh, nullptr);
402 dir.sync();
403 ASSERT_NE(dir.rgw_fh, nullptr);
404 ASSERT_TRUE(dir.rgw_fh->is_dir());
405
406 /* child file */
407 std::string sfname{"setattr_file_0"};
408 obj_rec sf{sfname, nullptr, dir.fh, nullptr};
409
410 (void) rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
411 RGW_LOOKUP_FLAG_NONE);
412
413 if (! sf.fh) {
414 /* make a new file object (the hard way) */
415 rc = rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
416 RGW_LOOKUP_FLAG_CREATE);
417 ASSERT_EQ(rc, 0);
418 sf.sync();
419 ASSERT_TRUE(sf.rgw_fh->is_file());
420
421 /* because we made it the hard way, fixup attributes */
422 st.st_uid = owner_uid;
423 st.st_gid = owner_gid;
424 st.st_mode = 644;
425 sf.rgw_fh->create_stat(&st, create_mask);
426
427 /* open handle */
428 rc = rgw_open(fs, sf.fh, 0 /* posix flags */, 0 /* flags */);
429 ASSERT_EQ(rc, 0);
430 ASSERT_TRUE(sf.rgw_fh->is_open());
431 /* stage seq write */
432 size_t nbytes;
433 string data = "data for " + sf.name;
434 rc = rgw_write(fs, sf.fh, 0, data.length(), &nbytes,
435 (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
436 ASSERT_EQ(rc, 0);
437 ASSERT_EQ(nbytes, data.length());
438 /* commit write transaction */
439 rc = rgw_close(fs, sf.fh, 0 /* flags */);
440 ASSERT_EQ(rc, 0);
441 } else {
442 sf.sync();
443 ASSERT_TRUE(sf.rgw_fh->is_file());
444 }
445
446 /* sf MUST now be materialized--now change it's attributes */
447 st.st_uid = magic_uid;
448 st.st_gid = magic_gid;
449
450 rc = rgw_setattr(fs, sf.fh, &st, create_mask, RGW_SETATTR_FLAG_NONE);
451 ASSERT_EQ(rc, 0);
452
453 /* force evict--subsequent lookups must reload */
454 static_cast<RGWLibFS*>(fs->fs_private)->release_evict(sf.rgw_fh);
455
456 sf.clear();
457
458 /* revalidate -- expect magic uid and gid */
459 (void) rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
460 RGW_LOOKUP_FLAG_NONE);
461 sf.sync();
462 ASSERT_NE(sf.fh, nullptr);
463
464 memset(&st, 0, sizeof(struct stat)); /* nothing up my sleeve... */
465
466 rc = rgw_getattr(fs, sf.fh, &st, RGW_GETATTR_FLAG_NONE);
467 ASSERT_EQ(rc, 0);
468
469 ASSERT_EQ(st.st_uid, magic_uid);
470 ASSERT_EQ(st.st_gid, magic_gid);
471
472 /* release 1 ref on sf */
473 rgw_fh_rele(fs, sf.fh, RGW_FH_RELE_FLAG_NONE);
474
475 /* release 1 ref on dir */
476 rgw_fh_rele(fs, dir.fh, RGW_FH_RELE_FLAG_NONE);
477 } /* dirs1 */
478 }
479 }
480
481 TEST(LibRGW, RGW_CREATE_DIRS1) {
482 /* verify rgw_create (create [empty] file objects the easy way) */
483 if (do_dirs1) {
484 if (do_create) {
485 int rc;
486 struct stat st;
487
488 st.st_uid = owner_uid;
489 st.st_gid = owner_gid;
490 st.st_mode = 644;
491
492 for (auto& dirs_rec : dirs_vec) {
493 /* create 1 more file in each sdir */
494 obj_rec& dir = get<0>(dirs_rec);
495 std::string sfname{"sfile_" + to_string(n_dirs1_objs)};
496 obj_rec sf{sfname, nullptr, dir.fh, nullptr};
497 (void) rgw_lookup(fs, sf.parent_fh, sf.name.c_str(), &sf.fh,
498 RGW_LOOKUP_FLAG_NONE);
499 if (! sf.fh) {
500 rc = rgw_create(fs, sf.parent_fh, sf.name.c_str(), &st, create_mask,
501 &sf.fh, 0 /* posix flags */, RGW_CREATE_FLAG_NONE);
502 ASSERT_EQ(rc, 0);
503 }
504 sf.sync();
505 }
506 n_dirs1_objs++;
507 }
508 }
509 }
510
511 TEST(LibRGW, RGW_SETUP_RENAME1) {
512 /* verify rgw_create (create [empty] file objects the easy way) */
513 if (do_rename) {
514 int rc;
515 struct stat st;
516 obj_vec ovec;
517
518 st.st_uid = owner_uid;
519 st.st_gid = owner_gid;
520 st.st_mode = 755;
521
522 for (int b_ix : {0, 1}) {
523 std::string bname{"brename_" + to_string(b_ix)};
524 obj_rec brec{bname, nullptr, nullptr, nullptr};
525 (void) rgw_lookup(fs, fs->root_fh, brec.name.c_str(), &brec.fh,
526 RGW_LOOKUP_FLAG_NONE);
527 if (! brec.fh) {
528 if (do_create) {
529 struct stat st;
530 int rc = rgw_mkdir(fs, fs->root_fh, brec.name.c_str(), &st,
531 create_mask, &brec.fh, RGW_MKDIR_FLAG_NONE);
532 ASSERT_EQ(rc, 0);
533 }
534 }
535 ASSERT_NE(brec.fh, nullptr);
536 brec.sync();
537
538 st.st_mode = 644; /* file mask */
539
540 for (int f_ix : {0, 1}) {
541 std::string rfname{"rfile_"};
542 rfname += to_string(f_ix);
543 obj_rec rf{rfname, nullptr, brec.fh, nullptr};
544 (void) rgw_lookup(fs, rf.parent_fh, rf.name.c_str(), &rf.fh,
545 RGW_LOOKUP_FLAG_NONE);
546 if (! rf.fh) {
547 rc = rgw_create(fs, rf.parent_fh, rf.name.c_str(), &st, create_mask,
548 &rf.fh, 0 /* posix flags */, RGW_CREATE_FLAG_NONE);
549 ASSERT_EQ(rc, 0);
550 }
551 rf.sync();
552 ovec.push_back(rf);
553 }
554 renames_vec.push_back(dirs1_rec{brec, ovec});
555 ovec.clear();
556 } /* b_ix */
557 }
558 }
559
560 TEST(LibRGW, RGW_INTRABUCKET_RENAME1) {
561 /* rgw_rename a file within a bucket */
562 if (do_rename) {
563 int rc;
564 obj_rec& bdir0 = get<0>(renames_vec[0]);
565 obj_rec& src_obj = get<1>(renames_vec[0])[0];
566 std::string rfname{"rfile_r0"};
567 if (verbose) {
568 std::cout << "rename file " << src_obj.name << " to "
569 << rfname << " (bucket " << bdir0.name << ")"
570 << std::endl;
571 }
572 rc = rgw_rename(fs, bdir0.fh, src_obj.name.c_str(), bdir0.fh,
573 rfname.c_str(), RGW_RENAME_FLAG_NONE);
574 ASSERT_EQ(rc, 0);
575 }
576 }
577
578 TEST(LibRGW, RGW_CROSSBUCKET_RENAME1) {
579 /* rgw_rename a file within a bucket */
580 if (do_rename) {
581 int rc;
582 obj_rec& bdir0 = get<0>(renames_vec[0]);
583 obj_rec& bdir1 = get<0>(renames_vec[1]);
584 obj_rec& src_obj = get<1>(renames_vec[0])[1];
585 std::string rfname{"rfile_rhilldog"};
586 if (verbose) {
587 std::cout << "rename file " << src_obj.name
588 << " (bucket " << bdir0.name << ") to "
589 << rfname << " (bucket " << bdir1.name << ")"
590 << std::endl;
591 }
592 rc = rgw_rename(fs, bdir0.fh, src_obj.name.c_str(), bdir1.fh,
593 rfname.c_str(), RGW_RENAME_FLAG_NONE);
594 ASSERT_EQ(rc, 0);
595 }
596 }
597
598 TEST(LibRGW, BAD_DELETES_DIRS1) {
599 if (do_dirs1) {
600 int rc;
601
602 if (dirs_vec.size() == 0) {
603 /* skip */
604 return;
605 }
606
607 if (do_delete) {
608 /* try to unlink a non-empty directory (bucket) */
609 rc = rgw_unlink(fs, dirs1_b.parent_fh, dirs1_b.name.c_str(),
610 RGW_UNLINK_FLAG_NONE);
611 ASSERT_NE(rc, 0);
612 }
613 /* try to unlink a non-empty directory (non-bucket) */
614 obj_rec& sdir_0 = get<1>(dirs_vec[0])[0];
615 ASSERT_EQ(sdir_0.name, "sdir_0");
616 ASSERT_TRUE(sdir_0.rgw_fh->is_dir());
617 /* XXX we can't enforce this currently */
618 #if 0
619 ASSERT_EQ(sdir_0.name, "sdir_0");
620 ASSERT_TRUE(sdir_0.rgw_fh->is_dir());
621 rc = rgw_unlink(fs, sdir_0.parent_fh, sdir_0.name.c_str(),
622 RGW_UNLINK_FLAG_NONE);
623 ASSERT_NE(rc, 0);
624 #endif
625 }
626 }
627
628 TEST(LibRGW, GETATTR_DIRS1)
629 {
630 if (do_dirs1) {
631 int rc;
632 struct stat st;
633 for (auto& dirs_rec : dirs_vec) {
634 obj_rec& dir = get<0>(dirs_rec);
635 if (verbose) {
636 std::cout << "scanning objects in "
637 << dir.rgw_fh->full_object_name()
638 << std::endl;
639 }
640 for (auto& sobj : get<1>(dirs_rec)) {
641 rc = rgw_getattr(fs, sobj.fh, &st, RGW_GETATTR_FLAG_NONE);
642 ASSERT_EQ(rc, 0);
643 /* validate, pretty-print */
644 if (sobj.rgw_fh->object_name().find("sfile") != std::string::npos) {
645 ASSERT_TRUE(sobj.rgw_fh->is_file());
646 ASSERT_TRUE(S_ISREG(st.st_mode));
647 }
648 if (sobj.rgw_fh->object_name().find("sdir") != std::string::npos) {
649 ASSERT_TRUE(sobj.rgw_fh->is_dir());
650 ASSERT_TRUE(S_ISDIR(st.st_mode));
651 }
652 /* validate Unix owners */
653 ASSERT_EQ(st.st_uid, owner_uid);
654 ASSERT_EQ(st.st_gid, owner_gid);
655 if (verbose) {
656 obj_rec_st rec_st{sobj, st};
657 std::cout << "\t"
658 << rec_st
659 << std::endl;
660 }
661 }
662 }
663 }
664 }
665
666 TEST(LibRGW, READ_DIRS1)
667 {
668 if (do_dirs1) {
669 int rc;
670 char buf[256];
671 size_t nread;
672 for (auto& dirs_rec : dirs_vec) {
673 obj_rec& dir = get<0>(dirs_rec);
674 if (verbose) {
675 std::cout << "read back objects in "
676 << dir.rgw_fh->full_object_name()
677 << std::endl;
678 }
679 for (auto& sobj : get<1>(dirs_rec)) {
680 /* only the first 2 file objects have data */
681 if ((sobj.rgw_fh->object_name().find("sfile_0")
682 != std::string::npos) ||
683 (sobj.rgw_fh->object_name().find("sfile_1")
684 != std::string::npos)) {
685 ASSERT_TRUE(sobj.rgw_fh->is_file());
686 ASSERT_EQ(sobj.rgw_fh->get_size(), 16UL);
687 // do it
688 memset(buf, 0, 256);
689 if (verbose) {
690 std::cout << "reading 0,256 " << sobj.rgw_fh->relative_object_name()
691 << std::endl;
692 }
693 rc = rgw_read(fs, sobj.fh, 0, 256, &nread, buf, RGW_READ_FLAG_NONE);
694 ASSERT_EQ(rc, 0);
695 if (verbose) {
696 std::cout << "\tread back from " << sobj.name
697 << " : \"" << buf << "\""
698 << std::endl;
699 }
700 }
701 }
702 }
703 }
704 }
705
706 TEST(LibRGW, READF_SETUP1)
707 {
708 struct stat st;
709 if (do_dirs1) {
710 if (do_create) {
711 if ((! stat(readf_out_name.c_str(), &st)) &&
712 (S_ISREG(st.st_mode)) &&
713 (st.st_size == 6291456))
714 return;
715 ofstream of;
716 of.open(readf_out_name, ios::out|ios::app|ios::binary);
717 for (int ix1 = 0; ix1 < 6; ++ix1) {
718 for (int ix2 = 0; ix2 < 1024*1024; ++ix2) {
719 of << ix1;
720 }
721 }
722 }
723 }
724 }
725
726 TEST(LibRGW, READF_DIRS1) {
727 if (do_dirs1) {
728 if (do_readf) {
729 obj_rec fobj{readf_name, nullptr, dirs1_b.fh, nullptr};
730
731 int rc = rgw_lookup(fs, dirs1_b.fh, fobj.name.c_str(), &fobj.fh,
732 RGW_LOOKUP_FLAG_NONE);
733 ASSERT_EQ(rc, 0);
734 ASSERT_NE(fobj.fh, nullptr);
735 fobj.sync();
736
737 ofstream of;
738 of.open(readf_out_name, ios::out|ios::app|ios::binary);
739 int bufsz = 1024 * 1024 * sizeof(char);
740 char *buffer = (char*) malloc(bufsz);
741
742 uint64_t offset = 0;
743 uint64_t length = bufsz;
744 for (int ix = 0; ix < 6; ++ix) {
745 size_t nread = 0;
746 memset(buffer, 0, length); // XXX
747 rc = rgw_read(fs, fobj.fh, offset, length, &nread, buffer,
748 RGW_READ_FLAG_NONE);
749 ASSERT_EQ(rc, 0);
750 ASSERT_EQ(nread, length);
751 of.write(buffer, length);
752 offset += nread;
753 }
754 of.close();
755 free(buffer);
756 rgw_fh_rele(fs, fobj.fh, 0 /* flags */);
757 }
758 }
759 }
760
761 TEST(LibRGW, WRITEF_DIRS1) {
762 if (do_dirs1) {
763 if (do_writef) {
764 int rc;
765 ifstream ifs;
766 ifs.open(readf_out_name, ios::out|ios::app|ios::binary);
767 ASSERT_TRUE(ifs.is_open());
768
769 obj_rec fobj{writef_name, nullptr, dirs1_b.fh, nullptr};
770
771 (void) rgw_lookup(fs, fobj.parent_fh, fobj.name.c_str(), &fobj.fh,
772 RGW_LOOKUP_FLAG_NONE);
773 if (! fobj.fh) {
774 if (do_create) {
775 /* make a new file object (the hard way) */
776 rc = rgw_lookup(fs, fobj.parent_fh, fobj.name.c_str(), &fobj.fh,
777 RGW_LOOKUP_FLAG_CREATE);
778 ASSERT_EQ(rc, 0);
779 }
780 }
781 ASSERT_NE(fobj.fh, nullptr);
782 fobj.sync();
783
784 /* begin write transaction */
785 rc = rgw_open(fs, fobj.fh, 0 /* posix flags */, 0 /* flags */);
786 ASSERT_EQ(rc, 0);
787 ASSERT_TRUE(fobj.rgw_fh->is_open());
788
789 int bufsz = 1024 * 1024 * sizeof(char);
790 char *buffer = (char*) malloc(bufsz);
791
792 uint64_t offset = 0;
793 uint64_t length = bufsz;
794 for (int ix = 0; ix < 6; ++ix) {
795 ASSERT_TRUE(ifs.good());
796 ifs.read(buffer, bufsz);
797 size_t nwritten = 0;
798 string str;
799 str.assign(buffer, 4);
800 if (verbose) {
801 std::cout << "read and writing " << length << " bytes"
802 << " from " << readf_out_name
803 << " at offset " << offset
804 << " (" << str << "... [first 4 chars])"
805 << std::endl;
806 }
807 char* leakbuf = (char*) malloc(bufsz);
808 memcpy(leakbuf, buffer, length);
809 rc = rgw_write(fs, fobj.fh, offset, length, &nwritten, leakbuf,
810 RGW_WRITE_FLAG_NONE);
811 ASSERT_EQ(rc, 0);
812 ASSERT_EQ(nwritten, length);
813 offset += length;
814 }
815
816 /* commit write transaction */
817 rc = rgw_close(fs, fobj.fh, RGW_CLOSE_FLAG_NONE);
818 ASSERT_EQ(rc, 0);
819
820 ifs.close();
821 free(buffer);
822 rgw_fh_rele(fs, fobj.fh, 0 /* flags */);
823 }
824 }
825 }
826
827 TEST(LibRGW, RELEASE_DIRS1) {
828 if (do_dirs1) {
829 /* force release of handles for children of dirs1--force subsequent
830 * checks to reload them from the cluster.
831 *
832 * while doing this, verify handle cleanup and correct LRU state
833 * (not reachable)
834 */
835 int rc;
836 for (auto& dirs_rec : dirs_vec) {
837 for (auto& obj : get<1>(dirs_rec)) {
838 if (verbose) {
839 std::cout << "release " << obj.name
840 << " type: " << obj.rgw_fh->stype()
841 << " refs: " << obj.rgw_fh->get_refcnt()
842 << std::endl;
843 }
844 ASSERT_EQ(obj.rgw_fh->get_refcnt(), 2UL);
845 rc = rgw_fh_rele(fs, obj.fh, 0 /* flags */);
846 ASSERT_EQ(rc, 0);
847 ASSERT_EQ(obj.rgw_fh->get_refcnt(), 1UL);
848 /* try-discard handle */
849 /* clear obj_rec vec */
850 }
851 }
852 }
853 }
854
855 extern "C" {
856 static bool r1_cb(const char* name, void *arg, uint64_t offset,
857 uint32_t flags) {
858 struct rgw_file_handle* parent_fh
859 = static_cast<struct rgw_file_handle*>(arg);
860 RGWFileHandle* rgw_fh = get_rgwfh(parent_fh);
861 lsubdout(cct, rgw, 10) << __func__
862 << " bucket=" << rgw_fh->bucket_name()
863 << " dir=" << rgw_fh->full_object_name()
864 << " called back name=" << name
865 << " flags=" << flags
866 << dendl;
867 string name_str{name};
868 if (! ((name_str == ".") ||
869 (name_str == ".."))) {
870 obj_stack.push(
871 obj_rec{std::move(name_str), nullptr, parent_fh, nullptr});
872 }
873 return true; /* XXX */
874 }
875 }
876
877 TEST(LibRGW, HIER1) {
878 if (do_hier1) {
879 int rc;
880 obj_stack.push(
881 obj_rec{bucket_name, nullptr, nullptr, nullptr});
882 while (! obj_stack.empty()) {
883 auto& elt = obj_stack.top();
884 if (! elt.fh) {
885 struct rgw_file_handle* parent_fh = elt.parent_fh
886 ? elt.parent_fh : fs->root_fh;
887 RGWFileHandle* pfh = get_rgwfh(parent_fh);
888 rgw::ignore(pfh);
889 lsubdout(cct, rgw, 10)
890 << "rgw_lookup:"
891 << " parent object_name()=" << pfh->object_name()
892 << " parent full_object_name()=" << pfh->full_object_name()
893 << " elt.name=" << elt.name
894 << dendl;
895 rc = rgw_lookup(fs, parent_fh, elt.name.c_str(), &elt.fh,
896 RGW_LOOKUP_FLAG_NONE);
897 ASSERT_EQ(rc, 0);
898 // XXXX
899 RGWFileHandle* efh = get_rgwfh(elt.fh);
900 rgw::ignore(efh);
901 lsubdout(cct, rgw, 10)
902 << "rgw_lookup result:"
903 << " elt object_name()=" << efh->object_name()
904 << " elt full_object_name()=" << efh->full_object_name()
905 << " elt.name=" << elt.name
906 << dendl;
907
908 ASSERT_NE(elt.fh, nullptr);
909 elt.rgw_fh = get_rgwfh(elt.fh);
910 elt.parent_fh = elt.rgw_fh->get_parent()->get_fh();
911 ASSERT_EQ(elt.parent_fh, parent_fh);
912 continue;
913 } else {
914 // we have a handle in some state in top position
915 switch(elt.fh->fh_type) {
916 case RGW_FS_TYPE_DIRECTORY:
917 if (! elt.state.readdir) {
918 // descending
919 uint64_t offset = 0;
920 bool eof; // XXX
921 lsubdout(cct, rgw, 10)
922 << "readdir in"
923 << " bucket: " << elt.rgw_fh->bucket_name()
924 << " object_name: " << elt.rgw_fh->object_name()
925 << " full_name: " << elt.rgw_fh->full_object_name()
926 << dendl;
927 rc = rgw_readdir(fs, elt.fh, &offset, r1_cb, elt.fh, &eof,
928 RGW_READDIR_FLAG_DOTDOT);
929 elt.state.readdir = true;
930 ASSERT_EQ(rc, 0);
931 // ASSERT_TRUE(eof); // XXXX working incorrectly w/single readdir
932 } else {
933 // ascending
934 std::cout << elt << std::endl;
935 cleanup_queue.push_back(elt);
936 obj_stack.pop();
937 }
938 break;
939 case RGW_FS_TYPE_FILE:
940 // ascending
941 std::cout << elt << std::endl;
942 cleanup_queue.push_back(elt);
943 obj_stack.pop();
944 break;
945 default:
946 abort();
947 };
948 }
949 }
950 }
951 }
952
953 TEST(LibRGW, MARKER1_SETUP_BUCKET) {
954 /* "large" directory enumeration test. this one deals only with
955 * file objects */
956 if (do_marker1) {
957 struct stat st;
958 int ret;
959
960 st.st_uid = owner_uid;
961 st.st_gid = owner_gid;
962 st.st_mode = 755;
963
964 if (do_create) {
965 ret = rgw_mkdir(fs, bucket_fh, marker_dir.c_str(), &st, create_mask,
966 &marker_fh, RGW_MKDIR_FLAG_NONE);
967 } else {
968 ret = rgw_lookup(fs, bucket_fh, marker_dir.c_str(), &marker_fh,
969 RGW_LOOKUP_FLAG_NONE);
970 }
971 ASSERT_EQ(ret, 0);
972 }
973 }
974
975 TEST(LibRGW, MARKER1_SETUP_OBJECTS)
976 {
977 /* "large" directory enumeration test. this one deals only with
978 * file objects */
979 if (do_marker1 && do_create) {
980 int ret;
981
982 for (int ix = 0; ix < marker_nobjs; ++ix) {
983 std::string object_name("f_");
984 object_name += to_string(ix);
985 obj_rec obj{object_name, nullptr, marker_fh, nullptr};
986 // lookup object--all operations are by handle
987 ret = rgw_lookup(fs, marker_fh, obj.name.c_str(), &obj.fh,
988 RGW_LOOKUP_FLAG_CREATE);
989 ASSERT_EQ(ret, 0);
990 obj.rgw_fh = get_rgwfh(obj.fh);
991 // open object--open transaction
992 ret = rgw_open(fs, obj.fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
993 ASSERT_EQ(ret, 0);
994 ASSERT_TRUE(obj.rgw_fh->is_open());
995 // unstable write data
996 size_t nbytes;
997 string data("data for ");
998 data += object_name;
999 int ret = rgw_write(fs, obj.fh, 0, data.length(), &nbytes,
1000 (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
1001 ASSERT_EQ(ret, 0);
1002 ASSERT_EQ(nbytes, data.length());
1003 // commit transaction (write on close)
1004 ret = rgw_close(fs, obj.fh, 0 /* flags */);
1005 ASSERT_EQ(ret, 0);
1006 // save for cleanup
1007 marker_objs.push_back(obj);
1008 }
1009 }
1010 }
1011
1012 extern "C" {
1013 static bool r2_cb(const char* name, void *arg, uint64_t offset,
1014 uint32_t flags) {
1015 dirent_vec& dvec =
1016 *(static_cast<dirent_vec*>(arg));
1017 lsubdout(cct, rgw, 10) << __func__
1018 << " bucket=" << bucket_name
1019 << " dir=" << marker_dir
1020 << " iv count=" << dvec.count
1021 << " called back name=" << name
1022 << " flags=" << flags
1023 << dendl;
1024 string name_str{name};
1025 if (! ((name_str == ".") ||
1026 (name_str == ".."))) {
1027 dvec.obj_names.push_back(dirent_t{std::move(name_str), offset});
1028 }
1029 return true; /* XXX */
1030 }
1031 }
1032
1033 TEST(LibRGW, MARKER1_READDIR)
1034 {
1035 if (do_marker1) {
1036 using std::get;
1037
1038 dirent_vec dvec;
1039 uint64_t offset = 0;
1040 bool eof = false;
1041
1042 /* because RGWReaddirRequest::default_max is 1000 (XXX make
1043 * configurable?) and marker_nobjs is 5*1024, the number
1044 * of required rgw_readdir operations N should be
1045 * marker_nobjs/1000 < N < marker_nobjs/1000+1, i.e., 6 when
1046 * marker_nobjs==5*1024 */
1047 uint32_t max_iterations = marker_nobjs/1000+1;
1048
1049 do {
1050 ASSERT_TRUE(dvec.count <= max_iterations);
1051 int ret = rgw_readdir(fs, marker_fh, &offset, r2_cb, &dvec, &eof,
1052 RGW_READDIR_FLAG_DOTDOT);
1053 ASSERT_EQ(ret, 0);
1054 ASSERT_EQ(offset, get<1>(dvec.obj_names.back())); // cookie check
1055 ++dvec.count;
1056 } while(!eof);
1057 std::cout << "Read " << dvec.obj_names.size() << " objects in "
1058 << marker_dir.c_str() << std::endl;
1059 }
1060 }
1061
1062 TEST(LibRGW, MARKER1_OBJ_CLEANUP)
1063 {
1064 int rc;
1065 for (auto& obj : marker_objs) {
1066 if (obj.fh) {
1067 if (do_delete) {
1068 if (verbose) {
1069 std::cout << "unlinking: " << bucket_name << ":" << obj.name
1070 << std::endl;
1071 }
1072 rc = rgw_unlink(fs, marker_fh, obj.name.c_str(), RGW_UNLINK_FLAG_NONE);
1073 }
1074 rc = rgw_fh_rele(fs, obj.fh, 0 /* flags */);
1075 ASSERT_EQ(rc, 0);
1076 }
1077 }
1078 marker_objs.clear();
1079 }
1080
1081 TEST(LibRGW, CLEANUP) {
1082 int rc;
1083
1084 if (do_marker1) {
1085 cleanup_queue.push_back(
1086 obj_rec{bucket_name, bucket_fh, fs->root_fh, get_rgwfh(fs->root_fh)});
1087 }
1088
1089 for (auto& elt : cleanup_queue) {
1090 if (elt.fh) {
1091 rc = rgw_fh_rele(fs, elt.fh, 0 /* flags */);
1092 ASSERT_EQ(rc, 0);
1093 }
1094 }
1095 cleanup_queue.clear();
1096 }
1097
1098 TEST(LibRGW, UMOUNT) {
1099 if (! fs)
1100 return;
1101
1102 int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
1103 ASSERT_EQ(ret, 0);
1104 }
1105
1106 TEST(LibRGW, SHUTDOWN) {
1107 librgw_shutdown(rgw_h);
1108 }
1109
1110 int main(int argc, char *argv[])
1111 {
1112 char *v{nullptr};
1113 string val;
1114 vector<const char*> args;
1115
1116 argv_to_vec(argc, const_cast<const char**>(argv), args);
1117 env_to_vec(args);
1118
1119 v = getenv("AWS_ACCESS_KEY_ID");
1120 if (v) {
1121 access_key = v;
1122 }
1123
1124 v = getenv("AWS_SECRET_ACCESS_KEY");
1125 if (v) {
1126 secret_key = v;
1127 }
1128
1129 for (auto arg_iter = args.begin(); arg_iter != args.end();) {
1130 if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
1131 (char*) nullptr)) {
1132 access_key = val;
1133 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
1134 (char*) nullptr)) {
1135 secret_key = val;
1136 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--userid",
1137 (char*) nullptr)) {
1138 userid = val;
1139 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
1140 (char*) nullptr)) {
1141 bucket_name = val;
1142 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
1143 (char*) nullptr)) {
1144 owner_uid = std::stoi(val);
1145 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--gid",
1146 (char*) nullptr)) {
1147 owner_gid = std::stoi(val);
1148 } else if (ceph_argparse_flag(args, arg_iter, "--hier1",
1149 (char*) nullptr)) {
1150 do_hier1 = true;
1151 } else if (ceph_argparse_flag(args, arg_iter, "--dirs1",
1152 (char*) nullptr)) {
1153 do_dirs1 = true;
1154 } else if (ceph_argparse_flag(args, arg_iter, "--marker1",
1155 (char*) nullptr)) {
1156 do_marker1 = true;
1157 } else if (ceph_argparse_flag(args, arg_iter, "--setattr",
1158 (char*) nullptr)) {
1159 do_setattr = true;
1160 } else if (ceph_argparse_flag(args, arg_iter, "--create",
1161 (char*) nullptr)) {
1162 do_create = true;
1163 } else if (ceph_argparse_flag(args, arg_iter, "--delete",
1164 (char*) nullptr)) {
1165 do_delete = true;
1166 } else if (ceph_argparse_flag(args, arg_iter, "--rename",
1167 (char*) nullptr)) {
1168 do_rename = true;
1169 } else if (ceph_argparse_flag(args, arg_iter, "--readf",
1170 (char*) nullptr)) {
1171 do_readf = true;
1172 } else if (ceph_argparse_flag(args, arg_iter, "--writef",
1173 (char*) nullptr)) {
1174 do_writef = true;
1175 } else if (ceph_argparse_flag(args, arg_iter, "--verbose",
1176 (char*) nullptr)) {
1177 verbose = true;
1178 } else {
1179 ++arg_iter;
1180 }
1181 }
1182
1183 /* dont accidentally run as anonymous */
1184 if ((access_key == "") ||
1185 (secret_key == "")) {
1186 std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
1187 return EPERM;
1188 }
1189
1190 saved_args.argc = argc;
1191 saved_args.argv = argv;
1192
1193 ::testing::InitGoogleTest(&argc, argv);
1194 return RUN_ALL_TESTS();
1195 }