1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
7 #include "include/rados/rgw_file.h"
22 #include <boost/intrusive_ptr.hpp>
23 #include <boost/range/adaptor/reversed.hpp>
24 #include <boost/container/flat_map.hpp>
25 #include <boost/variant.hpp>
26 #include <boost/utility/string_ref.hpp>
27 #include <boost/optional.hpp>
29 #include "include/buffer.h"
30 #include "common/cohort_lru.h"
31 #include "common/ceph_timer.h"
32 #include "rgw_common.h"
36 #include "rgw_token.h"
37 #include "rgw_putobj_processor.h"
38 #include "rgw_aio_throttle.h"
39 #include "rgw_compression.h"
43 * ASSERT_H somehow not defined after all the above (which bring
44 * in common/debug.h [e.g., dout])
46 #include "include/ceph_assert.h"
49 #define RGW_RWXMODE (S_IRWXU | S_IRWXG | S_IRWXO)
51 #define RGW_RWMODE (RGW_RWXMODE & \
52 ~(S_IXUSR | S_IXGRP | S_IXOTH))
58 static inline void ignore(T
&&) {}
61 namespace bi
= boost::intrusive
;
65 class RGWWriteRequest
;
67 static inline bool operator <(const struct timespec
& lhs
,
68 const struct timespec
& rhs
) {
69 if (lhs
.tv_sec
== rhs
.tv_sec
)
70 return lhs
.tv_nsec
< rhs
.tv_nsec
;
72 return lhs
.tv_sec
< rhs
.tv_sec
;
75 static inline bool operator ==(const struct timespec
& lhs
,
76 const struct timespec
& rhs
) {
77 return ((lhs
.tv_sec
== rhs
.tv_sec
) &&
78 (lhs
.tv_nsec
== rhs
.tv_nsec
));
83 * The current 64-bit, non-cryptographic hash used here is intended
84 * for prototyping only.
86 * However, the invariant being prototyped is that objects be
87 * identifiable by their hash components alone. We believe this can
88 * be legitimately implemented using 128-hash values for bucket and
89 * object components, together with a cluster-resident cryptographic
90 * key. Since an MD5 or SHA-1 key is 128 bits and the (fast),
91 * non-cryptographic CityHash128 hash algorithm takes a 128-bit seed,
92 * speculatively we could use that for the final hash computations.
99 static constexpr uint64_t seed
= 8675309;
101 fh_key() : version(0) {}
103 fh_key(const rgw_fh_hk
& _hk
)
104 : fh_hk(_hk
), version(0) {
108 fh_key(const uint64_t bk
, const uint64_t ok
)
114 fh_key(const uint64_t bk
, const char *_o
, const std::string
& _t
)
117 std::string to
= _t
+ ":" + _o
;
118 fh_hk
.object
= XXH64(to
.c_str(), to
.length(), seed
);
121 fh_key(const std::string
& _b
, const std::string
& _o
,
122 const std::string
& _t
/* tenant */)
124 std::string tb
= _t
+ ":" + _b
;
125 std::string to
= _t
+ ":" + _o
;
126 fh_hk
.bucket
= XXH64(tb
.c_str(), tb
.length(), seed
);
127 fh_hk
.object
= XXH64(to
.c_str(), to
.length(), seed
);
130 void encode(buffer::list
& bl
) const {
131 ENCODE_START(2, 1, bl
);
132 encode(fh_hk
.bucket
, bl
);
133 encode(fh_hk
.object
, bl
);
134 encode((uint32_t)2, bl
);
138 void decode(bufferlist::const_iterator
& bl
) {
140 decode(fh_hk
.bucket
, bl
);
141 decode(fh_hk
.object
, bl
);
148 friend std::ostream
& operator<<(std::ostream
&os
, fh_key
const &fhk
);
152 WRITE_CLASS_ENCODER(fh_key
);
154 inline bool operator<(const fh_key
& lhs
, const fh_key
& rhs
)
156 return ((lhs
.fh_hk
.bucket
< rhs
.fh_hk
.bucket
) ||
157 ((lhs
.fh_hk
.bucket
== rhs
.fh_hk
.bucket
) &&
158 (lhs
.fh_hk
.object
< rhs
.fh_hk
.object
)));
161 inline bool operator>(const fh_key
& lhs
, const fh_key
& rhs
)
166 inline bool operator==(const fh_key
& lhs
, const fh_key
& rhs
)
168 return ((lhs
.fh_hk
.bucket
== rhs
.fh_hk
.bucket
) &&
169 (lhs
.fh_hk
.object
== rhs
.fh_hk
.object
));
172 inline bool operator!=(const fh_key
& lhs
, const fh_key
& rhs
)
174 return !(lhs
== rhs
);
177 inline bool operator<=(const fh_key
& lhs
, const fh_key
& rhs
)
179 return (lhs
< rhs
) || (lhs
== rhs
);
182 using boost::variant
;
183 using boost::container::flat_map
;
185 typedef std::tuple
<bool, bool> DecodeAttrsResult
;
187 class RGWFileHandle
: public cohort::lru::Object
189 struct rgw_file_handle fh
;
193 RGWFileHandle
* bucket
;
194 RGWFileHandle
* parent
;
195 /* const */ std::string name
; /* XXX file or bucket name */
196 /* const */ fh_key fhk
;
198 using lock_guard
= std::lock_guard
<std::mutex
>;
199 using unique_lock
= std::unique_lock
<std::mutex
>;
201 /* TODO: keeping just the last marker is sufficient for
202 * nfs-ganesha 2.4.5; in the near future, nfs-ganesha will
203 * be able to hint the name of the next dirent required,
204 * from which we can directly synthesize a RADOS marker.
205 * using marker_cache_t = flat_map<uint64_t, rgw_obj_key>;
212 uint32_t owner_uid
; /* XXX need Unix attr */
213 uint32_t owner_gid
; /* XXX need Unix attr */
215 struct timespec ctime
;
216 struct timespec mtime
;
217 struct timespec atime
;
219 State() : dev(0), size(0), nlink(1), owner_uid(0), owner_gid(0),
220 ctime
{0,0}, mtime
{0,0}, atime
{0,0}, version(0) {}
224 RGWWriteRequest
* write_req
;
225 file() : write_req(nullptr) {}
231 static constexpr uint32_t FLAG_NONE
= 0x0000;
234 rgw_obj_key last_marker
;
235 struct timespec last_readdir
;
237 directory() : flags(FLAG_NONE
), last_readdir
{0,0} {}
241 void advance_mtime(uint32_t flags
= FLAG_NONE
);
243 boost::variant
<file
, directory
> variant_type
;
248 ceph::buffer::list etag
;
249 ceph::buffer::list acls
;
252 const static std::string root_name
;
254 static constexpr uint16_t MAX_DEPTH
= 256;
256 static constexpr uint32_t FLAG_NONE
= 0x0000;
257 static constexpr uint32_t FLAG_OPEN
= 0x0001;
258 static constexpr uint32_t FLAG_ROOT
= 0x0002;
259 static constexpr uint32_t FLAG_CREATE
= 0x0004;
260 static constexpr uint32_t FLAG_CREATING
= 0x0008;
261 static constexpr uint32_t FLAG_SYMBOLIC_LINK
= 0x0009;
262 static constexpr uint32_t FLAG_DIRECTORY
= 0x0010;
263 static constexpr uint32_t FLAG_BUCKET
= 0x0020;
264 static constexpr uint32_t FLAG_LOCK
= 0x0040;
265 static constexpr uint32_t FLAG_DELETED
= 0x0080;
266 static constexpr uint32_t FLAG_UNLINK_THIS
= 0x0100;
267 static constexpr uint32_t FLAG_LOCKED
= 0x0200;
268 static constexpr uint32_t FLAG_STATELESS_OPEN
= 0x0400;
269 static constexpr uint32_t FLAG_EXACT_MATCH
= 0x0800;
270 static constexpr uint32_t FLAG_MOUNT
= 0x1000;
272 #define CREATE_FLAGS(x) \
273 ((x) & ~(RGWFileHandle::FLAG_CREATE|RGWFileHandle::FLAG_LOCK))
275 static constexpr uint32_t RCB_MASK
= \
276 RGW_SETATTR_MTIME
|RGW_SETATTR_CTIME
|RGW_SETATTR_ATIME
|RGW_SETATTR_SIZE
;
278 friend class RGWLibFS
;
281 explicit RGWFileHandle(RGWLibFS
* _fs
)
282 : fs(_fs
), bucket(nullptr), parent(nullptr), variant_type
{directory()},
283 depth(0), flags(FLAG_NONE
)
288 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
289 variant_type
= directory();
291 state
.unix_mode
= RGW_RWXMODE
|S_IFDIR
;
292 /* pointer to self */
293 fh
.fh_private
= this;
296 uint64_t init_fsid(std::string
& uid
) {
297 return XXH64(uid
.c_str(), uid
.length(), fh_key::seed
);
300 void init_rootfs(std::string
& fsid
, const std::string
& object_name
,
303 fh
.fh_hk
.bucket
= XXH64(fsid
.c_str(), fsid
.length(), fh_key::seed
);
304 fh
.fh_hk
.object
= XXH64(object_name
.c_str(), object_name
.length(),
309 state
.dev
= init_fsid(fsid
);
312 flags
|= RGWFileHandle::FLAG_BUCKET
| RGWFileHandle::FLAG_MOUNT
;
316 flags
|= RGWFileHandle::FLAG_ROOT
| RGWFileHandle::FLAG_MOUNT
;
321 RGWFileHandle(RGWLibFS
* _fs
, RGWFileHandle
* _parent
,
322 const fh_key
& _fhk
, std::string
& _name
, uint32_t _flags
)
323 : fs(_fs
), bucket(nullptr), parent(_parent
), name(std::move(_name
)),
324 fhk(_fhk
), flags(_flags
) {
326 if (parent
->is_root()) {
327 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
328 variant_type
= directory();
329 flags
|= FLAG_BUCKET
;
331 bucket
= parent
->is_bucket() ? parent
333 if (flags
& FLAG_DIRECTORY
) {
334 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
335 variant_type
= directory();
336 } else if(flags
& FLAG_SYMBOLIC_LINK
) {
337 fh
.fh_type
= RGW_FS_TYPE_SYMBOLIC_LINK
;
338 variant_type
= file();
340 fh
.fh_type
= RGW_FS_TYPE_FILE
;
341 variant_type
= file();
345 depth
= parent
->depth
+ 1;
347 /* save constant fhk */
348 fh
.fh_hk
= fhk
.fh_hk
; /* XXX redundant in fh_hk */
350 /* inherits parent's fsid */
351 state
.dev
= parent
->state
.dev
;
353 switch (fh
.fh_type
) {
354 case RGW_FS_TYPE_DIRECTORY
:
355 state
.unix_mode
= RGW_RWXMODE
|S_IFDIR
;
356 /* virtual directories are always invalid */
359 case RGW_FS_TYPE_FILE
:
360 state
.unix_mode
= RGW_RWMODE
|S_IFREG
;
362 case RGW_FS_TYPE_SYMBOLIC_LINK
:
363 state
.unix_mode
= RGW_RWMODE
|S_IFLNK
;
369 /* pointer to self */
370 fh
.fh_private
= this;
373 const fh_key
& get_key() const {
377 directory
* get_directory() {
378 return get
<directory
>(&variant_type
);
381 size_t get_size() const { return state
.size
; }
383 const char* stype() {
384 return is_dir() ? "DIR" : "FILE";
387 uint16_t get_depth() const { return depth
; }
389 struct rgw_file_handle
* get_fh() { return &fh
; }
391 RGWLibFS
* get_fs() { return fs
; }
393 RGWFileHandle
* get_parent() { return parent
; }
395 uint32_t get_owner_uid() const { return state
.owner_uid
; }
396 uint32_t get_owner_gid() const { return state
.owner_gid
; }
398 struct timespec
get_ctime() const { return state
.ctime
; }
399 struct timespec
get_mtime() const { return state
.mtime
; }
401 const ceph::buffer::list
& get_etag() const { return etag
; }
402 const ceph::buffer::list
& get_acls() const { return acls
; }
404 void create_stat(struct stat
* st
, uint32_t mask
) {
405 if (mask
& RGW_SETATTR_UID
)
406 state
.owner_uid
= st
->st_uid
;
408 if (mask
& RGW_SETATTR_GID
)
409 state
.owner_gid
= st
->st_gid
;
411 if (mask
& RGW_SETATTR_MODE
) {
412 switch (fh
.fh_type
) {
413 case RGW_FS_TYPE_DIRECTORY
:
414 state
.unix_mode
= st
->st_mode
|S_IFDIR
;
416 case RGW_FS_TYPE_FILE
:
417 state
.unix_mode
= st
->st_mode
|S_IFREG
;
419 case RGW_FS_TYPE_SYMBOLIC_LINK
:
420 state
.unix_mode
= st
->st_mode
|S_IFLNK
;
427 if (mask
& RGW_SETATTR_ATIME
)
428 state
.atime
= st
->st_atim
;
430 if (mask
& RGW_SETATTR_MTIME
) {
431 if (fh
.fh_type
!= RGW_FS_TYPE_DIRECTORY
)
432 state
.mtime
= st
->st_mtim
;
435 if (mask
& RGW_SETATTR_CTIME
)
436 state
.ctime
= st
->st_ctim
;
439 int stat(struct stat
* st
, uint32_t flags
= FLAG_NONE
) {
440 /* partial Unix attrs */
441 memset(st
, 0, sizeof(struct stat
));
442 st
->st_dev
= state
.dev
;
443 st
->st_ino
= fh
.fh_hk
.object
; // XXX
445 st
->st_uid
= state
.owner_uid
;
446 st
->st_gid
= state
.owner_gid
;
448 st
->st_mode
= state
.unix_mode
;
450 switch (fh
.fh_type
) {
451 case RGW_FS_TYPE_DIRECTORY
:
452 /* virtual directories are always invalid */
453 advance_mtime(flags
);
454 st
->st_nlink
= state
.nlink
;
456 case RGW_FS_TYPE_FILE
:
458 st
->st_blksize
= 4096;
459 st
->st_size
= state
.size
;
460 st
->st_blocks
= (state
.size
) / 512;
462 case RGW_FS_TYPE_SYMBOLIC_LINK
:
464 st
->st_blksize
= 4096;
465 st
->st_size
= state
.size
;
466 st
->st_blocks
= (state
.size
) / 512;
472 #ifdef HAVE_STAT_ST_MTIMESPEC_TV_NSEC
473 st
->st_atimespec
= state
.atime
;
474 st
->st_mtimespec
= state
.mtime
;
475 st
->st_ctimespec
= state
.ctime
;
477 st
->st_atim
= state
.atime
;
478 st
->st_mtim
= state
.mtime
;
479 st
->st_ctim
= state
.ctime
;
485 const std::string
& bucket_name() const {
490 return bucket
->object_name();
493 const std::string
& object_name() const { return name
; }
495 std::string
full_object_name(bool omit_bucket
= false) const {
497 std::vector
<const std::string
*> segments
;
499 const RGWFileHandle
* tfh
= this;
500 while (tfh
&& !tfh
->is_root() && !(tfh
->is_bucket() && omit_bucket
)) {
501 segments
.push_back(&tfh
->object_name());
502 reserve
+= (1 + tfh
->object_name().length());
506 path
.reserve(reserve
);
507 for (auto& s
: boost::adaptors::reverse(segments
)) {
511 if (!omit_bucket
&& (path
.front() != '/')) // pretty-print
520 inline std::string
relative_object_name() const {
521 return full_object_name(true /* omit_bucket */);
524 inline std::string
format_child_name(const std::string
& cbasename
,
526 std::string child_name
{relative_object_name()};
527 if ((child_name
.size() > 0) &&
528 (child_name
.back() != '/'))
530 child_name
+= cbasename
;
536 inline std::string
make_key_name(const char *name
) const {
537 std::string key_name
{full_object_name()};
538 if (key_name
.length() > 0)
544 fh_key
make_fhk(const std::string
& name
);
546 void add_marker(uint64_t off
, const rgw_obj_key
& marker
,
549 directory
* d
= get
<directory
>(&variant_type
);
551 unique_lock
guard(mtx
);
552 d
->last_marker
= marker
;
556 const rgw_obj_key
* find_marker(uint64_t off
) const {
559 const directory
* d
= get
<directory
>(&variant_type
);
561 return &d
->last_marker
;
567 int offset_of(const std::string
& name
, int64_t *offset
, uint32_t flags
) {
568 if (unlikely(! is_dir())) {
571 *offset
= XXH64(name
.c_str(), name
.length(), fh_key::seed
);
575 bool is_open() const { return flags
& FLAG_OPEN
; }
576 bool is_root() const { return flags
& FLAG_ROOT
; }
577 bool is_mount() const { return flags
& FLAG_MOUNT
; }
578 bool is_bucket() const { return flags
& FLAG_BUCKET
; }
579 bool is_object() const { return !is_bucket(); }
580 bool is_file() const { return (fh
.fh_type
== RGW_FS_TYPE_FILE
); }
581 bool is_dir() const { return (fh
.fh_type
== RGW_FS_TYPE_DIRECTORY
); }
582 bool is_link() const { return (fh
.fh_type
== RGW_FS_TYPE_SYMBOLIC_LINK
); }
583 bool creating() const { return flags
& FLAG_CREATING
; }
584 bool deleted() const { return flags
& FLAG_DELETED
; }
585 bool stateless_open() const { return flags
& FLAG_STATELESS_OPEN
; }
586 bool has_children() const;
588 int open(uint32_t gsh_flags
) {
589 lock_guard
guard(mtx
);
591 if (gsh_flags
& RGW_OPEN_FLAG_V3
) {
592 flags
|= FLAG_STATELESS_OPEN
;
600 typedef boost::variant
<uint64_t*, const char*> readdir_offset
;
602 int readdir(rgw_readdir_cb rcb
, void *cb_arg
, readdir_offset offset
,
603 bool *eof
, uint32_t flags
);
605 int write(uint64_t off
, size_t len
, size_t *nbytes
, void *buffer
);
607 int commit(uint64_t offset
, uint64_t length
, uint32_t flags
) {
608 /* NFS3 and NFSv4 COMMIT implementation
609 * the current atomic update strategy doesn't actually permit
610 * clients to read-stable until either CLOSE (NFSv4+) or the
611 * expiration of the active write timer (NFS3). In the
612 * interim, the client may send an arbitrary number of COMMIT
613 * operations which must return a success result */
617 int write_finish(uint32_t flags
= FLAG_NONE
);
620 void open_for_create() {
621 lock_guard
guard(mtx
);
622 flags
|= FLAG_CREATING
;
625 void clear_creating() {
626 lock_guard
guard(mtx
);
627 flags
&= ~FLAG_CREATING
;
630 void inc_nlink(const uint64_t n
) {
634 void set_nlink(const uint64_t n
) {
638 void set_size(const size_t size
) {
642 void set_times(const struct timespec
&ts
) {
644 state
.mtime
= state
.ctime
;
645 state
.atime
= state
.ctime
;
648 void set_times(real_time t
) {
649 set_times(real_clock::to_timespec(t
));
652 void set_ctime(const struct timespec
&ts
) {
656 void set_mtime(const struct timespec
&ts
) {
660 void set_atime(const struct timespec
&ts
) {
664 void set_etag(const ceph::buffer::list
& _etag
) {
668 void set_acls(const ceph::buffer::list
& _acls
) {
672 void encode(buffer::list
& bl
) const {
673 ENCODE_START(2, 1, bl
);
674 encode(uint32_t(fh
.fh_type
), bl
);
675 encode(state
.dev
, bl
);
676 encode(state
.size
, bl
);
677 encode(state
.nlink
, bl
);
678 encode(state
.owner_uid
, bl
);
679 encode(state
.owner_gid
, bl
);
680 encode(state
.unix_mode
, bl
);
681 for (const auto& t
: { state
.ctime
, state
.mtime
, state
.atime
}) {
682 encode(real_clock::from_timespec(t
), bl
);
684 encode((uint32_t)2, bl
);
688 void decode(bufferlist::const_iterator
& bl
) {
692 if ((fh
.fh_type
!= fh_type
) &&
693 (fh_type
== RGW_FS_TYPE_SYMBOLIC_LINK
))
694 fh
.fh_type
= RGW_FS_TYPE_SYMBOLIC_LINK
;
695 ceph_assert(fh
.fh_type
== fh_type
);
696 decode(state
.dev
, bl
);
697 decode(state
.size
, bl
);
698 decode(state
.nlink
, bl
);
699 decode(state
.owner_uid
, bl
);
700 decode(state
.owner_gid
, bl
);
701 decode(state
.unix_mode
, bl
);
702 ceph::real_time enc_time
;
703 for (auto t
: { &(state
.ctime
), &(state
.mtime
), &(state
.atime
) }) {
704 decode(enc_time
, bl
);
705 *t
= real_clock::to_timespec(enc_time
);
708 decode(state
.version
, bl
);
713 void encode_attrs(ceph::buffer::list
& ux_key1
,
714 ceph::buffer::list
& ux_attrs1
);
716 DecodeAttrsResult
decode_attrs(const ceph::buffer::list
* ux_key1
,
717 const ceph::buffer::list
* ux_attrs1
);
721 bool reclaim() override
;
723 typedef cohort::lru::LRU
<std::mutex
> FhLRU
;
727 // for internal ordering
728 bool operator()(const RGWFileHandle
& lhs
, const RGWFileHandle
& rhs
) const
729 { return (lhs
.get_key() < rhs
.get_key()); }
731 // for external search by fh_key
732 bool operator()(const fh_key
& k
, const RGWFileHandle
& fh
) const
733 { return k
< fh
.get_key(); }
735 bool operator()(const RGWFileHandle
& fh
, const fh_key
& k
) const
736 { return fh
.get_key() < k
; }
741 bool operator()(const RGWFileHandle
& lhs
, const RGWFileHandle
& rhs
) const
742 { return (lhs
.get_key() == rhs
.get_key()); }
744 bool operator()(const fh_key
& k
, const RGWFileHandle
& fh
) const
745 { return k
== fh
.get_key(); }
747 bool operator()(const RGWFileHandle
& fh
, const fh_key
& k
) const
748 { return fh
.get_key() == k
; }
751 typedef bi::link_mode
<bi::safe_link
> link_mode
; /* XXX normal */
752 #if defined(FHCACHE_AVL)
753 typedef bi::avl_set_member_hook
<link_mode
> tree_hook_type
;
756 typedef bi::set_member_hook
<link_mode
> tree_hook_type
;
758 tree_hook_type fh_hook
;
760 typedef bi::member_hook
<
761 RGWFileHandle
, tree_hook_type
, &RGWFileHandle::fh_hook
> FhHook
;
763 #if defined(FHCACHE_AVL)
764 typedef bi::avltree
<RGWFileHandle
, bi::compare
<FhLT
>, FhHook
> FHTree
;
766 typedef bi::rbtree
<RGWFileHandle
, bi::compare
<FhLT
>, FhHook
> FhTree
;
768 typedef cohort::lru::TreeX
<RGWFileHandle
, FhTree
, FhLT
, FhEQ
, fh_key
,
771 ~RGWFileHandle() override
;
773 friend std::ostream
& operator<<(std::ostream
&os
,
774 RGWFileHandle
const &rgw_fh
);
776 class Factory
: public cohort::lru::ObjectFactory
780 RGWFileHandle
* parent
;
787 Factory(RGWLibFS
* _fs
, RGWFileHandle
* _parent
,
788 const fh_key
& _fhk
, std::string
& _name
, uint32_t _flags
)
789 : fs(_fs
), parent(_parent
), fhk(_fhk
), name(_name
),
792 void recycle (cohort::lru::Object
* o
) override
{
793 /* re-use an existing object */
794 o
->~Object(); // call lru::Object virtual dtor
796 new (o
) RGWFileHandle(fs
, parent
, fhk
, name
, flags
);
799 cohort::lru::Object
* alloc() override
{
800 return new RGWFileHandle(fs
, parent
, fhk
, name
, flags
);
804 }; /* RGWFileHandle */
806 WRITE_CLASS_ENCODER(RGWFileHandle
);
808 static inline RGWFileHandle
* get_rgwfh(struct rgw_file_handle
* fh
) {
809 return static_cast<RGWFileHandle
*>(fh
->fh_private
);
812 static inline enum rgw_fh_type
fh_type_of(uint32_t flags
) {
813 enum rgw_fh_type fh_type
;
814 switch(flags
& RGW_LOOKUP_TYPE_FLAGS
)
816 case RGW_LOOKUP_FLAG_DIR
:
817 fh_type
= RGW_FS_TYPE_DIRECTORY
;
819 case RGW_LOOKUP_FLAG_FILE
:
820 fh_type
= RGW_FS_TYPE_FILE
;
823 fh_type
= RGW_FS_TYPE_NIL
;
828 typedef std::tuple
<RGWFileHandle
*, uint32_t> LookupFHResult
;
829 typedef std::tuple
<RGWFileHandle
*, int> MkObjResult
;
835 RGWFileHandle root_fh
;
836 rgw_fh_callback_t invalidate_cb
;
837 void *invalidate_arg
;
840 mutable std::atomic
<uint64_t> refcnt
;
842 RGWFileHandle::FHCache fh_cache
;
843 RGWFileHandle::FhLRU fh_lru
;
845 std::string uid
; // should match user.user_id, iiuc
848 RGWAccessKey key
; // XXXX acc_key
850 static std::atomic
<uint32_t> fs_inst_counter
;
852 static uint32_t write_completion_interval_s
;
854 using lock_guard
= std::lock_guard
<std::mutex
>;
855 using unique_lock
= std::unique_lock
<std::mutex
>;
859 enum class type
: uint8_t { READDIR
} ;
863 event(type t
, const fh_key
& k
, const struct timespec
& ts
)
864 : t(t
), fhk(k
), ts(ts
) {}
867 friend std::ostream
& operator<<(std::ostream
&os
,
868 RGWLibFS::event
const &ev
);
870 using event_vector
= /* boost::small_vector<event, 16> */
873 struct WriteCompletion
875 RGWFileHandle
& rgw_fh
;
877 explicit WriteCompletion(RGWFileHandle
& _fh
) : rgw_fh(_fh
) {
878 rgw_fh
.get_fs()->ref(&rgw_fh
);
882 rgw_fh
.close(); /* will finish in-progress write */
883 rgw_fh
.get_fs()->unref(&rgw_fh
);
887 static ceph::timer
<ceph::mono_clock
> write_timer
;
891 std::atomic
<uint32_t> flags
;
892 std::deque
<event
> events
;
894 State() : flags(0) {}
896 void push_event(const event
& ev
) {
897 events
.push_back(ev
);
901 uint32_t new_inst() {
902 return ++fs_inst_counter
;
905 friend class RGWFileHandle
;
906 friend class RGWLibProcess
;
910 static constexpr uint32_t FLAG_NONE
= 0x0000;
911 static constexpr uint32_t FLAG_CLOSED
= 0x0001;
916 real_time creation_time
;
917 uint64_t num_entries
;
920 RGWLibFS(CephContext
* _cct
, const char *_uid
, const char *_user_id
,
921 const char* _key
, const char *root
)
922 : cct(_cct
), root_fh(this), invalidate_cb(nullptr),
923 invalidate_arg(nullptr), shutdown(false), refcnt(1),
924 fh_cache(cct
->_conf
->rgw_nfs_fhcache_partitions
,
925 cct
->_conf
->rgw_nfs_fhcache_size
),
926 fh_lru(cct
->_conf
->rgw_nfs_lru_lanes
,
927 cct
->_conf
->rgw_nfs_lru_lane_hiwat
),
928 uid(_uid
), key(_user_id
, _key
) {
930 if (!root
|| !strcmp(root
, "/")) {
931 root_fh
.init_rootfs(uid
, RGWFileHandle::root_name
, false);
933 root_fh
.init_rootfs(uid
, root
, true);
936 /* pointer to self */
937 fs
.fs_private
= this;
939 /* expose public root fh */
940 fs
.root_fh
= root_fh
.get_fh();
945 friend void intrusive_ptr_add_ref(const RGWLibFS
* fs
) {
946 fs
->refcnt
.fetch_add(1, std::memory_order_relaxed
);
949 friend void intrusive_ptr_release(const RGWLibFS
* fs
) {
950 if (fs
->refcnt
.fetch_sub(1, std::memory_order_release
) == 0) {
951 std::atomic_thread_fence(std::memory_order_acquire
);
957 intrusive_ptr_add_ref(this);
962 intrusive_ptr_release(this);
965 void stop() { shutdown
= true; }
967 void release_evict(RGWFileHandle
* fh
) {
968 /* remove from cache, releases sentinel ref */
969 fh_cache
.remove(fh
->fh
.fh_hk
.object
, fh
,
970 RGWFileHandle::FHCache::FLAG_LOCK
);
971 /* release call-path ref */
972 (void) fh_lru
.unref(fh
, cohort::lru::FLAG_NONE
);
975 int authorize(RGWRados
* store
) {
976 int ret
= rgw_get_user_info_by_access_key(store
, key
.id
, user
);
978 RGWAccessKey
* k
= user
.get_key(key
.id
);
979 if (!k
|| (k
->key
!= key
.key
))
982 return -ERR_USER_SUSPENDED
;
984 /* try external authenticators (ldap for now) */
985 rgw::LDAPHelper
* ldh
= rgwlib
.get_ldh(); /* !nullptr */
987 /* boost filters and/or string_ref may throw on invalid input */
989 token
= rgw::from_base64(key
.id
);
991 token
= std::string("");
993 if (token
.valid() && (ldh
->auth(token
.id
, token
.key
) == 0)) {
994 /* try to store user if it doesn't already exist */
995 if (rgw_get_user_info_by_uid(store
, token
.id
, user
) < 0) {
996 int ret
= rgw_store_user_info(store
, user
, NULL
, NULL
, real_time(),
999 lsubdout(get_context(), rgw
, 10)
1000 << "NOTICE: failed to store new user's info: ret=" << ret
1004 } /* auth success */
1009 int register_invalidate(rgw_fh_callback_t cb
, void *arg
, uint32_t flags
) {
1011 invalidate_arg
= arg
;
1015 /* find RGWFileHandle by id */
1016 LookupFHResult
lookup_fh(const fh_key
& fhk
,
1017 const uint32_t flags
= RGWFileHandle::FLAG_NONE
) {
1020 // cast int32_t(RGWFileHandle::FLAG_NONE) due to strictness of Clang
1021 // the cast transfers a lvalue into a rvalue in the ctor
1022 // check the commit message for the full details
1023 LookupFHResult fhr
{ nullptr, uint32_t(RGWFileHandle::FLAG_NONE
) };
1025 RGWFileHandle::FHCache::Latch lat
;
1026 bool fh_locked
= flags
& RGWFileHandle::FLAG_LOCKED
;
1030 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
1031 fhk
/* key */, lat
/* serializer */,
1032 RGWFileHandle::FHCache::FLAG_LOCK
);
1035 if (likely(! fh_locked
))
1036 fh
->mtx
.lock(); // XXX !RAII because may-return-LOCKED
1037 /* need initial ref from LRU (fast path) */
1038 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
1040 if (likely(! fh_locked
))
1042 goto retry
; /* !LATCHED */
1044 /* LATCHED, LOCKED */
1045 if (! (flags
& RGWFileHandle::FLAG_LOCK
))
1046 fh
->mtx
.unlock(); /* ! LOCKED */
1048 lat
.lock
->unlock(); /* !LATCHED */
1051 lsubdout(get_context(), rgw
, 17)
1052 << __func__
<< " 1 " << *fh
1056 } /* lookup_fh(const fh_key&) */
1058 /* find or create an RGWFileHandle */
1059 LookupFHResult
lookup_fh(RGWFileHandle
* parent
, const char *name
,
1060 const uint32_t flags
= RGWFileHandle::FLAG_NONE
) {
1063 // cast int32_t(RGWFileHandle::FLAG_NONE) due to strictness of Clang
1064 // the cast transfers a lvalue into a rvalue in the ctor
1065 // check the commit message for the full details
1066 LookupFHResult fhr
{ nullptr, uint32_t(RGWFileHandle::FLAG_NONE
) };
1068 /* mount is stale? */
1069 if (state
.flags
& FLAG_CLOSED
)
1072 RGWFileHandle::FHCache::Latch lat
;
1073 bool fh_locked
= flags
& RGWFileHandle::FLAG_LOCKED
;
1075 std::string obj_name
{name
};
1076 std::string key_name
{parent
->make_key_name(name
)};
1077 fh_key fhk
= parent
->make_fhk(obj_name
);
1079 lsubdout(get_context(), rgw
, 10)
1080 << __func__
<< " called on "
1081 << parent
->object_name() << " for " << key_name
1082 << " (" << obj_name
<< ")"
1088 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
1089 fhk
/* key */, lat
/* serializer */,
1090 RGWFileHandle::FHCache::FLAG_LOCK
);
1093 if (likely(! fh_locked
))
1094 fh
->mtx
.lock(); // XXX !RAII because may-return-LOCKED
1095 if (fh
->flags
& RGWFileHandle::FLAG_DELETED
) {
1096 /* for now, delay briefly and retry */
1098 if (likely(! fh_locked
))
1100 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1101 goto retry
; /* !LATCHED */
1103 /* need initial ref from LRU (fast path) */
1104 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
1106 if (likely(! fh_locked
))
1108 goto retry
; /* !LATCHED */
1110 /* LATCHED, LOCKED */
1111 if (! (flags
& RGWFileHandle::FLAG_LOCK
))
1112 if (likely(! fh_locked
))
1113 fh
->mtx
.unlock(); /* ! LOCKED */
1115 /* make or re-use handle */
1116 RGWFileHandle::Factory
prototype(this, parent
, fhk
,
1117 obj_name
, CREATE_FLAGS(flags
));
1118 uint32_t iflags
{cohort::lru::FLAG_INITIAL
};
1119 fh
= static_cast<RGWFileHandle
*>(
1120 fh_lru
.insert(&prototype
,
1121 cohort::lru::Edge::MRU
,
1124 /* lock fh (LATCHED) */
1125 if (flags
& RGWFileHandle::FLAG_LOCK
)
1127 if (likely(! (iflags
& cohort::lru::FLAG_RECYCLE
))) {
1128 /* inserts at cached insert iterator, releasing latch */
1129 fh_cache
.insert_latched(
1130 fh
, lat
, RGWFileHandle::FHCache::FLAG_UNLOCK
);
1132 /* recycle step invalidates Latch */
1134 fhk
.fh_hk
.object
, fh
, RGWFileHandle::FHCache::FLAG_NONE
);
1135 lat
.lock
->unlock(); /* !LATCHED */
1137 get
<1>(fhr
) |= RGWFileHandle::FLAG_CREATE
;
1138 /* ref parent (non-initial ref cannot fail on valid object) */
1139 if (! parent
->is_mount()) {
1140 (void) fh_lru
.ref(parent
, cohort::lru::FLAG_NONE
);
1142 goto out
; /* !LATCHED */
1145 goto retry
; /* !LATCHED */
1148 lat
.lock
->unlock(); /* !LATCHED */
1152 lsubdout(get_context(), rgw
, 17)
1153 << __func__
<< " 2 " << *fh
1157 } /* lookup_fh(RGWFileHandle*, const char *, const uint32_t) */
1159 inline void unref(RGWFileHandle
* fh
) {
1160 if (likely(! fh
->is_mount())) {
1161 (void) fh_lru
.unref(fh
, cohort::lru::FLAG_NONE
);
1165 inline RGWFileHandle
* ref(RGWFileHandle
* fh
) {
1166 if (likely(! fh
->is_mount())) {
1167 fh_lru
.ref(fh
, cohort::lru::FLAG_NONE
);
1172 int getattr(RGWFileHandle
* rgw_fh
, struct stat
* st
);
1174 int setattr(RGWFileHandle
* rgw_fh
, struct stat
* st
, uint32_t mask
,
1177 void update_fh(RGWFileHandle
*rgw_fh
);
1179 LookupFHResult
stat_bucket(RGWFileHandle
* parent
, const char *path
,
1180 RGWLibFS::BucketStats
& bs
,
1183 LookupFHResult
fake_leaf(RGWFileHandle
* parent
, const char *path
,
1184 enum rgw_fh_type type
= RGW_FS_TYPE_NIL
,
1185 struct stat
*st
= nullptr, uint32_t mask
= 0,
1186 uint32_t flags
= RGWFileHandle::FLAG_NONE
);
1188 LookupFHResult
stat_leaf(RGWFileHandle
* parent
, const char *path
,
1189 enum rgw_fh_type type
= RGW_FS_TYPE_NIL
,
1190 uint32_t flags
= RGWFileHandle::FLAG_NONE
);
1192 int read(RGWFileHandle
* rgw_fh
, uint64_t offset
, size_t length
,
1193 size_t* bytes_read
, void* buffer
, uint32_t flags
);
1195 int readlink(RGWFileHandle
* rgw_fh
, uint64_t offset
, size_t length
,
1196 size_t* bytes_read
, void* buffer
, uint32_t flags
);
1198 int rename(RGWFileHandle
* old_fh
, RGWFileHandle
* new_fh
,
1199 const char *old_name
, const char *new_name
);
1201 MkObjResult
create(RGWFileHandle
* parent
, const char *name
, struct stat
*st
,
1202 uint32_t mask
, uint32_t flags
);
1204 MkObjResult
symlink(RGWFileHandle
* parent
, const char *name
,
1205 const char *link_path
, struct stat
*st
, uint32_t mask
, uint32_t flags
);
1207 MkObjResult
mkdir(RGWFileHandle
* parent
, const char *name
, struct stat
*st
,
1208 uint32_t mask
, uint32_t flags
);
1210 int unlink(RGWFileHandle
* rgw_fh
, const char *name
,
1211 uint32_t flags
= FLAG_NONE
);
1213 /* find existing RGWFileHandle */
1214 RGWFileHandle
* lookup_handle(struct rgw_fh_hk fh_hk
) {
1216 if (state
.flags
& FLAG_CLOSED
)
1219 RGWFileHandle::FHCache::Latch lat
;
1224 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
1225 fhk
/* key */, lat
/* serializer */,
1226 RGWFileHandle::FHCache::FLAG_LOCK
);
1229 if (unlikely(fhk
== root_fh
.fh
.fh_hk
)) {
1230 /* lookup for root of this fs */
1234 lsubdout(get_context(), rgw
, 0)
1235 << __func__
<< " handle lookup failed " << fhk
1240 if (fh
->flags
& RGWFileHandle::FLAG_DELETED
) {
1241 /* for now, delay briefly and retry */
1243 fh
->mtx
.unlock(); /* !LOCKED */
1244 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1245 goto retry
; /* !LATCHED */
1247 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
1250 goto retry
; /* !LATCHED */
1253 fh
->mtx
.unlock(); /* !LOCKED */
1255 lat
.lock
->unlock(); /* !LATCHED */
1257 /* special case: lookup root_fh */
1259 if (unlikely(fh_hk
== root_fh
.fh
.fh_hk
)) {
1267 CephContext
* get_context() {
1271 struct rgw_fs
* get_fs() { return &fs
; }
1273 uint64_t get_fsid() { return root_fh
.state
.dev
; }
1275 RGWUserInfo
* get_user() { return &user
; }
1277 void update_user() {
1278 RGWUserInfo _user
= user
;
1279 int ret
= rgw_get_user_info_by_access_key(rgwlib
.get_store(), key
.id
, user
);
1288 static inline std::string
make_uri(const std::string
& bucket_name
,
1289 const std::string
& object_name
) {
1290 std::string
uri("/");
1291 uri
.reserve(bucket_name
.length() + object_name
.length() + 2);
1299 read directory content (buckets)
1302 class RGWListBucketsRequest
: public RGWLibRequest
,
1303 public RGWListBuckets
/* RGWOp */
1306 RGWFileHandle
* rgw_fh
;
1307 RGWFileHandle::readdir_offset offset
;
1313 bool rcb_eof
; // caller forced early stop in readdir cycle
1315 RGWListBucketsRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1316 RGWFileHandle
* _rgw_fh
, rgw_readdir_cb _rcb
,
1317 void* _cb_arg
, RGWFileHandle::readdir_offset
& _offset
)
1318 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), offset(_offset
),
1319 cb_arg(_cb_arg
), rcb(_rcb
), ioff(nullptr), ix(0), d_count(0),
1324 if (unlikely(!! get
<uint64_t*>(&offset
))) {
1325 ioff
= get
<uint64_t*>(offset
);
1326 const auto& mk
= rgw_fh
->find_marker(*ioff
);
1331 const char* mk
= get
<const char*>(offset
);
1339 bool only_bucket() override
{ return false; }
1341 int op_init() override
{
1342 // assign store, s, and dialect_handler
1343 RGWObjectCtx
* rados_ctx
1344 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1345 // framework promises to call op_init after parent init
1346 ceph_assert(rados_ctx
);
1347 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1348 op
= this; // assign self as op: REQUIRED
1352 int header_init() override
{
1353 struct req_state
* s
= get_state();
1354 s
->info
.method
= "GET";
1357 /* XXX derp derp derp */
1358 s
->relative_uri
= "/";
1359 s
->info
.request_uri
= "/"; // XXX
1360 s
->info
.effective_uri
= "/";
1361 s
->info
.request_params
= "";
1362 s
->info
.domain
= ""; /* XXX ? */
1366 s
->bucket_tenant
= user
->user_id
.tenant
;
1371 int get_params() override
{
1372 limit
= -1; /* no limit */
1376 void send_response_begin(bool has_buckets
) override
{
1380 void send_response_data(RGWUserBuckets
& buckets
) override
{
1383 map
<string
, RGWBucketEnt
>& m
= buckets
.get_buckets();
1384 for (const auto& iter
: m
) {
1385 boost::string_ref marker
{iter
.first
};
1386 const RGWBucketEnt
& ent
= iter
.second
;
1387 if (! this->operator()(ent
.bucket
.name
, marker
)) {
1388 /* caller cannot accept more */
1389 lsubdout(cct
, rgw
, 5) << "ListBuckets rcb failed"
1390 << " dirent=" << ent
.bucket
.name
1391 << " call count=" << ix
1398 } /* send_response_data */
1400 void send_response_end() override
{
1404 int operator()(const boost::string_ref
& name
,
1405 const boost::string_ref
& marker
) {
1406 uint64_t off
= XXH64(name
.data(), name
.length(), fh_key::seed
);
1410 /* update traversal cache */
1411 rgw_fh
->add_marker(off
, rgw_obj_key
{marker
.data(), ""},
1412 RGW_FS_TYPE_DIRECTORY
);
1414 return rcb(name
.data(), cb_arg
, off
, nullptr, 0, RGW_LOOKUP_FLAG_DIR
);
1418 if (unlikely(cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15))) {
1420 unlikely(! get
<const char*>(&offset
)) ||
1421 !! get
<const char*>(offset
);
1422 lsubdout(cct
, rgw
, 15) << "READDIR offset: " <<
1423 ((is_offset
) ? offset
: "(nil)")
1424 << " is_truncated: " << is_truncated
1427 return !is_truncated
&& !rcb_eof
;
1430 }; /* RGWListBucketsRequest */
1433 read directory content (bucket objects)
1436 class RGWReaddirRequest
: public RGWLibRequest
,
1437 public RGWListBucket
/* RGWOp */
1440 RGWFileHandle
* rgw_fh
;
1441 RGWFileHandle::readdir_offset offset
;
1447 bool rcb_eof
; // caller forced early stop in readdir cycle
1449 RGWReaddirRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1450 RGWFileHandle
* _rgw_fh
, rgw_readdir_cb _rcb
,
1451 void* _cb_arg
, RGWFileHandle::readdir_offset
& _offset
)
1452 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), offset(_offset
),
1453 cb_arg(_cb_arg
), rcb(_rcb
), ioff(nullptr), ix(0), d_count(0),
1458 if (unlikely(!! get
<uint64_t*>(&offset
))) {
1459 ioff
= get
<uint64_t*>(offset
);
1460 const auto& mk
= rgw_fh
->find_marker(*ioff
);
1465 const char* mk
= get
<const char*>(offset
);
1467 std::string tmark
{rgw_fh
->relative_object_name()};
1468 if (tmark
.length() > 0)
1471 marker
= rgw_obj_key
{std::move(tmark
), "", ""};
1475 default_max
= 1000; // XXX was being omitted
1479 bool only_bucket() override
{ return true; }
1481 int op_init() override
{
1482 // assign store, s, and dialect_handler
1483 RGWObjectCtx
* rados_ctx
1484 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1485 // framework promises to call op_init after parent init
1486 ceph_assert(rados_ctx
);
1487 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1488 op
= this; // assign self as op: REQUIRED
1492 int header_init() override
{
1493 struct req_state
* s
= get_state();
1494 s
->info
.method
= "GET";
1497 /* XXX derp derp derp */
1498 std::string uri
= "/" + rgw_fh
->bucket_name() + "/";
1499 s
->relative_uri
= uri
;
1500 s
->info
.request_uri
= uri
; // XXX
1501 s
->info
.effective_uri
= uri
;
1502 s
->info
.request_params
= "";
1503 s
->info
.domain
= ""; /* XXX ? */
1507 s
->bucket_tenant
= user
->user_id
.tenant
;
1509 prefix
= rgw_fh
->relative_object_name();
1510 if (prefix
.length() > 0)
1517 int operator()(const boost::string_ref name
, const rgw_obj_key
& marker
,
1518 const ceph::real_time
& t
, const uint64_t fsz
, uint8_t type
) {
1520 assert(name
.length() > 0); // all cases handled in callers
1522 /* hash offset of name in parent (short name) for NFS readdir cookie */
1523 uint64_t off
= XXH64(name
.data(), name
.length(), fh_key::seed
);
1524 if (unlikely(!! ioff
)) {
1528 /* update traversal cache */
1529 rgw_fh
->add_marker(off
, marker
, type
);
1532 /* set c/mtime and size from bucket index entry */
1533 struct stat st
= {};
1534 #ifdef HAVE_STAT_ST_MTIMESPEC_TV_NSEC
1535 st
.st_atimespec
= ceph::real_clock::to_timespec(t
);
1536 st
.st_mtimespec
= st
.st_atimespec
;
1537 st
.st_ctimespec
= st
.st_atimespec
;
1539 st
.st_atim
= ceph::real_clock::to_timespec(t
);
1540 st
.st_mtim
= st
.st_atim
;
1541 st
.st_ctim
= st
.st_atim
;
1545 return rcb(name
.data(), cb_arg
, off
, &st
, RGWFileHandle::RCB_MASK
,
1546 (type
== RGW_FS_TYPE_DIRECTORY
) ?
1547 RGW_LOOKUP_FLAG_DIR
:
1548 RGW_LOOKUP_FLAG_FILE
);
1551 int get_params() override
{
1556 void send_response() override
{
1557 struct req_state
* s
= get_state();
1558 for (const auto& iter
: objs
) {
1560 boost::string_ref sref
{iter
.key
.name
};
1562 lsubdout(cct
, rgw
, 15) << "readdir objects prefix: " << prefix
1563 << " obj: " << sref
<< dendl
;
1565 size_t last_del
= sref
.find_last_of('/');
1566 if (last_del
!= string::npos
)
1567 sref
.remove_prefix(last_del
+1);
1569 /* leaf directory? */
1573 lsubdout(cct
, rgw
, 15) << "RGWReaddirRequest "
1575 << "list uri=" << s
->relative_uri
<< " "
1576 << " prefix=" << prefix
<< " "
1577 << " obj path=" << iter
.key
.name
1578 << " (" << sref
<< ")" << ""
1580 << real_clock::to_time_t(iter
.meta
.mtime
)
1581 << " size=" << iter
.meta
.accounted_size
1584 if (! this->operator()(sref
, next_marker
, iter
.meta
.mtime
,
1585 iter
.meta
.accounted_size
, RGW_FS_TYPE_FILE
)) {
1586 /* caller cannot accept more */
1587 lsubdout(cct
, rgw
, 5) << "readdir rcb failed"
1588 << " dirent=" << sref
.data()
1589 << " call count=" << ix
1597 auto cnow
= real_clock::now();
1598 for (auto& iter
: common_prefixes
) {
1600 lsubdout(cct
, rgw
, 15) << "readdir common prefixes prefix: " << prefix
1601 << " iter first: " << iter
.first
1602 << " iter second: " << iter
.second
1605 /* XXX aieee--I have seen this case! */
1606 if (iter
.first
== "/")
1609 /* it's safest to modify the element in place--a suffix-modifying
1610 * string_ref operation is problematic since ULP rgw_file callers
1611 * will ultimately need a c-string */
1612 if (iter
.first
.back() == '/')
1613 const_cast<std::string
&>(iter
.first
).pop_back();
1615 boost::string_ref sref
{iter
.first
};
1617 size_t last_del
= sref
.find_last_of('/');
1618 if (last_del
!= string::npos
)
1619 sref
.remove_prefix(last_del
+1);
1621 lsubdout(cct
, rgw
, 15) << "RGWReaddirRequest "
1623 << "list uri=" << s
->relative_uri
<< " "
1624 << " prefix=" << prefix
<< " "
1625 << " cpref=" << sref
1629 /* null path segment--could be created in S3 but has no NFS
1634 if (! this->operator()(sref
, next_marker
, cnow
, 0,
1635 RGW_FS_TYPE_DIRECTORY
)) {
1636 /* caller cannot accept more */
1637 lsubdout(cct
, rgw
, 5) << "readdir rcb failed"
1638 << " dirent=" << sref
.data()
1639 << " call count=" << ix
1648 virtual void send_versioned_response() {
1653 if (unlikely(cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15))) {
1655 unlikely(! get
<const char*>(&offset
)) ||
1656 !! get
<const char*>(offset
);
1657 lsubdout(cct
, rgw
, 15) << "READDIR offset: " <<
1658 ((is_offset
) ? offset
: "(nil)")
1659 << " next marker: " << next_marker
1660 << " is_truncated: " << is_truncated
1663 return !is_truncated
&& !rcb_eof
;
1666 }; /* RGWReaddirRequest */
1669 dir has-children predicate (bucket objects)
1672 class RGWRMdirCheck
: public RGWLibRequest
,
1673 public RGWListBucket
/* RGWOp */
1676 const RGWFileHandle
* rgw_fh
;
1680 RGWRMdirCheck (CephContext
* _cct
, RGWUserInfo
*_user
,
1681 const RGWFileHandle
* _rgw_fh
)
1682 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), valid(false),
1683 has_children(false) {
1688 bool only_bucket() override
{ return true; }
1690 int op_init() override
{
1691 // assign store, s, and dialect_handler
1692 RGWObjectCtx
* rados_ctx
1693 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1694 // framework promises to call op_init after parent init
1695 ceph_assert(rados_ctx
);
1696 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1697 op
= this; // assign self as op: REQUIRED
1701 int header_init() override
{
1702 struct req_state
* s
= get_state();
1703 s
->info
.method
= "GET";
1706 std::string uri
= "/" + rgw_fh
->bucket_name() + "/";
1707 s
->relative_uri
= uri
;
1708 s
->info
.request_uri
= uri
;
1709 s
->info
.effective_uri
= uri
;
1710 s
->info
.request_params
= "";
1711 s
->info
.domain
= ""; /* XXX ? */
1714 s
->bucket_tenant
= user
->user_id
.tenant
;
1716 prefix
= rgw_fh
->relative_object_name();
1717 if (prefix
.length() > 0)
1724 int get_params() override
{
1729 void send_response() override
{
1731 if ((objs
.size() > 1) ||
1733 (objs
.front().key
.name
!= prefix
))) {
1734 has_children
= true;
1737 for (auto& iter
: common_prefixes
) {
1738 /* readdir never produces a name for this case */
1739 if (iter
.first
== "/")
1741 has_children
= true;
1746 virtual void send_versioned_response() {
1750 }; /* RGWRMdirCheck */
1756 class RGWCreateBucketRequest
: public RGWLibRequest
,
1757 public RGWCreateBucket
/* RGWOp */
1760 const std::string
& bucket_name
;
1762 RGWCreateBucketRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1763 std::string
& _bname
)
1764 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
) {
1768 bool only_bucket() override
{ return false; }
1770 int read_permissions(RGWOp
* op_obj
) override
{
1771 /* we ARE a 'create bucket' request (cf. rgw_rest.cc, ll. 1305-6) */
1775 int op_init() override
{
1776 // assign store, s, and dialect_handler
1777 RGWObjectCtx
* rados_ctx
1778 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1779 // framework promises to call op_init after parent init
1780 ceph_assert(rados_ctx
);
1781 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1782 op
= this; // assign self as op: REQUIRED
1786 int header_init() override
{
1788 struct req_state
* s
= get_state();
1789 s
->info
.method
= "PUT";
1792 string uri
= "/" + bucket_name
;
1793 /* XXX derp derp derp */
1794 s
->relative_uri
= uri
;
1795 s
->info
.request_uri
= uri
; // XXX
1796 s
->info
.effective_uri
= uri
;
1797 s
->info
.request_params
= "";
1798 s
->info
.domain
= ""; /* XXX ? */
1802 s
->bucket_tenant
= user
->user_id
.tenant
;
1807 int get_params() override
{
1808 struct req_state
* s
= get_state();
1809 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1810 /* we don't have (any) headers, so just create canned ACLs */
1811 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
1816 void send_response() override
{
1817 /* TODO: something (maybe) */
1819 }; /* RGWCreateBucketRequest */
1825 class RGWDeleteBucketRequest
: public RGWLibRequest
,
1826 public RGWDeleteBucket
/* RGWOp */
1829 const std::string
& bucket_name
;
1831 RGWDeleteBucketRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1832 std::string
& _bname
)
1833 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
) {
1837 bool only_bucket() override
{ return true; }
1839 int op_init() override
{
1840 // assign store, s, and dialect_handler
1841 RGWObjectCtx
* rados_ctx
1842 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1843 // framework promises to call op_init after parent init
1844 ceph_assert(rados_ctx
);
1845 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1846 op
= this; // assign self as op: REQUIRED
1850 int header_init() override
{
1852 struct req_state
* s
= get_state();
1853 s
->info
.method
= "DELETE";
1856 string uri
= "/" + bucket_name
;
1857 /* XXX derp derp derp */
1858 s
->relative_uri
= uri
;
1859 s
->info
.request_uri
= uri
; // XXX
1860 s
->info
.effective_uri
= uri
;
1861 s
->info
.request_params
= "";
1862 s
->info
.domain
= ""; /* XXX ? */
1866 s
->bucket_tenant
= user
->user_id
.tenant
;
1871 void send_response() override
{}
1873 }; /* RGWDeleteBucketRequest */
1878 class RGWPutObjRequest
: public RGWLibRequest
,
1879 public RGWPutObj
/* RGWOp */
1882 const std::string
& bucket_name
;
1883 const std::string
& obj_name
;
1884 buffer::list
& bl
; /* XXX */
1885 size_t bytes_written
;
1887 RGWPutObjRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1888 const std::string
& _bname
, const std::string
& _oname
,
1890 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
),
1891 bl(_bl
), bytes_written(0) {
1895 bool only_bucket() override
{ return true; }
1897 int op_init() override
{
1898 // assign store, s, and dialect_handler
1899 RGWObjectCtx
* rados_ctx
1900 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1901 // framework promises to call op_init after parent init
1902 ceph_assert(rados_ctx
);
1903 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1904 op
= this; // assign self as op: REQUIRED
1906 int rc
= valid_s3_object_name(obj_name
);
1913 int header_init() override
{
1915 struct req_state
* s
= get_state();
1916 s
->info
.method
= "PUT";
1919 /* XXX derp derp derp */
1920 std::string uri
= make_uri(bucket_name
, obj_name
);
1921 s
->relative_uri
= uri
;
1922 s
->info
.request_uri
= uri
; // XXX
1923 s
->info
.effective_uri
= uri
;
1924 s
->info
.request_params
= "";
1925 s
->info
.domain
= ""; /* XXX ? */
1927 /* XXX required in RGWOp::execute() */
1928 s
->content_length
= bl
.length();
1932 s
->bucket_tenant
= user
->user_id
.tenant
;
1937 int get_params() override
{
1938 struct req_state
* s
= get_state();
1939 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1940 /* we don't have (any) headers, so just create canned ACLs */
1941 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
1946 int get_data(buffer::list
& _bl
) override
{
1947 /* XXX for now, use sharing semantics */
1949 uint32_t len
= _bl
.length();
1950 bytes_written
+= len
;
1954 void send_response() override
{}
1956 int verify_params() override
{
1957 if (bl
.length() > cct
->_conf
->rgw_max_put_size
)
1958 return -ERR_TOO_LARGE
;
1962 buffer::list
* get_attr(const std::string
& k
) {
1963 auto iter
= attrs
.find(k
);
1964 return (iter
!= attrs
.end()) ? &(iter
->second
) : nullptr;
1967 }; /* RGWPutObjRequest */
1973 class RGWReadRequest
: public RGWLibRequest
,
1974 public RGWGetObj
/* RGWOp */
1977 RGWFileHandle
* rgw_fh
;
1980 size_t read_resid
; /* initialize to len, <= sizeof(ulp_buffer) */
1981 bool do_hexdump
= false;
1983 RGWReadRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1984 RGWFileHandle
* _rgw_fh
, uint64_t off
, uint64_t len
,
1986 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), ulp_buffer(_ulp_buffer
),
1987 nread(0), read_resid(len
) {
1990 /* fixup RGWGetObj (already know range parameters) */
1991 RGWGetObj::range_parsed
= true;
1992 RGWGetObj::get_data
= true; // XXX
1993 RGWGetObj::partial_content
= true;
1994 RGWGetObj::ofs
= off
;
1995 RGWGetObj::end
= off
+ len
;
1998 bool only_bucket() override
{ return false; }
2000 int op_init() override
{
2001 // assign store, s, and dialect_handler
2002 RGWObjectCtx
* rados_ctx
2003 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2004 // framework promises to call op_init after parent init
2005 ceph_assert(rados_ctx
);
2006 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2007 op
= this; // assign self as op: REQUIRED
2011 int header_init() override
{
2013 struct req_state
* s
= get_state();
2014 s
->info
.method
= "GET";
2017 /* XXX derp derp derp */
2018 s
->relative_uri
= make_uri(rgw_fh
->bucket_name(),
2019 rgw_fh
->relative_object_name());
2020 s
->info
.request_uri
= s
->relative_uri
; // XXX
2021 s
->info
.effective_uri
= s
->relative_uri
;
2022 s
->info
.request_params
= "";
2023 s
->info
.domain
= ""; /* XXX ? */
2027 s
->bucket_tenant
= user
->user_id
.tenant
;
2032 int get_params() override
{
2036 int send_response_data(ceph::buffer::list
& bl
, off_t bl_off
,
2037 off_t bl_len
) override
{
2039 for (auto& bp
: bl
.buffers()) {
2040 /* if for some reason bl_off indicates the start-of-data is not at
2041 * the current buffer::ptr, skip it and account */
2042 if (bl_off
> bp
.length()) {
2043 bl_off
-= bp
.length();
2046 /* read no more than read_resid */
2047 bytes
= std::min(read_resid
, size_t(bp
.length()-bl_off
));
2048 memcpy(static_cast<char*>(ulp_buffer
)+nread
, bp
.c_str()+bl_off
, bytes
);
2049 read_resid
-= bytes
; /* reduce read_resid by bytes read */
2052 /* stop if we have no residual ulp_buffer */
2059 int send_response_data_error() override
{
2060 /* S3 implementation just sends nothing--there is no side effect
2061 * to simulate here */
2065 }; /* RGWReadRequest */
2071 class RGWDeleteObjRequest
: public RGWLibRequest
,
2072 public RGWDeleteObj
/* RGWOp */
2075 const std::string
& bucket_name
;
2076 const std::string
& obj_name
;
2078 RGWDeleteObjRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2079 const std::string
& _bname
, const std::string
& _oname
)
2080 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
) {
2084 bool only_bucket() override
{ return true; }
2086 int op_init() override
{
2087 // assign store, s, and dialect_handler
2088 RGWObjectCtx
* rados_ctx
2089 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2090 // framework promises to call op_init after parent init
2091 ceph_assert(rados_ctx
);
2092 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2093 op
= this; // assign self as op: REQUIRED
2097 int header_init() override
{
2099 struct req_state
* s
= get_state();
2100 s
->info
.method
= "DELETE";
2103 /* XXX derp derp derp */
2104 std::string uri
= make_uri(bucket_name
, obj_name
);
2105 s
->relative_uri
= uri
;
2106 s
->info
.request_uri
= uri
; // XXX
2107 s
->info
.effective_uri
= uri
;
2108 s
->info
.request_params
= "";
2109 s
->info
.domain
= ""; /* XXX ? */
2113 s
->bucket_tenant
= user
->user_id
.tenant
;
2118 void send_response() override
{}
2120 }; /* RGWDeleteObjRequest */
2122 class RGWStatObjRequest
: public RGWLibRequest
,
2123 public RGWGetObj
/* RGWOp */
2126 const std::string
& bucket_name
;
2127 const std::string
& obj_name
;
2131 static constexpr uint32_t FLAG_NONE
= 0x000;
2133 RGWStatObjRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2134 const std::string
& _bname
, const std::string
& _oname
,
2136 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
),
2137 _size(0), flags(_flags
) {
2140 /* fixup RGWGetObj (already know range parameters) */
2141 RGWGetObj::range_parsed
= true;
2142 RGWGetObj::get_data
= false; // XXX
2143 RGWGetObj::partial_content
= true;
2145 RGWGetObj::end
= UINT64_MAX
;
2148 const char* name() const override
{ return "stat_obj"; }
2149 RGWOpType
get_type() override
{ return RGW_OP_STAT_OBJ
; }
2151 real_time
get_mtime() const {
2156 uint64_t get_size() { return _size
; }
2157 real_time
ctime() { return mod_time
; } // XXX
2158 real_time
mtime() { return mod_time
; }
2159 std::map
<string
, bufferlist
>& get_attrs() { return attrs
; }
2161 buffer::list
* get_attr(const std::string
& k
) {
2162 auto iter
= attrs
.find(k
);
2163 return (iter
!= attrs
.end()) ? &(iter
->second
) : nullptr;
2166 bool only_bucket() override
{ return false; }
2168 int op_init() override
{
2169 // assign store, s, and dialect_handler
2170 RGWObjectCtx
* rados_ctx
2171 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2172 // framework promises to call op_init after parent init
2173 ceph_assert(rados_ctx
);
2174 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2175 op
= this; // assign self as op: REQUIRED
2179 int header_init() override
{
2181 struct req_state
* s
= get_state();
2182 s
->info
.method
= "GET";
2185 /* XXX derp derp derp */
2186 s
->relative_uri
= make_uri(bucket_name
, obj_name
);
2187 s
->info
.request_uri
= s
->relative_uri
; // XXX
2188 s
->info
.effective_uri
= s
->relative_uri
;
2189 s
->info
.request_params
= "";
2190 s
->info
.domain
= ""; /* XXX ? */
2194 s
->bucket_tenant
= user
->user_id
.tenant
;
2199 int get_params() override
{
2203 int send_response_data(ceph::buffer::list
& _bl
, off_t s_off
,
2204 off_t e_off
) override
{
2206 /* XXX save attrs? */
2210 int send_response_data_error() override
{
2215 void execute() override
{
2216 RGWGetObj::execute();
2217 _size
= get_state()->obj_size
;
2220 }; /* RGWStatObjRequest */
2222 class RGWStatBucketRequest
: public RGWLibRequest
,
2223 public RGWStatBucket
/* RGWOp */
2227 std::map
<std::string
, buffer::list
> attrs
;
2228 RGWLibFS::BucketStats
& bs
;
2230 RGWStatBucketRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2231 const std::string
& _path
,
2232 RGWLibFS::BucketStats
& _stats
)
2233 : RGWLibRequest(_cct
, _user
), bs(_stats
) {
2238 buffer::list
* get_attr(const std::string
& k
) {
2239 auto iter
= attrs
.find(k
);
2240 return (iter
!= attrs
.end()) ? &(iter
->second
) : nullptr;
2243 real_time
get_ctime() const {
2244 return bucket
.creation_time
;
2247 bool only_bucket() override
{ return false; }
2249 int op_init() override
{
2250 // assign store, s, and dialect_handler
2251 RGWObjectCtx
* rados_ctx
2252 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2253 // framework promises to call op_init after parent init
2254 ceph_assert(rados_ctx
);
2255 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2256 op
= this; // assign self as op: REQUIRED
2260 int header_init() override
{
2262 struct req_state
* s
= get_state();
2263 s
->info
.method
= "GET";
2266 /* XXX derp derp derp */
2267 s
->relative_uri
= uri
;
2268 s
->info
.request_uri
= uri
; // XXX
2269 s
->info
.effective_uri
= uri
;
2270 s
->info
.request_params
= "";
2271 s
->info
.domain
= ""; /* XXX ? */
2275 s
->bucket_tenant
= user
->user_id
.tenant
;
2280 virtual int get_params() {
2284 void send_response() override
{
2285 bucket
.creation_time
= get_state()->bucket_info
.creation_time
;
2286 bs
.size
= bucket
.size
;
2287 bs
.size_rounded
= bucket
.size_rounded
;
2288 bs
.creation_time
= bucket
.creation_time
;
2289 bs
.num_entries
= bucket
.count
;
2290 std::swap(attrs
, get_state()->bucket_attrs
);
2294 return (bucket
.bucket
.name
.length() > 0);
2297 }; /* RGWStatBucketRequest */
2299 class RGWStatLeafRequest
: public RGWLibRequest
,
2300 public RGWListBucket
/* RGWOp */
2303 RGWFileHandle
* rgw_fh
;
2309 RGWStatLeafRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2310 RGWFileHandle
* _rgw_fh
, const std::string
& _path
)
2311 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), path(_path
),
2312 matched(false), is_dir(false), exact_matched(false) {
2313 default_max
= 1000; // logical max {"foo", "foo/"}
2317 bool only_bucket() override
{ return true; }
2319 int op_init() override
{
2320 // assign store, s, and dialect_handler
2321 RGWObjectCtx
* rados_ctx
2322 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2323 // framework promises to call op_init after parent init
2324 ceph_assert(rados_ctx
);
2325 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2326 op
= this; // assign self as op: REQUIRED
2330 int header_init() override
{
2332 struct req_state
* s
= get_state();
2333 s
->info
.method
= "GET";
2336 /* XXX derp derp derp */
2337 std::string uri
= "/" + rgw_fh
->bucket_name() + "/";
2338 s
->relative_uri
= uri
;
2339 s
->info
.request_uri
= uri
; // XXX
2340 s
->info
.effective_uri
= uri
;
2341 s
->info
.request_params
= "";
2342 s
->info
.domain
= ""; /* XXX ? */
2346 s
->bucket_tenant
= user
->user_id
.tenant
;
2348 prefix
= rgw_fh
->relative_object_name();
2349 if (prefix
.length() > 0)
2357 int get_params() override
{
2362 void send_response() override
{
2363 struct req_state
* s
= get_state();
2365 for (const auto& iter
: objs
) {
2366 auto& name
= iter
.key
.name
;
2367 lsubdout(cct
, rgw
, 15) << "RGWStatLeafRequest "
2369 << "list uri=" << s
->relative_uri
<< " "
2370 << " prefix=" << prefix
<< " "
2371 << " obj path=" << name
<< ""
2372 << " target = " << path
<< ""
2374 /* XXX is there a missing match-dir case (trailing '/')? */
2377 exact_matched
= true;
2381 for (auto& iter
: common_prefixes
) {
2382 auto& name
= iter
.first
;
2383 lsubdout(cct
, rgw
, 15) << "RGWStatLeafRequest "
2385 << "list uri=" << s
->relative_uri
<< " "
2386 << " prefix=" << prefix
<< " "
2387 << " pref path=" << name
<< " (not chomped)"
2388 << " target = " << path
<< ""
2391 /* match-dir case (trailing '/') */
2392 if (name
== prefix
+ "/")
2393 exact_matched
= true;
2399 virtual void send_versioned_response() {
2402 }; /* RGWStatLeafRequest */
2408 class RGWWriteRequest
: public RGWLibContinuedReq
,
2409 public RGWPutObj
/* RGWOp */
2412 const std::string
& bucket_name
;
2413 const std::string
& obj_name
;
2414 RGWFileHandle
* rgw_fh
;
2415 std::optional
<rgw::AioThrottle
> aio
;
2416 std::optional
<rgw::putobj::AtomicObjectProcessor
> processor
;
2417 rgw::putobj::DataProcessor
* filter
;
2418 boost::optional
<RGWPutObj_Compress
> compressor
;
2419 CompressorRef plugin
;
2424 size_t bytes_written
;
2427 RGWWriteRequest(CephContext
* _cct
, RGWUserInfo
*_user
, RGWFileHandle
* _fh
,
2428 const std::string
& _bname
, const std::string
& _oname
)
2429 : RGWLibContinuedReq(_cct
, _user
),
2430 bucket_name(_bname
), obj_name(_oname
),
2431 rgw_fh(_fh
), filter(nullptr), real_ofs(0),
2432 bytes_written(0), eio(false) {
2434 int ret
= header_init();
2436 ret
= init_from_header(get_state());
2441 bool only_bucket() override
{ return true; }
2443 int op_init() override
{
2444 // assign store, s, and dialect_handler
2445 RGWObjectCtx
* rados_ctx
2446 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2447 // framework promises to call op_init after parent init
2448 ceph_assert(rados_ctx
);
2449 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2450 op
= this; // assign self as op: REQUIRED
2454 int header_init() override
{
2456 struct req_state
* s
= get_state();
2457 s
->info
.method
= "PUT";
2460 /* XXX derp derp derp */
2461 std::string uri
= make_uri(bucket_name
, obj_name
);
2462 s
->relative_uri
= uri
;
2463 s
->info
.request_uri
= uri
; // XXX
2464 s
->info
.effective_uri
= uri
;
2465 s
->info
.request_params
= "";
2466 s
->info
.domain
= ""; /* XXX ? */
2470 s
->bucket_tenant
= user
->user_id
.tenant
;
2475 int get_params() override
{
2476 struct req_state
* s
= get_state();
2477 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2478 /* we don't have (any) headers, so just create canned ACLs */
2479 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
2484 int get_data(buffer::list
& _bl
) override
{
2485 /* XXX for now, use sharing semantics */
2486 uint32_t len
= data
.length();
2488 bytes_written
+= len
;
2492 void put_data(off_t off
, buffer::list
& _bl
) {
2493 if (off
!= real_ofs
) {
2497 real_ofs
+= data
.length();
2498 ofs
= off
; /* consumed in exec_continue() */
2501 int exec_start() override
;
2502 int exec_continue() override
;
2503 int exec_finish() override
;
2505 void send_response() override
{}
2507 int verify_params() override
{
2510 }; /* RGWWriteRequest */
2515 class RGWCopyObjRequest
: public RGWLibRequest
,
2516 public RGWCopyObj
/* RGWOp */
2519 RGWFileHandle
* src_parent
;
2520 RGWFileHandle
* dst_parent
;
2521 const std::string
& src_name
;
2522 const std::string
& dst_name
;
2524 RGWCopyObjRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2525 RGWFileHandle
* _src_parent
, RGWFileHandle
* _dst_parent
,
2526 const std::string
& _src_name
, const std::string
& _dst_name
)
2527 : RGWLibRequest(_cct
, _user
), src_parent(_src_parent
),
2528 dst_parent(_dst_parent
), src_name(_src_name
), dst_name(_dst_name
) {
2529 /* all requests have this */
2532 /* allow this request to replace selected attrs */
2533 attrs_mod
= RGWRados::ATTRSMOD_MERGE
;
2536 bool only_bucket() override
{ return true; }
2538 int op_init() override
{
2539 // assign store, s, and dialect_handler
2540 RGWObjectCtx
* rados_ctx
2541 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2542 // framework promises to call op_init after parent init
2543 ceph_assert(rados_ctx
);
2544 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2545 op
= this; // assign self as op: REQUIRED
2550 int header_init() override
{
2552 struct req_state
* s
= get_state();
2553 s
->info
.method
= "PUT"; // XXX check
2556 src_bucket_name
= src_parent
->bucket_name();
2557 // need s->src_bucket_name?
2558 src_object
.name
= src_parent
->format_child_name(src_name
, false);
2559 // need s->src_object?
2561 dest_bucket_name
= dst_parent
->bucket_name();
2562 // need s->bucket.name?
2563 dest_object
= dst_parent
->format_child_name(dst_name
, false);
2564 // need s->object_name?
2566 int rc
= valid_s3_object_name(dest_object
);
2570 /* XXX and fixup key attr (could optimize w/string ref and
2572 buffer::list ux_key
;
2573 fh_key fhk
= dst_parent
->make_fhk(dst_name
);
2574 rgw::encode(fhk
, ux_key
);
2575 emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
2577 #if 0 /* XXX needed? */
2578 s
->relative_uri
= uri
;
2579 s
->info
.request_uri
= uri
; // XXX
2580 s
->info
.effective_uri
= uri
;
2581 s
->info
.request_params
= "";
2582 s
->info
.domain
= ""; /* XXX ? */
2587 s
->bucket_tenant
= user
->user_id
.tenant
;
2592 int get_params() override
{
2593 struct req_state
* s
= get_state();
2594 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2595 /* we don't have (any) headers, so just create canned ACLs */
2596 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
2597 dest_policy
= s3policy
;
2601 void send_response() override
{}
2602 void send_partial_response(off_t ofs
) override
{}
2604 }; /* RGWCopyObjRequest */
2606 class RGWSetAttrsRequest
: public RGWLibRequest
,
2607 public RGWSetAttrs
/* RGWOp */
2610 const std::string
& bucket_name
;
2611 const std::string
& obj_name
;
2613 RGWSetAttrsRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2614 const std::string
& _bname
, const std::string
& _oname
)
2615 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
) {
2619 bool only_bucket() override
{ return false; }
2621 int op_init() override
{
2622 // assign store, s, and dialect_handler
2623 RGWObjectCtx
* rados_ctx
2624 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2625 // framework promises to call op_init after parent init
2626 ceph_assert(rados_ctx
);
2627 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2628 op
= this; // assign self as op: REQUIRED
2632 int header_init() override
{
2634 struct req_state
* s
= get_state();
2635 s
->info
.method
= "PUT";
2638 /* XXX derp derp derp */
2639 std::string uri
= make_uri(bucket_name
, obj_name
);
2640 s
->relative_uri
= uri
;
2641 s
->info
.request_uri
= uri
; // XXX
2642 s
->info
.effective_uri
= uri
;
2643 s
->info
.request_params
= "";
2644 s
->info
.domain
= ""; /* XXX ? */
2648 s
->bucket_tenant
= user
->user_id
.tenant
;
2653 int get_params() override
{
2657 void send_response() override
{}
2659 }; /* RGWSetAttrsRequest */
2662 * Send request to get the rados cluster stats
2664 class RGWGetClusterStatReq
: public RGWLibRequest
,
2665 public RGWGetClusterStat
{
2667 struct rados_cluster_stat_t
& stats_req
;
2668 RGWGetClusterStatReq(CephContext
* _cct
,RGWUserInfo
*_user
,
2669 rados_cluster_stat_t
& _stats
):
2670 RGWLibRequest(_cct
, _user
), stats_req(_stats
){
2674 int op_init() override
{
2675 // assign store, s, and dialect_handler
2676 RGWObjectCtx
* rados_ctx
2677 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2678 // framework promises to call op_init after parent init
2679 ceph_assert(rados_ctx
);
2680 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2681 op
= this; // assign self as op: REQUIRED
2685 int header_init() override
{
2686 struct req_state
* s
= get_state();
2687 s
->info
.method
= "GET";
2693 int get_params() override
{ return 0; }
2694 bool only_bucket() override
{ return false; }
2695 void send_response() override
{
2696 stats_req
.kb
= stats_op
.kb
;
2697 stats_req
.kb_avail
= stats_op
.kb_avail
;
2698 stats_req
.kb_used
= stats_op
.kb_used
;
2699 stats_req
.num_objects
= stats_op
.num_objects
;
2701 }; /* RGWGetClusterStatReq */
2704 } /* namespace rgw */
2706 #endif /* RGW_FILE_H */