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