]>
git.proxmox.com Git - ceph.git/blob - 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
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2015 Red Hat, Inc.
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.
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
26 #include "gtest/gtest.h"
27 #include "common/ceph_argparse.h"
28 #include "common/debug.h"
29 #include "include/ceph_assert.h"
31 #define dout_subsys ceph_subsys_rgw
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;
48 uint32_t owner_uid
= 867;
49 uint32_t owner_gid
= 5309;
51 uint32_t magic_uid
= 1701;
52 uint32_t magic_gid
= 9876;
54 uint32_t create_mask
= RGW_SETATTR_UID
| RGW_SETATTR_GID
| RGW_SETATTR_MODE
;
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"};
69 struct rgw_file_handle
* fh
;
70 struct rgw_file_handle
* parent_fh
;
71 RGWFileHandle
* rgw_fh
; // alias into fh
75 state() : readdir(false) {}
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
),
90 rgw_fh
= get_rgwfh(fh
);
93 friend ostream
& operator<<(ostream
& os
, const obj_rec
& rec
);
96 ostream
& operator<<(ostream
& os
, const obj_rec
& rec
)
98 RGWFileHandle
* rgw_fh
= rec
.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() << "): "
108 std::stack
<obj_rec
> obj_stack
;
109 std::deque
<obj_rec
> cleanup_queue
;
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
;
120 const struct stat
& st
;
122 obj_rec_st(const obj_rec
& _obj
, const struct stat
& _st
)
123 : obj(_obj
), st(_st
) {}
126 ostream
& operator<<(ostream
& os
, const obj_rec_st
& rec
)
128 RGWFileHandle
* rgw_fh
= rec
.obj
.rgw_fh
;
130 const char* type
= rgw_fh
->is_dir() ? "DIR " : "FILE ";
131 os
<< rgw_fh
->full_object_name()
132 << " (" << rgw_fh
->object_name() << "): "
134 const struct stat
& st
= rec
.st
;
135 switch(uint8_t(rgw_fh
->is_dir())) {
137 os
<< " mode: " << st
.st_mode
;
138 os
<< " nlinks: " << st
.st_nlink
;
142 os
<< " mode: " << st
.st_mode
;
143 os
<< " size: " << st
.st_size
;
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;
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
;
168 using dirent_t
= std::tuple
<std::string
, uint64_t>;
171 std::vector
<dirent_t
> obj_names
;
173 dirent_vec() : count(0) {}
176 obj_rec dirs1_b
{dirs1_bucket_name
, nullptr, nullptr, nullptr};
178 dirs1_vec renames_vec
;
187 int ret
= librgw_create(&rgw_h
, saved_args
.argc
, saved_args
.argv
);
189 ASSERT_NE(rgw_h
, nullptr);
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
);
198 ASSERT_NE(fs
, nullptr);
200 auto& root_fh
= static_cast<RGWLibFS
*>(fs
->fs_private
)->get_fh();
201 ASSERT_EQ(root_fh
.get_name(), "/");
203 ret
= rgw_umount(fs
, RGW_UMOUNT_FLAG_NONE
);
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
);
211 ASSERT_NE(fs
, nullptr);
213 cct
= static_cast<RGWLibFS
*>(fs
->fs_private
)->get_context();
216 TEST(LibRGW
, SETUP_HIER1
)
219 (void) rgw_lookup(fs
, fs
->root_fh
, bucket_name
.c_str(), &bucket_fh
,
220 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
225 st
.st_uid
= owner_uid
;
226 st
.st_gid
= owner_gid
;
229 int rc
= rgw_mkdir(fs
, fs
->root_fh
, bucket_name
.c_str(), &st
,
230 create_mask
, &bucket_fh
, RGW_MKDIR_FLAG_NONE
);
235 ASSERT_NE(bucket_fh
, nullptr);
238 /* create objects directly */
239 std::vector
<std::string
> obj_names
=
245 "foo/bar/baz/hungry",
246 "foo/bar/baz/hungry/",
249 "foo/bar/baz/sasquatch",
250 "foo/bar/baz/sasquatch/",
251 "foo/bar/baz/frobozz"};
253 buffer::list bl
; // empty object
254 RGWLibFS
*fs_private
= static_cast<RGWLibFS
*>(fs
->fs_private
);
256 for (const auto& obj_name
: obj_names
) {
258 std::cout
<< "creating: " << bucket_name
<< ":" << obj_name
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();
273 TEST(LibRGW
, SETUP_DIRS1
) {
278 st
.st_uid
= owner_uid
;
279 st
.st_gid
= owner_gid
;
282 dirs1_b
.parent_fh
= fs
->root_fh
;
284 (void) rgw_lookup(fs
, dirs1_b
.parent_fh
, dirs1_bucket_name
.c_str(),
285 &dirs1_b
.fh
, nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
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
);
293 /* no top-level dir and can't create it--skip remaining tests */
299 /* make top-level dirs */
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};
308 (void) rgw_lookup(fs
, dir
.parent_fh
, dir
.name
.c_str(), &dir
.fh
,
309 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
312 rc
= rgw_mkdir(fs
, dir
.parent_fh
, dir
.name
.c_str(), &st
, create_mask
,
313 &dir
.fh
, RGW_MKDIR_FLAG_NONE
);
318 ASSERT_NE(dir
.fh
, nullptr);
320 ASSERT_NE(dir
.rgw_fh
, nullptr);
321 ASSERT_TRUE(dir
.rgw_fh
->is_dir());
324 for (f_ix
= 0; f_ix
< n_dirs1_objs
; ++f_ix
) {
326 std::string sdname
{"sdir_"};
327 sdname
+= to_string(f_ix
);
328 obj_rec sdir
{sdname
, nullptr, dir
.fh
, nullptr};
330 (void) rgw_lookup(fs
, sdir
.parent_fh
, sdir
.name
.c_str(), &sdir
.fh
,
331 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
335 rc
= rgw_mkdir(fs
, sdir
.parent_fh
, sdir
.name
.c_str(), &st
,
336 create_mask
, &sdir
.fh
, RGW_MKDIR_FLAG_NONE
);
341 ASSERT_NE(sdir
.fh
, nullptr); // suppress !lookup && !create case
344 ASSERT_TRUE(sdir
.rgw_fh
->is_dir());
345 ovec
.push_back(sdir
);
348 std::string sfname
{"sfile_"};
350 sfname
+= to_string(f_ix
);
351 obj_rec sf
{sfname
, nullptr, dir
.fh
, nullptr};
353 (void) rgw_lookup(fs
, sf
.parent_fh
, sf
.name
.c_str(), &sf
.fh
,
354 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
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
);
363 ASSERT_TRUE(sf
.rgw_fh
->is_file());
365 /* because we made it the hard way, fixup attributes */
367 st
.st_uid
= owner_uid
;
368 st
.st_gid
= owner_gid
;
370 sf
.rgw_fh
->create_stat(&st
, create_mask
);
373 rc
= rgw_open(fs
, sf
.fh
, 0 /* posix flags */, 0 /* flags */);
375 ASSERT_TRUE(sf
.rgw_fh
->is_open());
376 /* stage seq write */
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
);
382 ASSERT_EQ(nbytes
, data
.length());
383 /* commit write transaction */
384 rc
= rgw_close(fs
, sf
.fh
, 0 /* flags */);
389 ASSERT_TRUE(sf
.rgw_fh
->is_file());
395 dirs_vec
.push_back(dirs1_rec
{dir
, ovec
});
397 } /* dirs1 top-level !exist */
400 TEST(LibRGW
, SETATTR
) {
407 st
.st_uid
= owner_uid
;
408 st
.st_gid
= owner_gid
;
411 std::string dname
{"dir_0"};
412 obj_rec dir
{dname
, nullptr, dirs1_b
.fh
, nullptr};
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
);
418 ASSERT_NE(dir
.fh
, nullptr);
420 ASSERT_NE(dir
.rgw_fh
, nullptr);
421 ASSERT_TRUE(dir
.rgw_fh
->is_dir());
424 std::string sfname
{"setattr_file_0"};
425 obj_rec sf
{sfname
, nullptr, dir
.fh
, nullptr};
427 (void) rgw_lookup(fs
, sf
.parent_fh
, sf
.name
.c_str(), &sf
.fh
,
428 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
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
);
436 ASSERT_TRUE(sf
.rgw_fh
->is_file());
438 /* because we made it the hard way, fixup attributes */
439 st
.st_uid
= owner_uid
;
440 st
.st_gid
= owner_gid
;
442 sf
.rgw_fh
->create_stat(&st
, create_mask
);
445 rc
= rgw_open(fs
, sf
.fh
, 0 /* posix flags */, 0 /* flags */);
447 ASSERT_TRUE(sf
.rgw_fh
->is_open());
448 /* stage seq write */
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
);
454 ASSERT_EQ(nbytes
, data
.length());
455 /* commit write transaction */
456 rc
= rgw_close(fs
, sf
.fh
, 0 /* flags */);
460 ASSERT_TRUE(sf
.rgw_fh
->is_file());
463 /* sf MUST now be materialized--now change it's attributes */
464 st
.st_uid
= magic_uid
;
465 st
.st_gid
= magic_gid
;
467 rc
= rgw_setattr(fs
, sf
.fh
, &st
, create_mask
, RGW_SETATTR_FLAG_NONE
);
470 /* force evict--subsequent lookups must reload */
471 static_cast<RGWLibFS
*>(fs
->fs_private
)->release_evict(sf
.rgw_fh
);
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
);
479 ASSERT_NE(sf
.fh
, nullptr);
481 memset(&st
, 0, sizeof(struct stat
)); /* nothing up my sleeve... */
483 rc
= rgw_getattr(fs
, sf
.fh
, &st
, RGW_GETATTR_FLAG_NONE
);
486 ASSERT_EQ(st
.st_uid
, magic_uid
);
487 ASSERT_EQ(st
.st_gid
, magic_gid
);
489 /* release 1 ref on sf */
490 rgw_fh_rele(fs
, sf
.fh
, RGW_FH_RELE_FLAG_NONE
);
492 /* release 1 ref on dir */
493 rgw_fh_rele(fs
, dir
.fh
, RGW_FH_RELE_FLAG_NONE
);
498 TEST(LibRGW
, RGW_CREATE_DIRS1
) {
499 /* verify rgw_create (create [empty] file objects the easy way) */
505 st
.st_uid
= owner_uid
;
506 st
.st_gid
= owner_gid
;
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
);
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
);
528 TEST(LibRGW
, RGW_SETUP_RENAME1
) {
529 /* verify rgw_create (create [empty] file objects the easy way) */
535 st
.st_uid
= owner_uid
;
536 st
.st_gid
= owner_gid
;
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
);
547 int rc
= rgw_mkdir(fs
, fs
->root_fh
, brec
.name
.c_str(), &st
,
548 create_mask
, &brec
.fh
, RGW_MKDIR_FLAG_NONE
);
552 ASSERT_NE(brec
.fh
, nullptr);
555 st
.st_mode
= 644; /* file mask */
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
);
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
);
571 renames_vec
.push_back(dirs1_rec
{brec
, ovec
});
577 TEST(LibRGW
, RGW_INTRABUCKET_RENAME1
) {
578 /* rgw_rename a file within a bucket */
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"};
585 std::cout
<< "rename file " << src_obj
.name
<< " to "
586 << rfname
<< " (bucket " << bdir0
.name
<< ")"
589 rc
= rgw_rename(fs
, bdir0
.fh
, src_obj
.name
.c_str(), bdir0
.fh
,
590 rfname
.c_str(), RGW_RENAME_FLAG_NONE
);
595 TEST(LibRGW
, RGW_CROSSBUCKET_RENAME1
) {
596 /* rgw_rename a file within a bucket */
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"};
604 std::cout
<< "rename file " << src_obj
.name
605 << " (bucket " << bdir0
.name
<< ") to "
606 << rfname
<< " (bucket " << bdir1
.name
<< ")"
609 rc
= rgw_rename(fs
, bdir0
.fh
, src_obj
.name
.c_str(), bdir1
.fh
,
610 rfname
.c_str(), RGW_RENAME_FLAG_NONE
);
615 #if 0 /* XXX inconsistent failure here */
616 TEST(LibRGW
, BAD_DELETES_DIRS1
) {
620 if (dirs_vec
.size() == 0) {
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
);
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 */
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
);
647 TEST(LibRGW
, GETATTR_DIRS1
)
652 for (auto& dirs_rec
: dirs_vec
) {
653 obj_rec
& dir
= get
<0>(dirs_rec
);
655 std::cout
<< "scanning objects in "
656 << dir
.rgw_fh
->full_object_name()
659 for (auto& sobj
: get
<1>(dirs_rec
)) {
660 rc
= rgw_getattr(fs
, sobj
.fh
, &st
, RGW_GETATTR_FLAG_NONE
);
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
));
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
));
671 /* validate Unix owners */
672 ASSERT_EQ(st
.st_uid
, owner_uid
);
673 ASSERT_EQ(st
.st_gid
, owner_gid
);
675 obj_rec_st rec_st
{sobj
, st
};
685 TEST(LibRGW
, READ_DIRS1
)
691 for (auto& dirs_rec
: dirs_vec
) {
692 obj_rec
& dir
= get
<0>(dirs_rec
);
694 std::cout
<< "read back objects in "
695 << dir
.rgw_fh
->full_object_name()
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);
709 std::cout
<< "reading 0,256 " << sobj
.rgw_fh
->relative_object_name()
712 rc
= rgw_read(fs
, sobj
.fh
, 0, 256, &nread
, buf
, RGW_READ_FLAG_NONE
);
715 std::cout
<< "\tread back from " << sobj
.name
716 << " : \"" << buf
<< "\""
725 TEST(LibRGW
, READF_SETUP1
)
730 if ((! stat(readf_out_name
.c_str(), &st
)) &&
731 (S_ISREG(st
.st_mode
)) &&
732 (st
.st_size
== 6291456))
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
) {
745 TEST(LibRGW
, READF_DIRS1
) {
748 obj_rec fobj
{readf_name
, nullptr, dirs1_b
.fh
, nullptr};
750 int rc
= rgw_lookup(fs
, dirs1_b
.fh
, fobj
.name
.c_str(), &fobj
.fh
,
751 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
753 ASSERT_NE(fobj
.fh
, nullptr);
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
);
762 uint64_t length
= bufsz
;
763 for (int ix
= 0; ix
< 6; ++ix
) {
765 memset(buffer
.get(), 0, length
); // XXX
766 rc
= rgw_read(fs
, fobj
.fh
, offset
, length
, &nread
, buffer
.get(),
769 ASSERT_EQ(nread
, length
);
770 of
.write(buffer
.get(), length
);
774 rgw_fh_rele(fs
, fobj
.fh
, 0 /* flags */);
779 TEST(LibRGW
, WRITEF_DIRS1
) {
784 ifs
.open(readf_out_name
, ios::out
|ios::app
|ios::binary
);
785 ASSERT_TRUE(ifs
.is_open());
787 obj_rec fobj
{writef_name
, nullptr, dirs1_b
.fh
, nullptr};
789 (void) rgw_lookup(fs
, fobj
.parent_fh
, fobj
.name
.c_str(), &fobj
.fh
,
790 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
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
);
799 ASSERT_NE(fobj
.fh
, nullptr);
802 /* begin write transaction */
803 rc
= rgw_open(fs
, fobj
.fh
, 0 /* posix flags */, 0 /* flags */);
805 ASSERT_TRUE(fobj
.rgw_fh
->is_open());
807 int bufsz
= 1024 * 1024 * sizeof(char);
808 char *buffer
= (char*) malloc(bufsz
);
811 uint64_t length
= bufsz
;
812 for (int ix
= 0; ix
< 6; ++ix
) {
813 ASSERT_TRUE(ifs
.good());
814 ifs
.read(buffer
, bufsz
);
817 str
.assign(buffer
, 4);
819 std::cout
<< "read and writing " << length
<< " bytes"
820 << " from " << readf_out_name
821 << " at offset " << offset
822 << " (" << str
<< "... [first 4 chars])"
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
);
830 ASSERT_EQ(nwritten
, length
);
834 /* commit write transaction */
835 rc
= rgw_close(fs
, fobj
.fh
, RGW_CLOSE_FLAG_NONE
);
840 rgw_fh_rele(fs
, fobj
.fh
, 0 /* flags */);
845 TEST(LibRGW
, RELEASE_DIRS1
) {
847 /* force release of handles for children of dirs1--force subsequent
848 * checks to reload them from the cluster.
850 * while doing this, verify handle cleanup and correct LRU state
854 for (auto& dirs_rec
: dirs_vec
) {
855 for (auto& obj
: get
<1>(dirs_rec
)) {
857 std::cout
<< "release " << obj
.name
858 << " type: " << obj
.rgw_fh
->stype()
859 << " refs: " << obj
.rgw_fh
->get_refcnt()
862 ASSERT_EQ(obj
.rgw_fh
->get_refcnt(), 2UL);
863 rc
= rgw_fh_rele(fs
, obj
.fh
, 0 /* flags */);
865 ASSERT_EQ(obj
.rgw_fh
->get_refcnt(), 1UL);
866 /* try-discard handle */
867 /* clear obj_rec vec */
874 static bool r1_cb(const char* name
, void *arg
, uint64_t offset
,
875 struct stat
* st
, uint32_t st_mask
,
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
886 string name_str
{name
};
887 if (! ((name_str
== ".") ||
888 (name_str
== ".."))) {
890 obj_rec
{std::move(name_str
), nullptr, parent_fh
, nullptr});
892 return true; /* XXX */
896 TEST(LibRGW
, HIER1
) {
900 obj_rec
{bucket_name
, nullptr, nullptr, nullptr});
901 while (! obj_stack
.empty()) {
902 auto& elt
= obj_stack
.top();
904 struct rgw_file_handle
* parent_fh
= elt
.parent_fh
905 ? elt
.parent_fh
: fs
->root_fh
;
906 RGWFileHandle
* pfh
= get_rgwfh(parent_fh
);
908 lsubdout(cct
, rgw
, 10)
910 << " parent object_name()=" << pfh
->object_name()
911 << " parent full_object_name()=" << pfh
->full_object_name()
912 << " elt.name=" << elt
.name
914 rc
= rgw_lookup(fs
, parent_fh
, elt
.name
.c_str(), &elt
.fh
,
915 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
918 RGWFileHandle
* efh
= get_rgwfh(elt
.fh
);
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
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
);
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
) {
940 lsubdout(cct
, rgw
, 10)
942 << " bucket: " << elt
.rgw_fh
->bucket_name()
943 << " object_name: " << elt
.rgw_fh
->object_name()
944 << " full_name: " << elt
.rgw_fh
->full_object_name()
946 rc
= rgw_readdir(fs
, elt
.fh
, &offset
, r1_cb
, elt
.fh
, &eof
,
947 RGW_READDIR_FLAG_DOTDOT
);
948 elt
.state
.readdir
= true;
950 // ASSERT_TRUE(eof); // XXXX working incorrectly w/single readdir
953 std::cout
<< elt
<< std::endl
;
954 cleanup_queue
.push_back(elt
);
958 case RGW_FS_TYPE_FILE
:
960 std::cout
<< elt
<< std::endl
;
961 cleanup_queue
.push_back(elt
);
972 TEST(LibRGW
, MARKER1_SETUP_BUCKET
) {
973 /* "large" directory enumeration test. this one deals only with
979 st
.st_uid
= owner_uid
;
980 st
.st_gid
= owner_gid
;
984 ret
= rgw_mkdir(fs
, bucket_fh
, marker_dir
.c_str(), &st
, create_mask
,
985 &marker_fh
, RGW_MKDIR_FLAG_NONE
);
987 ret
= rgw_lookup(fs
, bucket_fh
, marker_dir
.c_str(), &marker_fh
,
988 nullptr, 0, RGW_LOOKUP_FLAG_NONE
);
994 TEST(LibRGW
, MARKER1_SETUP_OBJECTS
)
996 /* "large" directory enumeration test. this one deals only with
998 if (do_marker1
&& do_create
) {
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
);
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
);
1013 ASSERT_TRUE(obj
.rgw_fh
->is_open());
1014 // unstable write data
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
);
1021 ASSERT_EQ(nbytes
, data
.length());
1022 // commit transaction (write on close)
1023 ret
= rgw_close(fs
, obj
.fh
, 0 /* flags */);
1026 marker_objs
.push_back(obj
);
1032 static bool r2_cb(const char* name
, void *arg
, uint64_t offset
,
1033 struct stat
* st
, uint32_t st_mask
,
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
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
});
1049 return true; /* XXX */
1053 TEST(LibRGW
, MARKER1_READDIR
)
1059 uint64_t offset
= 0;
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;
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
);
1074 ASSERT_EQ(offset
, get
<1>(dvec
.obj_names
.back())); // cookie check
1077 std::cout
<< "Read " << dvec
.obj_names
.size() << " objects in "
1078 << marker_dir
.c_str() << std::endl
;
1082 TEST(LibRGW
, MARKER1_OBJ_CLEANUP
)
1085 for (auto& obj
: marker_objs
) {
1089 std::cout
<< "unlinking: " << bucket_name
<< ":" << obj
.name
1092 rc
= rgw_unlink(fs
, marker_fh
, obj
.name
.c_str(), RGW_UNLINK_FLAG_NONE
);
1094 rc
= rgw_fh_rele(fs
, obj
.fh
, 0 /* flags */);
1098 marker_objs
.clear();
1101 TEST(LibRGW
, CLEANUP
) {
1105 cleanup_queue
.push_back(
1106 obj_rec
{bucket_name
, bucket_fh
, fs
->root_fh
, get_rgwfh(fs
->root_fh
)});
1109 for (auto& elt
: cleanup_queue
) {
1111 rc
= rgw_fh_rele(fs
, elt
.fh
, 0 /* flags */);
1115 cleanup_queue
.clear();
1118 TEST(LibRGW
, UMOUNT
) {
1122 int ret
= rgw_umount(fs
, RGW_UMOUNT_FLAG_NONE
);
1126 TEST(LibRGW
, SHUTDOWN
) {
1127 librgw_shutdown(rgw_h
);
1130 int main(int argc
, char *argv
[])
1132 auto args
= argv_to_vec(argc
, argv
);
1135 char* v
= getenv("AWS_ACCESS_KEY_ID");
1140 v
= getenv("AWS_SECRET_ACCESS_KEY");
1146 for (auto arg_iter
= args
.begin(); arg_iter
!= args
.end();) {
1147 if (ceph_argparse_witharg(args
, arg_iter
, &val
, "--access",
1150 } else if (ceph_argparse_witharg(args
, arg_iter
, &val
, "--secret",
1153 } else if (ceph_argparse_witharg(args
, arg_iter
, &val
, "--userid",
1156 } else if (ceph_argparse_witharg(args
, arg_iter
, &val
, "--bn",
1159 } else if (ceph_argparse_witharg(args
, arg_iter
, &val
, "--uid",
1161 owner_uid
= std::stoi(val
);
1162 } else if (ceph_argparse_witharg(args
, arg_iter
, &val
, "--gid",
1164 owner_gid
= std::stoi(val
);
1165 } else if (ceph_argparse_flag(args
, arg_iter
, "--hier1",
1168 } else if (ceph_argparse_flag(args
, arg_iter
, "--dirs1",
1171 } else if (ceph_argparse_flag(args
, arg_iter
, "--marker1",
1174 } else if (ceph_argparse_flag(args
, arg_iter
, "--setattr",
1177 } else if (ceph_argparse_flag(args
, arg_iter
, "--create",
1180 } else if (ceph_argparse_flag(args
, arg_iter
, "--delete",
1183 } else if (ceph_argparse_flag(args
, arg_iter
, "--rename",
1186 } else if (ceph_argparse_flag(args
, arg_iter
, "--readf",
1189 } else if (ceph_argparse_flag(args
, arg_iter
, "--writef",
1192 } else if (ceph_argparse_flag(args
, arg_iter
, "--verbose",
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
;
1207 saved_args
.argc
= argc
;
1208 saved_args
.argv
= argv
;
1210 ::testing::InitGoogleTest(&argc
, argv
);
1211 return RUN_ALL_TESTS();