]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/librgw_file_nfsns.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / test / librgw_file_nfsns.cc
CommitLineData
7c673cae
FG
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"
11fdf7f2 29#include "include/ceph_assert.h"
7c673cae
FG
30
31#define dout_subsys ceph_subsys_rgw
32
33namespace {
34
35 using namespace rgw;
36 using std::get;
37 using std::string;
38
39 librgw_t rgw_h = nullptr;
40 string userid("testuser");
41 string access_key("");
42 string secret_key("");
43 struct rgw_fs *fs = nullptr;
44 CephContext* cct = nullptr;
45
46 uint32_t owner_uid = 867;
47 uint32_t owner_gid = 5309;
48
49 uint32_t magic_uid = 1701;
50 uint32_t magic_gid = 9876;
51
52 uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE;
53
54 string bucket_name("nfsroot");
55 string dirs1_bucket_name("bdirs1");
56 string readf_name("toyland");
57 string readf_out_name("rgwlib_readf.out");
58 std::string writef_name{"bigbird"};
59
60 int n_dirs1_dirs = 3;
61 int n_dirs1_objs = 2;
62
63 class obj_rec
64 {
65 public:
66 string name;
67 struct rgw_file_handle* fh;
68 struct rgw_file_handle* parent_fh;
69 RGWFileHandle* rgw_fh; // alias into fh
70
71 struct state {
72 bool readdir;
73 state() : readdir(false) {}
74 } state;
75
76 obj_rec(string _name, struct rgw_file_handle* _fh,
77 struct rgw_file_handle* _parent_fh, RGWFileHandle* _rgw_fh)
78 : name(std::move(_name)), fh(_fh), parent_fh(_parent_fh),
79 rgw_fh(_rgw_fh) {}
80
81 void clear() {
82 fh = nullptr;
83 rgw_fh = nullptr;
84 }
85
86 void sync() {
87 if (fh)
88 rgw_fh = get_rgwfh(fh);
89 }
90
91 friend ostream& operator<<(ostream& os, const obj_rec& rec);
92 };
93
94 ostream& operator<<(ostream& os, const obj_rec& rec)
95 {
96 RGWFileHandle* rgw_fh = rec.rgw_fh;
97 if (rgw_fh) {
98 const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
99 os << rec.rgw_fh->full_object_name()
100 << " (" << rec.rgw_fh->object_name() << "): "
101 << type;
102 }
103 return os;
104 }
105
106 std::stack<obj_rec> obj_stack;
107 std::deque<obj_rec> cleanup_queue;
108
109 typedef std::vector<obj_rec> obj_vec;
110 typedef std::tuple<obj_rec, obj_vec> dirs1_rec;
111 typedef std::vector<dirs1_rec> dirs1_vec;
112
113 dirs1_vec dirs_vec;
114
115 struct obj_rec_st
116 {
117 const obj_rec& obj;
118 const struct stat& st;
119
120 obj_rec_st(const obj_rec& _obj, const struct stat& _st)
121 : obj(_obj), st(_st) {}
122 };
123
124 ostream& operator<<(ostream& os, const obj_rec_st& rec)
125 {
126 RGWFileHandle* rgw_fh = rec.obj.rgw_fh;
127 if (rgw_fh) {
128 const char* type = rgw_fh->is_dir() ? "DIR " : "FILE ";
129 os << rgw_fh->full_object_name()
130 << " (" << rgw_fh->object_name() << "): "
131 << type;
132 const struct stat& st = rec.st;
133 switch(uint8_t(rgw_fh->is_dir())) {
134 case 1:
135 os << " mode: " << st.st_mode;
136 os << " nlinks: " << st.st_nlink;
137 break;
138 case 0:
139 default:
140 os << " mode: " << st.st_mode;
141 os << " size: " << st.st_size;
142 // xxx
143 break;
144 }
145 }
146 return os;
147 }
148
149 bool do_hier1 = false;
150 bool do_dirs1 = false;
151 bool do_readf = false;
152 bool do_writef = false;
153 bool do_marker1 = false;
154 bool do_create = false;
155 bool do_delete = false;
156 bool do_rename = false;
157 bool do_setattr = false;
158 bool verbose = false;
159
160 string marker_dir("nfs_marker");
161 struct rgw_file_handle *bucket_fh = nullptr;
162 struct rgw_file_handle *marker_fh;
163 static constexpr int marker_nobjs = 2*1024;
164 std::deque<obj_rec> marker_objs;
165
166 using dirent_t = std::tuple<std::string, uint64_t>;
167 struct dirent_vec
168 {
169 std::vector<dirent_t> obj_names;
170 uint32_t count;
171 dirent_vec() : count(0) {}
172 };
173
174 obj_rec dirs1_b{dirs1_bucket_name, nullptr, nullptr, nullptr};
175
176 dirs1_vec renames_vec;
177
178 struct {
179 int argc;
180 char **argv;
181 } saved_args;
182}
183
184TEST(LibRGW, INIT) {
185 int ret = librgw_create(&rgw_h, saved_args.argc, saved_args.argv);
186 ASSERT_EQ(ret, 0);
187 ASSERT_NE(rgw_h, nullptr);
188}
189
190TEST(LibRGW, MOUNT) {
3efd9988
FG
191 int ret = rgw_mount2(rgw_h, userid.c_str(), access_key.c_str(),
192 secret_key.c_str(), "/", &fs, RGW_MOUNT_FLAG_NONE);
7c673cae
FG
193 ASSERT_EQ(ret, 0);
194 ASSERT_NE(fs, nullptr);
195
196 cct = static_cast<RGWLibFS*>(fs->fs_private)->get_context();
197}
198
199TEST(LibRGW, SETUP_HIER1)
200{
201 if (do_hier1) {
202 (void) rgw_lookup(fs, fs->root_fh, bucket_name.c_str(), &bucket_fh,
eafe8130 203 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
204 if (! bucket_fh) {
205 if (do_create) {
206 struct stat st;
207
208 st.st_uid = owner_uid;
209 st.st_gid = owner_gid;
210 st.st_mode = 755;
211
212 int rc = rgw_mkdir(fs, fs->root_fh, bucket_name.c_str(), &st,
213 create_mask, &bucket_fh, RGW_MKDIR_FLAG_NONE);
214 ASSERT_EQ(rc, 0);
215 }
216 }
217
218 ASSERT_NE(bucket_fh, nullptr);
219
220 if (do_create) {
221 /* create objects directly */
222 std::vector<std::string> obj_names =
223 {"foo/bar/baz/quux",
224 "foo/f1",
225 "foo/f2",
226 "foo/bar/f1",
227 "foo/bar/d1/",
228 "foo/bar/baz/hungry",
229 "foo/bar/baz/hungry/",
230 "foo/bar/baz/momma",
231 "foo/bar/baz/bear/",
232 "foo/bar/baz/sasquatch",
233 "foo/bar/baz/sasquatch/",
234 "foo/bar/baz/frobozz"};
235
236 buffer::list bl; // empty object
237 RGWLibFS *fs_private = static_cast<RGWLibFS*>(fs->fs_private);
238
239 for (const auto& obj_name : obj_names) {
240 if (verbose) {
241 std::cout << "creating: " << bucket_name << ":" << obj_name
242 << std::endl;
243 }
f67539c2
TL
244 RGWPutObjRequest req(cct,
245 rgwlib.get_store()->get_user(fs_private->get_user()->user_id),
246 bucket_name, obj_name, bl);
7c673cae
FG
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
256TEST(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(),
eafe8130 268 &dirs1_b.fh, nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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,
eafe8130 292 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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,
eafe8130 314 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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,
eafe8130 337 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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,
eafe8130 343 nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
7c673cae
FG
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
383TEST(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,
eafe8130 399 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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,
eafe8130 411 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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,
eafe8130 416 nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
7c673cae
FG
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,
eafe8130 460 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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
481TEST(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,
eafe8130 498 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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
511TEST(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}) {
f67539c2 523 std::string bname{"brename" + to_string(b_ix)};
7c673cae
FG
524 obj_rec brec{bname, nullptr, nullptr, nullptr};
525 (void) rgw_lookup(fs, fs->root_fh, brec.name.c_str(), &brec.fh,
eafe8130 526 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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,
eafe8130 545 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
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
560TEST(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
578TEST(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
f67539c2 598#if 0 /* XXX inconsistent failure here */
7c673cae
FG
599TEST(LibRGW, BAD_DELETES_DIRS1) {
600 if (do_dirs1) {
601 int rc;
602
603 if (dirs_vec.size() == 0) {
604 /* skip */
605 return;
606 }
607
608 if (do_delete) {
609 /* try to unlink a non-empty directory (bucket) */
610 rc = rgw_unlink(fs, dirs1_b.parent_fh, dirs1_b.name.c_str(),
611 RGW_UNLINK_FLAG_NONE);
612 ASSERT_NE(rc, 0);
613 }
614 /* try to unlink a non-empty directory (non-bucket) */
615 obj_rec& sdir_0 = get<1>(dirs_vec[0])[0];
616 ASSERT_EQ(sdir_0.name, "sdir_0");
617 ASSERT_TRUE(sdir_0.rgw_fh->is_dir());
618 /* XXX we can't enforce this currently */
619#if 0
620 ASSERT_EQ(sdir_0.name, "sdir_0");
621 ASSERT_TRUE(sdir_0.rgw_fh->is_dir());
622 rc = rgw_unlink(fs, sdir_0.parent_fh, sdir_0.name.c_str(),
623 RGW_UNLINK_FLAG_NONE);
624 ASSERT_NE(rc, 0);
625#endif
626 }
627}
f67539c2 628#endif
7c673cae
FG
629
630TEST(LibRGW, GETATTR_DIRS1)
631{
632 if (do_dirs1) {
633 int rc;
634 struct stat st;
635 for (auto& dirs_rec : dirs_vec) {
636 obj_rec& dir = get<0>(dirs_rec);
637 if (verbose) {
638 std::cout << "scanning objects in "
639 << dir.rgw_fh->full_object_name()
640 << std::endl;
641 }
642 for (auto& sobj : get<1>(dirs_rec)) {
643 rc = rgw_getattr(fs, sobj.fh, &st, RGW_GETATTR_FLAG_NONE);
644 ASSERT_EQ(rc, 0);
645 /* validate, pretty-print */
646 if (sobj.rgw_fh->object_name().find("sfile") != std::string::npos) {
647 ASSERT_TRUE(sobj.rgw_fh->is_file());
648 ASSERT_TRUE(S_ISREG(st.st_mode));
649 }
650 if (sobj.rgw_fh->object_name().find("sdir") != std::string::npos) {
651 ASSERT_TRUE(sobj.rgw_fh->is_dir());
652 ASSERT_TRUE(S_ISDIR(st.st_mode));
653 }
654 /* validate Unix owners */
655 ASSERT_EQ(st.st_uid, owner_uid);
656 ASSERT_EQ(st.st_gid, owner_gid);
657 if (verbose) {
658 obj_rec_st rec_st{sobj, st};
659 std::cout << "\t"
660 << rec_st
661 << std::endl;
662 }
663 }
664 }
665 }
666}
667
668TEST(LibRGW, READ_DIRS1)
669{
670 if (do_dirs1) {
671 int rc;
672 char buf[256];
673 size_t nread;
674 for (auto& dirs_rec : dirs_vec) {
675 obj_rec& dir = get<0>(dirs_rec);
676 if (verbose) {
677 std::cout << "read back objects in "
678 << dir.rgw_fh->full_object_name()
679 << std::endl;
680 }
681 for (auto& sobj : get<1>(dirs_rec)) {
682 /* only the first 2 file objects have data */
683 if ((sobj.rgw_fh->object_name().find("sfile_0")
684 != std::string::npos) ||
685 (sobj.rgw_fh->object_name().find("sfile_1")
686 != std::string::npos)) {
687 ASSERT_TRUE(sobj.rgw_fh->is_file());
688 ASSERT_EQ(sobj.rgw_fh->get_size(), 16UL);
689 // do it
690 memset(buf, 0, 256);
691 if (verbose) {
692 std::cout << "reading 0,256 " << sobj.rgw_fh->relative_object_name()
693 << std::endl;
694 }
695 rc = rgw_read(fs, sobj.fh, 0, 256, &nread, buf, RGW_READ_FLAG_NONE);
696 ASSERT_EQ(rc, 0);
697 if (verbose) {
698 std::cout << "\tread back from " << sobj.name
699 << " : \"" << buf << "\""
700 << std::endl;
701 }
702 }
703 }
704 }
705 }
706}
707
708TEST(LibRGW, READF_SETUP1)
709{
710 struct stat st;
711 if (do_dirs1) {
712 if (do_create) {
713 if ((! stat(readf_out_name.c_str(), &st)) &&
714 (S_ISREG(st.st_mode)) &&
715 (st.st_size == 6291456))
716 return;
717 ofstream of;
718 of.open(readf_out_name, ios::out|ios::app|ios::binary);
719 for (int ix1 = 0; ix1 < 6; ++ix1) {
720 for (int ix2 = 0; ix2 < 1024*1024; ++ix2) {
721 of << ix1;
722 }
723 }
724 }
725 }
726}
727
728TEST(LibRGW, READF_DIRS1) {
729 if (do_dirs1) {
730 if (do_readf) {
731 obj_rec fobj{readf_name, nullptr, dirs1_b.fh, nullptr};
732
733 int rc = rgw_lookup(fs, dirs1_b.fh, fobj.name.c_str(), &fobj.fh,
eafe8130 734 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
735 ASSERT_EQ(rc, 0);
736 ASSERT_NE(fobj.fh, nullptr);
737 fobj.sync();
738
739 ofstream of;
740 of.open(readf_out_name, ios::out|ios::app|ios::binary);
741 int bufsz = 1024 * 1024 * sizeof(char);
11fdf7f2 742 auto buffer = std::make_unique<char[]>(bufsz);
7c673cae
FG
743
744 uint64_t offset = 0;
745 uint64_t length = bufsz;
746 for (int ix = 0; ix < 6; ++ix) {
747 size_t nread = 0;
224ce89b
WB
748 memset(buffer.get(), 0, length); // XXX
749 rc = rgw_read(fs, fobj.fh, offset, length, &nread, buffer.get(),
7c673cae
FG
750 RGW_READ_FLAG_NONE);
751 ASSERT_EQ(rc, 0);
752 ASSERT_EQ(nread, length);
224ce89b 753 of.write(buffer.get(), length);
7c673cae
FG
754 offset += nread;
755 }
756 of.close();
7c673cae
FG
757 rgw_fh_rele(fs, fobj.fh, 0 /* flags */);
758 }
759 }
760}
761
762TEST(LibRGW, WRITEF_DIRS1) {
763 if (do_dirs1) {
764 if (do_writef) {
765 int rc;
766 ifstream ifs;
767 ifs.open(readf_out_name, ios::out|ios::app|ios::binary);
768 ASSERT_TRUE(ifs.is_open());
769
770 obj_rec fobj{writef_name, nullptr, dirs1_b.fh, nullptr};
771
772 (void) rgw_lookup(fs, fobj.parent_fh, fobj.name.c_str(), &fobj.fh,
eafe8130 773 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
774 if (! fobj.fh) {
775 if (do_create) {
776 /* make a new file object (the hard way) */
777 rc = rgw_lookup(fs, fobj.parent_fh, fobj.name.c_str(), &fobj.fh,
eafe8130 778 nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
7c673cae
FG
779 ASSERT_EQ(rc, 0);
780 }
781 }
782 ASSERT_NE(fobj.fh, nullptr);
783 fobj.sync();
784
785 /* begin write transaction */
786 rc = rgw_open(fs, fobj.fh, 0 /* posix flags */, 0 /* flags */);
787 ASSERT_EQ(rc, 0);
788 ASSERT_TRUE(fobj.rgw_fh->is_open());
789
790 int bufsz = 1024 * 1024 * sizeof(char);
791 char *buffer = (char*) malloc(bufsz);
792
793 uint64_t offset = 0;
794 uint64_t length = bufsz;
795 for (int ix = 0; ix < 6; ++ix) {
796 ASSERT_TRUE(ifs.good());
797 ifs.read(buffer, bufsz);
798 size_t nwritten = 0;
799 string str;
800 str.assign(buffer, 4);
801 if (verbose) {
802 std::cout << "read and writing " << length << " bytes"
803 << " from " << readf_out_name
804 << " at offset " << offset
805 << " (" << str << "... [first 4 chars])"
806 << std::endl;
807 }
808 char* leakbuf = (char*) malloc(bufsz);
809 memcpy(leakbuf, buffer, length);
810 rc = rgw_write(fs, fobj.fh, offset, length, &nwritten, leakbuf,
811 RGW_WRITE_FLAG_NONE);
812 ASSERT_EQ(rc, 0);
813 ASSERT_EQ(nwritten, length);
814 offset += length;
815 }
816
817 /* commit write transaction */
818 rc = rgw_close(fs, fobj.fh, RGW_CLOSE_FLAG_NONE);
819 ASSERT_EQ(rc, 0);
820
821 ifs.close();
822 free(buffer);
823 rgw_fh_rele(fs, fobj.fh, 0 /* flags */);
824 }
825 }
826}
827
828TEST(LibRGW, RELEASE_DIRS1) {
829 if (do_dirs1) {
830 /* force release of handles for children of dirs1--force subsequent
831 * checks to reload them from the cluster.
832 *
833 * while doing this, verify handle cleanup and correct LRU state
834 * (not reachable)
835 */
836 int rc;
837 for (auto& dirs_rec : dirs_vec) {
838 for (auto& obj : get<1>(dirs_rec)) {
839 if (verbose) {
840 std::cout << "release " << obj.name
841 << " type: " << obj.rgw_fh->stype()
842 << " refs: " << obj.rgw_fh->get_refcnt()
843 << std::endl;
844 }
845 ASSERT_EQ(obj.rgw_fh->get_refcnt(), 2UL);
846 rc = rgw_fh_rele(fs, obj.fh, 0 /* flags */);
847 ASSERT_EQ(rc, 0);
848 ASSERT_EQ(obj.rgw_fh->get_refcnt(), 1UL);
849 /* try-discard handle */
850 /* clear obj_rec vec */
851 }
852 }
853 }
854}
855
856extern "C" {
857 static bool r1_cb(const char* name, void *arg, uint64_t offset,
eafe8130 858 struct stat* st, uint32_t st_mask,
7c673cae
FG
859 uint32_t flags) {
860 struct rgw_file_handle* parent_fh
861 = static_cast<struct rgw_file_handle*>(arg);
862 RGWFileHandle* rgw_fh = get_rgwfh(parent_fh);
863 lsubdout(cct, rgw, 10) << __func__
864 << " bucket=" << rgw_fh->bucket_name()
865 << " dir=" << rgw_fh->full_object_name()
866 << " called back name=" << name
867 << " flags=" << flags
868 << dendl;
869 string name_str{name};
870 if (! ((name_str == ".") ||
871 (name_str == ".."))) {
872 obj_stack.push(
873 obj_rec{std::move(name_str), nullptr, parent_fh, nullptr});
874 }
875 return true; /* XXX */
876 }
877}
878
879TEST(LibRGW, HIER1) {
880 if (do_hier1) {
881 int rc;
882 obj_stack.push(
883 obj_rec{bucket_name, nullptr, nullptr, nullptr});
884 while (! obj_stack.empty()) {
885 auto& elt = obj_stack.top();
886 if (! elt.fh) {
887 struct rgw_file_handle* parent_fh = elt.parent_fh
888 ? elt.parent_fh : fs->root_fh;
889 RGWFileHandle* pfh = get_rgwfh(parent_fh);
890 rgw::ignore(pfh);
891 lsubdout(cct, rgw, 10)
892 << "rgw_lookup:"
893 << " parent object_name()=" << pfh->object_name()
894 << " parent full_object_name()=" << pfh->full_object_name()
895 << " elt.name=" << elt.name
896 << dendl;
897 rc = rgw_lookup(fs, parent_fh, elt.name.c_str(), &elt.fh,
eafe8130 898 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
899 ASSERT_EQ(rc, 0);
900 // XXXX
901 RGWFileHandle* efh = get_rgwfh(elt.fh);
902 rgw::ignore(efh);
903 lsubdout(cct, rgw, 10)
904 << "rgw_lookup result:"
905 << " elt object_name()=" << efh->object_name()
906 << " elt full_object_name()=" << efh->full_object_name()
907 << " elt.name=" << elt.name
908 << dendl;
909
910 ASSERT_NE(elt.fh, nullptr);
911 elt.rgw_fh = get_rgwfh(elt.fh);
912 elt.parent_fh = elt.rgw_fh->get_parent()->get_fh();
913 ASSERT_EQ(elt.parent_fh, parent_fh);
914 continue;
915 } else {
916 // we have a handle in some state in top position
917 switch(elt.fh->fh_type) {
918 case RGW_FS_TYPE_DIRECTORY:
919 if (! elt.state.readdir) {
920 // descending
921 uint64_t offset = 0;
922 bool eof; // XXX
923 lsubdout(cct, rgw, 10)
924 << "readdir in"
925 << " bucket: " << elt.rgw_fh->bucket_name()
926 << " object_name: " << elt.rgw_fh->object_name()
927 << " full_name: " << elt.rgw_fh->full_object_name()
928 << dendl;
929 rc = rgw_readdir(fs, elt.fh, &offset, r1_cb, elt.fh, &eof,
930 RGW_READDIR_FLAG_DOTDOT);
931 elt.state.readdir = true;
932 ASSERT_EQ(rc, 0);
933 // ASSERT_TRUE(eof); // XXXX working incorrectly w/single readdir
934 } else {
935 // ascending
936 std::cout << elt << std::endl;
937 cleanup_queue.push_back(elt);
938 obj_stack.pop();
939 }
940 break;
941 case RGW_FS_TYPE_FILE:
942 // ascending
943 std::cout << elt << std::endl;
944 cleanup_queue.push_back(elt);
945 obj_stack.pop();
946 break;
947 default:
11fdf7f2 948 ceph_abort();
7c673cae
FG
949 };
950 }
951 }
952 }
953}
954
955TEST(LibRGW, MARKER1_SETUP_BUCKET) {
956 /* "large" directory enumeration test. this one deals only with
957 * file objects */
958 if (do_marker1) {
959 struct stat st;
960 int ret;
961
962 st.st_uid = owner_uid;
963 st.st_gid = owner_gid;
964 st.st_mode = 755;
965
966 if (do_create) {
967 ret = rgw_mkdir(fs, bucket_fh, marker_dir.c_str(), &st, create_mask,
968 &marker_fh, RGW_MKDIR_FLAG_NONE);
969 } else {
970 ret = rgw_lookup(fs, bucket_fh, marker_dir.c_str(), &marker_fh,
eafe8130 971 nullptr, 0, RGW_LOOKUP_FLAG_NONE);
7c673cae
FG
972 }
973 ASSERT_EQ(ret, 0);
974 }
975}
976
977TEST(LibRGW, MARKER1_SETUP_OBJECTS)
978{
979 /* "large" directory enumeration test. this one deals only with
980 * file objects */
981 if (do_marker1 && do_create) {
982 int ret;
983
984 for (int ix = 0; ix < marker_nobjs; ++ix) {
985 std::string object_name("f_");
986 object_name += to_string(ix);
987 obj_rec obj{object_name, nullptr, marker_fh, nullptr};
988 // lookup object--all operations are by handle
989 ret = rgw_lookup(fs, marker_fh, obj.name.c_str(), &obj.fh,
eafe8130 990 nullptr, 0, RGW_LOOKUP_FLAG_CREATE);
7c673cae
FG
991 ASSERT_EQ(ret, 0);
992 obj.rgw_fh = get_rgwfh(obj.fh);
993 // open object--open transaction
994 ret = rgw_open(fs, obj.fh, 0 /* posix flags */, RGW_OPEN_FLAG_NONE);
995 ASSERT_EQ(ret, 0);
996 ASSERT_TRUE(obj.rgw_fh->is_open());
997 // unstable write data
998 size_t nbytes;
999 string data("data for ");
1000 data += object_name;
1001 int ret = rgw_write(fs, obj.fh, 0, data.length(), &nbytes,
1002 (void*) data.c_str(), RGW_WRITE_FLAG_NONE);
1003 ASSERT_EQ(ret, 0);
1004 ASSERT_EQ(nbytes, data.length());
1005 // commit transaction (write on close)
1006 ret = rgw_close(fs, obj.fh, 0 /* flags */);
1007 ASSERT_EQ(ret, 0);
1008 // save for cleanup
1009 marker_objs.push_back(obj);
1010 }
1011 }
1012}
1013
1014extern "C" {
1015 static bool r2_cb(const char* name, void *arg, uint64_t offset,
eafe8130 1016 struct stat* st, uint32_t st_mask,
7c673cae
FG
1017 uint32_t flags) {
1018 dirent_vec& dvec =
1019 *(static_cast<dirent_vec*>(arg));
1020 lsubdout(cct, rgw, 10) << __func__
1021 << " bucket=" << bucket_name
1022 << " dir=" << marker_dir
1023 << " iv count=" << dvec.count
1024 << " called back name=" << name
1025 << " flags=" << flags
1026 << dendl;
1027 string name_str{name};
1028 if (! ((name_str == ".") ||
1029 (name_str == ".."))) {
1030 dvec.obj_names.push_back(dirent_t{std::move(name_str), offset});
1031 }
1032 return true; /* XXX */
1033 }
1034}
1035
1036TEST(LibRGW, MARKER1_READDIR)
1037{
1038 if (do_marker1) {
1039 using std::get;
1040
1041 dirent_vec dvec;
1042 uint64_t offset = 0;
1043 bool eof = false;
1044
1045 /* because RGWReaddirRequest::default_max is 1000 (XXX make
1046 * configurable?) and marker_nobjs is 5*1024, the number
1047 * of required rgw_readdir operations N should be
1048 * marker_nobjs/1000 < N < marker_nobjs/1000+1, i.e., 6 when
1049 * marker_nobjs==5*1024 */
1050 uint32_t max_iterations = marker_nobjs/1000+1;
1051
1052 do {
1053 ASSERT_TRUE(dvec.count <= max_iterations);
1054 int ret = rgw_readdir(fs, marker_fh, &offset, r2_cb, &dvec, &eof,
1055 RGW_READDIR_FLAG_DOTDOT);
1056 ASSERT_EQ(ret, 0);
1057 ASSERT_EQ(offset, get<1>(dvec.obj_names.back())); // cookie check
1058 ++dvec.count;
1059 } while(!eof);
1060 std::cout << "Read " << dvec.obj_names.size() << " objects in "
1061 << marker_dir.c_str() << std::endl;
1062 }
1063}
1064
1065TEST(LibRGW, MARKER1_OBJ_CLEANUP)
1066{
1067 int rc;
1068 for (auto& obj : marker_objs) {
1069 if (obj.fh) {
1070 if (do_delete) {
1071 if (verbose) {
1072 std::cout << "unlinking: " << bucket_name << ":" << obj.name
1073 << std::endl;
1074 }
1075 rc = rgw_unlink(fs, marker_fh, obj.name.c_str(), RGW_UNLINK_FLAG_NONE);
1076 }
1077 rc = rgw_fh_rele(fs, obj.fh, 0 /* flags */);
1078 ASSERT_EQ(rc, 0);
1079 }
1080 }
1081 marker_objs.clear();
1082}
1083
1084TEST(LibRGW, CLEANUP) {
1085 int rc;
1086
1087 if (do_marker1) {
1088 cleanup_queue.push_back(
1089 obj_rec{bucket_name, bucket_fh, fs->root_fh, get_rgwfh(fs->root_fh)});
1090 }
1091
1092 for (auto& elt : cleanup_queue) {
1093 if (elt.fh) {
1094 rc = rgw_fh_rele(fs, elt.fh, 0 /* flags */);
1095 ASSERT_EQ(rc, 0);
1096 }
1097 }
1098 cleanup_queue.clear();
1099}
1100
1101TEST(LibRGW, UMOUNT) {
1102 if (! fs)
1103 return;
1104
1105 int ret = rgw_umount(fs, RGW_UMOUNT_FLAG_NONE);
1106 ASSERT_EQ(ret, 0);
1107}
1108
1109TEST(LibRGW, SHUTDOWN) {
1110 librgw_shutdown(rgw_h);
1111}
1112
1113int main(int argc, char *argv[])
1114{
1115 char *v{nullptr};
1116 string val;
1117 vector<const char*> args;
1118
1119 argv_to_vec(argc, const_cast<const char**>(argv), args);
1120 env_to_vec(args);
1121
1122 v = getenv("AWS_ACCESS_KEY_ID");
1123 if (v) {
1124 access_key = v;
1125 }
1126
1127 v = getenv("AWS_SECRET_ACCESS_KEY");
1128 if (v) {
1129 secret_key = v;
1130 }
1131
1132 for (auto arg_iter = args.begin(); arg_iter != args.end();) {
1133 if (ceph_argparse_witharg(args, arg_iter, &val, "--access",
1134 (char*) nullptr)) {
1135 access_key = val;
1136 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--secret",
1137 (char*) nullptr)) {
1138 secret_key = val;
1139 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--userid",
1140 (char*) nullptr)) {
1141 userid = val;
1142 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--bn",
1143 (char*) nullptr)) {
1144 bucket_name = val;
1145 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--uid",
1146 (char*) nullptr)) {
1147 owner_uid = std::stoi(val);
1148 } else if (ceph_argparse_witharg(args, arg_iter, &val, "--gid",
1149 (char*) nullptr)) {
1150 owner_gid = std::stoi(val);
1151 } else if (ceph_argparse_flag(args, arg_iter, "--hier1",
1152 (char*) nullptr)) {
1153 do_hier1 = true;
1154 } else if (ceph_argparse_flag(args, arg_iter, "--dirs1",
1155 (char*) nullptr)) {
1156 do_dirs1 = true;
1157 } else if (ceph_argparse_flag(args, arg_iter, "--marker1",
1158 (char*) nullptr)) {
1159 do_marker1 = true;
1160 } else if (ceph_argparse_flag(args, arg_iter, "--setattr",
1161 (char*) nullptr)) {
1162 do_setattr = true;
1163 } else if (ceph_argparse_flag(args, arg_iter, "--create",
1164 (char*) nullptr)) {
1165 do_create = true;
1166 } else if (ceph_argparse_flag(args, arg_iter, "--delete",
1167 (char*) nullptr)) {
1168 do_delete = true;
1169 } else if (ceph_argparse_flag(args, arg_iter, "--rename",
1170 (char*) nullptr)) {
1171 do_rename = true;
1172 } else if (ceph_argparse_flag(args, arg_iter, "--readf",
1173 (char*) nullptr)) {
1174 do_readf = true;
1175 } else if (ceph_argparse_flag(args, arg_iter, "--writef",
1176 (char*) nullptr)) {
1177 do_writef = true;
1178 } else if (ceph_argparse_flag(args, arg_iter, "--verbose",
1179 (char*) nullptr)) {
1180 verbose = true;
1181 } else {
1182 ++arg_iter;
1183 }
1184 }
1185
11fdf7f2 1186 /* don't accidentally run as anonymous */
7c673cae
FG
1187 if ((access_key == "") ||
1188 (secret_key == "")) {
1189 std::cout << argv[0] << " no AWS credentials, exiting" << std::endl;
1190 return EPERM;
1191 }
1192
1193 saved_args.argc = argc;
1194 saved_args.argv = argv;
1195
1196 ::testing::InitGoogleTest(&argc, argv);
1197 return RUN_ALL_TESTS();
1198}