1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
7 #include "include/rados/rgw_file.h"
11 #include <string_view>
23 #include <boost/intrusive_ptr.hpp>
24 #include <boost/range/adaptor/reversed.hpp>
25 #include <boost/container/flat_map.hpp>
26 #include <boost/variant.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 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 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), unix_mode(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;
271 static constexpr uint32_t FLAG_IN_CB
= 0x2000;
273 #define CREATE_FLAGS(x) \
274 ((x) & ~(RGWFileHandle::FLAG_CREATE|RGWFileHandle::FLAG_LOCK))
276 static constexpr uint32_t RCB_MASK
= \
277 RGW_SETATTR_MTIME
|RGW_SETATTR_CTIME
|RGW_SETATTR_ATIME
|RGW_SETATTR_SIZE
;
279 friend class RGWLibFS
;
282 explicit RGWFileHandle(RGWLibFS
* _fs
)
283 : fs(_fs
), bucket(nullptr), parent(nullptr), variant_type
{directory()},
284 depth(0), flags(FLAG_NONE
)
289 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
290 variant_type
= directory();
292 state
.unix_mode
= RGW_RWXMODE
|S_IFDIR
;
293 /* pointer to self */
294 fh
.fh_private
= this;
297 uint64_t init_fsid(std::string
& uid
) {
298 return XXH64(uid
.c_str(), uid
.length(), fh_key::seed
);
301 void init_rootfs(std::string
& fsid
, const std::string
& object_name
,
304 fh
.fh_hk
.bucket
= XXH64(fsid
.c_str(), fsid
.length(), fh_key::seed
);
305 fh
.fh_hk
.object
= XXH64(object_name
.c_str(), object_name
.length(),
310 state
.dev
= init_fsid(fsid
);
313 flags
|= RGWFileHandle::FLAG_BUCKET
| RGWFileHandle::FLAG_MOUNT
;
317 flags
|= RGWFileHandle::FLAG_ROOT
| RGWFileHandle::FLAG_MOUNT
;
322 RGWFileHandle(RGWLibFS
* _fs
, RGWFileHandle
* _parent
,
323 const fh_key
& _fhk
, std::string
& _name
, uint32_t _flags
)
324 : fs(_fs
), bucket(nullptr), parent(_parent
), name(std::move(_name
)),
325 fhk(_fhk
), flags(_flags
) {
327 if (parent
->is_root()) {
328 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
329 variant_type
= directory();
330 flags
|= FLAG_BUCKET
;
332 bucket
= parent
->is_bucket() ? parent
334 if (flags
& FLAG_DIRECTORY
) {
335 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
336 variant_type
= directory();
337 } else if(flags
& FLAG_SYMBOLIC_LINK
) {
338 fh
.fh_type
= RGW_FS_TYPE_SYMBOLIC_LINK
;
339 variant_type
= file();
341 fh
.fh_type
= RGW_FS_TYPE_FILE
;
342 variant_type
= file();
346 depth
= parent
->depth
+ 1;
348 /* save constant fhk */
349 fh
.fh_hk
= fhk
.fh_hk
; /* XXX redundant in fh_hk */
351 /* inherits parent's fsid */
352 state
.dev
= parent
->state
.dev
;
354 switch (fh
.fh_type
) {
355 case RGW_FS_TYPE_DIRECTORY
:
356 state
.unix_mode
= RGW_RWXMODE
|S_IFDIR
;
357 /* virtual directories are always invalid */
360 case RGW_FS_TYPE_FILE
:
361 state
.unix_mode
= RGW_RWMODE
|S_IFREG
;
363 case RGW_FS_TYPE_SYMBOLIC_LINK
:
364 state
.unix_mode
= RGW_RWMODE
|S_IFLNK
;
370 /* pointer to self */
371 fh
.fh_private
= this;
374 const std::string
& get_name() const {
378 const fh_key
& get_key() const {
382 directory
* get_directory() {
383 return boost::get
<directory
>(&variant_type
);
386 size_t get_size() const { return state
.size
; }
388 const char* stype() {
389 return is_dir() ? "DIR" : "FILE";
392 uint16_t get_depth() const { return depth
; }
394 struct rgw_file_handle
* get_fh() { return &fh
; }
396 RGWLibFS
* get_fs() { return fs
; }
398 RGWFileHandle
* get_parent() { return parent
; }
400 uint32_t get_owner_uid() const { return state
.owner_uid
; }
401 uint32_t get_owner_gid() const { return state
.owner_gid
; }
403 struct timespec
get_ctime() const { return state
.ctime
; }
404 struct timespec
get_mtime() const { return state
.mtime
; }
406 const ceph::buffer::list
& get_etag() const { return etag
; }
407 const ceph::buffer::list
& get_acls() const { return acls
; }
409 void create_stat(struct stat
* st
, uint32_t mask
) {
410 if (mask
& RGW_SETATTR_UID
)
411 state
.owner_uid
= st
->st_uid
;
413 if (mask
& RGW_SETATTR_GID
)
414 state
.owner_gid
= st
->st_gid
;
416 if (mask
& RGW_SETATTR_MODE
) {
417 switch (fh
.fh_type
) {
418 case RGW_FS_TYPE_DIRECTORY
:
419 state
.unix_mode
= st
->st_mode
|S_IFDIR
;
421 case RGW_FS_TYPE_FILE
:
422 state
.unix_mode
= st
->st_mode
|S_IFREG
;
424 case RGW_FS_TYPE_SYMBOLIC_LINK
:
425 state
.unix_mode
= st
->st_mode
|S_IFLNK
;
432 if (mask
& RGW_SETATTR_ATIME
)
433 state
.atime
= st
->st_atim
;
435 if (mask
& RGW_SETATTR_MTIME
) {
436 if (fh
.fh_type
!= RGW_FS_TYPE_DIRECTORY
)
437 state
.mtime
= st
->st_mtim
;
440 if (mask
& RGW_SETATTR_CTIME
)
441 state
.ctime
= st
->st_ctim
;
444 int stat(struct stat
* st
, uint32_t flags
= FLAG_NONE
) {
445 /* partial Unix attrs */
446 /* FIPS zeroization audit 20191115: this memset is not security
448 memset(st
, 0, sizeof(struct stat
));
449 st
->st_dev
= state
.dev
;
450 st
->st_ino
= fh
.fh_hk
.object
; // XXX
452 st
->st_uid
= state
.owner_uid
;
453 st
->st_gid
= state
.owner_gid
;
455 st
->st_mode
= state
.unix_mode
;
457 switch (fh
.fh_type
) {
458 case RGW_FS_TYPE_DIRECTORY
:
459 /* virtual directories are always invalid */
460 advance_mtime(flags
);
461 st
->st_nlink
= state
.nlink
;
463 case RGW_FS_TYPE_FILE
:
465 st
->st_blksize
= 4096;
466 st
->st_size
= state
.size
;
467 st
->st_blocks
= (state
.size
) / 512;
469 case RGW_FS_TYPE_SYMBOLIC_LINK
:
471 st
->st_blksize
= 4096;
472 st
->st_size
= state
.size
;
473 st
->st_blocks
= (state
.size
) / 512;
479 #ifdef HAVE_STAT_ST_MTIMESPEC_TV_NSEC
480 st
->st_atimespec
= state
.atime
;
481 st
->st_mtimespec
= state
.mtime
;
482 st
->st_ctimespec
= state
.ctime
;
484 st
->st_atim
= state
.atime
;
485 st
->st_mtim
= state
.mtime
;
486 st
->st_ctim
= state
.ctime
;
492 const std::string
& bucket_name() const {
497 return bucket
->object_name();
500 const std::string
& object_name() const { return name
; }
502 std::string
full_object_name(bool omit_bucket
= false) const {
504 std::vector
<const std::string
*> segments
;
506 const RGWFileHandle
* tfh
= this;
507 while (tfh
&& !tfh
->is_root() && !(tfh
->is_bucket() && omit_bucket
)) {
508 segments
.push_back(&tfh
->object_name());
509 reserve
+= (1 + tfh
->object_name().length());
513 path
.reserve(reserve
);
514 for (auto& s
: boost::adaptors::reverse(segments
)) {
519 ((path
.length() == 0) || (path
.front() != '/')))
528 inline std::string
relative_object_name() const {
529 return full_object_name(true /* omit_bucket */);
532 inline std::string
relative_object_name2() {
533 std::string rname
= full_object_name(true /* omit_bucket */);
540 inline std::string
format_child_name(const std::string
& cbasename
,
542 std::string child_name
{relative_object_name()};
543 if ((child_name
.size() > 0) &&
544 (child_name
.back() != '/'))
546 child_name
+= cbasename
;
552 inline std::string
make_key_name(const char *name
) const {
553 std::string key_name
{full_object_name()};
554 if (key_name
.length() > 0)
560 fh_key
make_fhk(const std::string
& name
);
562 void add_marker(uint64_t off
, const rgw_obj_key
& marker
,
565 directory
* d
= get
<directory
>(&variant_type
);
567 unique_lock
guard(mtx
);
568 d
->last_marker
= marker
;
572 const rgw_obj_key
* find_marker(uint64_t off
) const {
575 const directory
* d
= get
<directory
>(&variant_type
);
577 return &d
->last_marker
;
583 int offset_of(const std::string
& name
, int64_t *offset
, uint32_t flags
) {
584 if (unlikely(! is_dir())) {
587 *offset
= XXH64(name
.c_str(), name
.length(), fh_key::seed
);
591 bool is_open() const { return flags
& FLAG_OPEN
; }
592 bool is_root() const { return flags
& FLAG_ROOT
; }
593 bool is_mount() const { return flags
& FLAG_MOUNT
; }
594 bool is_bucket() const { return flags
& FLAG_BUCKET
; }
595 bool is_object() const { return !is_bucket(); }
596 bool is_file() const { return (fh
.fh_type
== RGW_FS_TYPE_FILE
); }
597 bool is_dir() const { return (fh
.fh_type
== RGW_FS_TYPE_DIRECTORY
); }
598 bool is_link() const { return (fh
.fh_type
== RGW_FS_TYPE_SYMBOLIC_LINK
); }
599 bool creating() const { return flags
& FLAG_CREATING
; }
600 bool deleted() const { return flags
& FLAG_DELETED
; }
601 bool stateless_open() const { return flags
& FLAG_STATELESS_OPEN
; }
602 bool has_children() const;
604 int open(uint32_t gsh_flags
) {
605 lock_guard
guard(mtx
);
607 if (gsh_flags
& RGW_OPEN_FLAG_V3
) {
608 flags
|= FLAG_STATELESS_OPEN
;
616 typedef boost::variant
<uint64_t*, const char*> readdir_offset
;
618 int readdir(rgw_readdir_cb rcb
, void *cb_arg
, readdir_offset offset
,
619 bool *eof
, uint32_t flags
);
621 int write(uint64_t off
, size_t len
, size_t *nbytes
, void *buffer
);
623 int commit(uint64_t offset
, uint64_t length
, uint32_t flags
) {
624 /* NFS3 and NFSv4 COMMIT implementation
625 * the current atomic update strategy doesn't actually permit
626 * clients to read-stable until either CLOSE (NFSv4+) or the
627 * expiration of the active write timer (NFS3). In the
628 * interim, the client may send an arbitrary number of COMMIT
629 * operations which must return a success result */
633 int write_finish(uint32_t flags
= FLAG_NONE
);
636 void open_for_create() {
637 lock_guard
guard(mtx
);
638 flags
|= FLAG_CREATING
;
641 void clear_creating() {
642 lock_guard
guard(mtx
);
643 flags
&= ~FLAG_CREATING
;
646 void inc_nlink(const uint64_t n
) {
650 void set_nlink(const uint64_t n
) {
654 void set_size(const size_t size
) {
658 void set_times(const struct timespec
&ts
) {
660 state
.mtime
= state
.ctime
;
661 state
.atime
= state
.ctime
;
664 void set_times(real_time t
) {
665 set_times(real_clock::to_timespec(t
));
668 void set_ctime(const struct timespec
&ts
) {
672 void set_mtime(const struct timespec
&ts
) {
676 void set_atime(const struct timespec
&ts
) {
680 void set_etag(const ceph::buffer::list
& _etag
) {
684 void set_acls(const ceph::buffer::list
& _acls
) {
688 void encode(buffer::list
& bl
) const {
689 ENCODE_START(2, 1, bl
);
690 encode(uint32_t(fh
.fh_type
), bl
);
691 encode(state
.dev
, bl
);
692 encode(state
.size
, bl
);
693 encode(state
.nlink
, bl
);
694 encode(state
.owner_uid
, bl
);
695 encode(state
.owner_gid
, bl
);
696 encode(state
.unix_mode
, bl
);
697 for (const auto& t
: { state
.ctime
, state
.mtime
, state
.atime
}) {
698 encode(real_clock::from_timespec(t
), bl
);
700 encode((uint32_t)2, bl
);
704 void decode(bufferlist::const_iterator
& bl
) {
708 if ((fh
.fh_type
!= fh_type
) &&
709 (fh_type
== RGW_FS_TYPE_SYMBOLIC_LINK
))
710 fh
.fh_type
= RGW_FS_TYPE_SYMBOLIC_LINK
;
711 ceph_assert(fh
.fh_type
== fh_type
);
712 decode(state
.dev
, bl
);
713 decode(state
.size
, bl
);
714 decode(state
.nlink
, bl
);
715 decode(state
.owner_uid
, bl
);
716 decode(state
.owner_gid
, bl
);
717 decode(state
.unix_mode
, bl
);
718 ceph::real_time enc_time
;
719 for (auto t
: { &(state
.ctime
), &(state
.mtime
), &(state
.atime
) }) {
720 decode(enc_time
, bl
);
721 *t
= real_clock::to_timespec(enc_time
);
724 decode(state
.version
, bl
);
729 void encode_attrs(ceph::buffer::list
& ux_key1
,
730 ceph::buffer::list
& ux_attrs1
);
732 DecodeAttrsResult
decode_attrs(const ceph::buffer::list
* ux_key1
,
733 const ceph::buffer::list
* ux_attrs1
);
737 bool reclaim(const cohort::lru::ObjectFactory
* newobj_fac
) override
;
739 typedef cohort::lru::LRU
<std::mutex
> FhLRU
;
743 // for internal ordering
744 bool operator()(const RGWFileHandle
& lhs
, const RGWFileHandle
& rhs
) const
745 { return (lhs
.get_key() < rhs
.get_key()); }
747 // for external search by fh_key
748 bool operator()(const fh_key
& k
, const RGWFileHandle
& fh
) const
749 { return k
< fh
.get_key(); }
751 bool operator()(const RGWFileHandle
& fh
, const fh_key
& k
) const
752 { return fh
.get_key() < k
; }
757 bool operator()(const RGWFileHandle
& lhs
, const RGWFileHandle
& rhs
) const
758 { return (lhs
.get_key() == rhs
.get_key()); }
760 bool operator()(const fh_key
& k
, const RGWFileHandle
& fh
) const
761 { return k
== fh
.get_key(); }
763 bool operator()(const RGWFileHandle
& fh
, const fh_key
& k
) const
764 { return fh
.get_key() == k
; }
767 typedef bi::link_mode
<bi::safe_link
> link_mode
; /* XXX normal */
768 #if defined(FHCACHE_AVL)
769 typedef bi::avl_set_member_hook
<link_mode
> tree_hook_type
;
772 typedef bi::set_member_hook
<link_mode
> tree_hook_type
;
774 tree_hook_type fh_hook
;
776 typedef bi::member_hook
<
777 RGWFileHandle
, tree_hook_type
, &RGWFileHandle::fh_hook
> FhHook
;
779 #if defined(FHCACHE_AVL)
780 typedef bi::avltree
<RGWFileHandle
, bi::compare
<FhLT
>, FhHook
> FHTree
;
782 typedef bi::rbtree
<RGWFileHandle
, bi::compare
<FhLT
>, FhHook
> FhTree
;
784 typedef cohort::lru::TreeX
<RGWFileHandle
, FhTree
, FhLT
, FhEQ
, fh_key
,
787 ~RGWFileHandle() override
;
789 friend std::ostream
& operator<<(std::ostream
&os
,
790 RGWFileHandle
const &rgw_fh
);
792 class Factory
: public cohort::lru::ObjectFactory
796 RGWFileHandle
* parent
;
803 Factory(RGWLibFS
* _fs
, RGWFileHandle
* _parent
,
804 const fh_key
& _fhk
, std::string
& _name
, uint32_t _flags
)
805 : fs(_fs
), parent(_parent
), fhk(_fhk
), name(_name
),
808 void recycle (cohort::lru::Object
* o
) override
{
809 /* re-use an existing object */
810 o
->~Object(); // call lru::Object virtual dtor
812 new (o
) RGWFileHandle(fs
, parent
, fhk
, name
, flags
);
815 cohort::lru::Object
* alloc() override
{
816 return new RGWFileHandle(fs
, parent
, fhk
, name
, flags
);
820 }; /* RGWFileHandle */
822 WRITE_CLASS_ENCODER(RGWFileHandle
);
824 inline RGWFileHandle
* get_rgwfh(struct rgw_file_handle
* fh
) {
825 return static_cast<RGWFileHandle
*>(fh
->fh_private
);
828 inline enum rgw_fh_type
fh_type_of(uint32_t flags
) {
829 enum rgw_fh_type fh_type
;
830 switch(flags
& RGW_LOOKUP_TYPE_FLAGS
)
832 case RGW_LOOKUP_FLAG_DIR
:
833 fh_type
= RGW_FS_TYPE_DIRECTORY
;
835 case RGW_LOOKUP_FLAG_FILE
:
836 fh_type
= RGW_FS_TYPE_FILE
;
839 fh_type
= RGW_FS_TYPE_NIL
;
844 typedef std::tuple
<RGWFileHandle
*, uint32_t> LookupFHResult
;
845 typedef std::tuple
<RGWFileHandle
*, int> MkObjResult
;
851 RGWFileHandle root_fh
;
852 rgw_fh_callback_t invalidate_cb
;
853 void *invalidate_arg
;
856 mutable std::atomic
<uint64_t> refcnt
;
858 RGWFileHandle::FHCache fh_cache
;
859 RGWFileHandle::FhLRU fh_lru
;
861 std::string uid
; // should match user.user_id, iiuc
863 std::unique_ptr
<rgw::sal::User
> user
;
864 RGWAccessKey key
; // XXXX acc_key
866 static std::atomic
<uint32_t> fs_inst_counter
;
868 static uint32_t write_completion_interval_s
;
870 using lock_guard
= std::lock_guard
<std::mutex
>;
871 using unique_lock
= std::unique_lock
<std::mutex
>;
875 enum class type
: uint8_t { READDIR
} ;
879 event(type t
, const fh_key
& k
, const struct timespec
& ts
)
880 : t(t
), fhk(k
), ts(ts
) {}
883 friend std::ostream
& operator<<(std::ostream
&os
,
884 RGWLibFS::event
const &ev
);
886 using event_vector
= /* boost::small_vector<event, 16> */
889 struct WriteCompletion
891 RGWFileHandle
& rgw_fh
;
893 explicit WriteCompletion(RGWFileHandle
& _fh
) : rgw_fh(_fh
) {
894 rgw_fh
.get_fs()->ref(&rgw_fh
);
898 rgw_fh
.close(); /* will finish in-progress write */
899 rgw_fh
.get_fs()->unref(&rgw_fh
);
903 static ceph::timer
<ceph::mono_clock
> write_timer
;
907 std::atomic
<uint32_t> flags
;
908 std::deque
<event
> events
;
910 State() : flags(0) {}
912 void push_event(const event
& ev
) {
913 events
.push_back(ev
);
917 uint32_t new_inst() {
918 return ++fs_inst_counter
;
921 friend class RGWFileHandle
;
922 friend class RGWLibProcess
;
926 static constexpr uint32_t FLAG_NONE
= 0x0000;
927 static constexpr uint32_t FLAG_CLOSED
= 0x0001;
932 real_time creation_time
;
933 uint64_t num_entries
;
936 RGWLibFS(CephContext
* _cct
, const char *_uid
, const char *_user_id
,
937 const char* _key
, const char *root
)
938 : cct(_cct
), root_fh(this), invalidate_cb(nullptr),
939 invalidate_arg(nullptr), shutdown(false), refcnt(1),
940 fh_cache(cct
->_conf
->rgw_nfs_fhcache_partitions
,
941 cct
->_conf
->rgw_nfs_fhcache_size
),
942 fh_lru(cct
->_conf
->rgw_nfs_lru_lanes
,
943 cct
->_conf
->rgw_nfs_lru_lane_hiwat
),
944 uid(_uid
), key(_user_id
, _key
) {
946 if (!root
|| !strcmp(root
, "/")) {
947 root_fh
.init_rootfs(uid
, RGWFileHandle::root_name
, false);
949 root_fh
.init_rootfs(uid
, root
, true);
952 /* pointer to self */
953 fs
.fs_private
= this;
955 /* expose public root fh */
956 fs
.root_fh
= root_fh
.get_fh();
961 friend void intrusive_ptr_add_ref(const RGWLibFS
* fs
) {
962 fs
->refcnt
.fetch_add(1, std::memory_order_relaxed
);
965 friend void intrusive_ptr_release(const RGWLibFS
* fs
) {
966 if (fs
->refcnt
.fetch_sub(1, std::memory_order_release
) == 0) {
967 std::atomic_thread_fence(std::memory_order_acquire
);
973 intrusive_ptr_add_ref(this);
978 intrusive_ptr_release(this);
981 void stop() { shutdown
= true; }
983 void release_evict(RGWFileHandle
* fh
) {
984 /* remove from cache, releases sentinel ref */
985 fh_cache
.remove(fh
->fh
.fh_hk
.object
, fh
,
986 RGWFileHandle::FHCache::FLAG_LOCK
);
987 /* release call-path ref */
988 (void) fh_lru
.unref(fh
, cohort::lru::FLAG_NONE
);
991 int authorize(const DoutPrefixProvider
*dpp
, rgw::sal::Store
* store
) {
992 int ret
= store
->get_user_by_access_key(dpp
, key
.id
, null_yield
, &user
);
994 RGWAccessKey
* k
= user
->get_info().get_key(key
.id
);
995 if (!k
|| (k
->key
!= key
.key
))
997 if (user
->get_info().suspended
)
998 return -ERR_USER_SUSPENDED
;
1000 /* try external authenticators (ldap for now) */
1001 rgw::LDAPHelper
* ldh
= rgwlib
.get_ldh(); /* !nullptr */
1003 /* boost filters and/or string_ref may throw on invalid input */
1005 token
= rgw::from_base64(key
.id
);
1007 token
= std::string("");
1009 if (token
.valid() && (ldh
->auth(token
.id
, token
.key
) == 0)) {
1010 /* try to store user if it doesn't already exist */
1011 if (user
->load_user(dpp
, null_yield
) < 0) {
1012 int ret
= user
->store_user(dpp
, null_yield
, true);
1014 lsubdout(get_context(), rgw
, 10)
1015 << "NOTICE: failed to store new user's info: ret=" << ret
1019 } /* auth success */
1024 int register_invalidate(rgw_fh_callback_t cb
, void *arg
, uint32_t flags
) {
1026 invalidate_arg
= arg
;
1030 /* find RGWFileHandle by id */
1031 LookupFHResult
lookup_fh(const fh_key
& fhk
,
1032 const uint32_t flags
= RGWFileHandle::FLAG_NONE
) {
1035 // cast int32_t(RGWFileHandle::FLAG_NONE) due to strictness of Clang
1036 // the cast transfers a lvalue into a rvalue in the ctor
1037 // check the commit message for the full details
1038 LookupFHResult fhr
{ nullptr, uint32_t(RGWFileHandle::FLAG_NONE
) };
1040 RGWFileHandle::FHCache::Latch lat
;
1041 bool fh_locked
= flags
& RGWFileHandle::FLAG_LOCKED
;
1045 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
1046 fhk
/* key */, lat
/* serializer */,
1047 RGWFileHandle::FHCache::FLAG_LOCK
);
1050 if (likely(! fh_locked
))
1051 fh
->mtx
.lock(); // XXX !RAII because may-return-LOCKED
1052 /* need initial ref from LRU (fast path) */
1053 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
1055 if (likely(! fh_locked
))
1057 goto retry
; /* !LATCHED */
1059 /* LATCHED, LOCKED */
1060 if (! (flags
& RGWFileHandle::FLAG_LOCK
))
1061 fh
->mtx
.unlock(); /* ! LOCKED */
1063 lat
.lock
->unlock(); /* !LATCHED */
1066 lsubdout(get_context(), rgw
, 17)
1067 << __func__
<< " 1 " << *fh
1071 } /* lookup_fh(const fh_key&) */
1073 /* find or create an RGWFileHandle */
1074 LookupFHResult
lookup_fh(RGWFileHandle
* parent
, const char *name
,
1075 const uint32_t flags
= RGWFileHandle::FLAG_NONE
) {
1078 // cast int32_t(RGWFileHandle::FLAG_NONE) due to strictness of Clang
1079 // the cast transfers a lvalue into a rvalue in the ctor
1080 // check the commit message for the full details
1081 LookupFHResult fhr
{ nullptr, uint32_t(RGWFileHandle::FLAG_NONE
) };
1083 /* mount is stale? */
1084 if (state
.flags
& FLAG_CLOSED
)
1087 RGWFileHandle::FHCache::Latch lat
;
1088 bool fh_locked
= flags
& RGWFileHandle::FLAG_LOCKED
;
1090 std::string obj_name
{name
};
1091 std::string key_name
{parent
->make_key_name(name
)};
1092 fh_key fhk
= parent
->make_fhk(obj_name
);
1094 lsubdout(get_context(), rgw
, 10)
1095 << __func__
<< " called on "
1096 << parent
->object_name() << " for " << key_name
1097 << " (" << obj_name
<< ")"
1103 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
1104 fhk
/* key */, lat
/* serializer */,
1105 RGWFileHandle::FHCache::FLAG_LOCK
);
1108 if (likely(! fh_locked
))
1109 fh
->mtx
.lock(); // XXX !RAII because may-return-LOCKED
1110 if (fh
->flags
& RGWFileHandle::FLAG_DELETED
) {
1111 /* for now, delay briefly and retry */
1113 if (likely(! fh_locked
))
1115 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1116 goto retry
; /* !LATCHED */
1118 /* need initial ref from LRU (fast path) */
1119 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
1121 if (likely(! fh_locked
))
1123 goto retry
; /* !LATCHED */
1125 /* LATCHED, LOCKED */
1126 if (! (flags
& RGWFileHandle::FLAG_LOCK
))
1127 if (likely(! fh_locked
))
1128 fh
->mtx
.unlock(); /* ! LOCKED */
1130 /* make or re-use handle */
1131 RGWFileHandle::Factory
prototype(this, parent
, fhk
,
1132 obj_name
, CREATE_FLAGS(flags
));
1133 uint32_t iflags
{cohort::lru::FLAG_INITIAL
};
1134 fh
= static_cast<RGWFileHandle
*>(
1135 fh_lru
.insert(&prototype
,
1136 cohort::lru::Edge::MRU
,
1139 /* lock fh (LATCHED) */
1140 if (flags
& RGWFileHandle::FLAG_LOCK
)
1142 if (likely(! (iflags
& cohort::lru::FLAG_RECYCLE
))) {
1143 /* inserts at cached insert iterator, releasing latch */
1144 fh_cache
.insert_latched(
1145 fh
, lat
, RGWFileHandle::FHCache::FLAG_UNLOCK
);
1147 /* recycle step invalidates Latch */
1149 fhk
.fh_hk
.object
, fh
, RGWFileHandle::FHCache::FLAG_NONE
);
1150 lat
.lock
->unlock(); /* !LATCHED */
1152 get
<1>(fhr
) |= RGWFileHandle::FLAG_CREATE
;
1153 /* ref parent (non-initial ref cannot fail on valid object) */
1154 if (! parent
->is_mount()) {
1155 (void) fh_lru
.ref(parent
, cohort::lru::FLAG_NONE
);
1157 goto out
; /* !LATCHED */
1160 goto retry
; /* !LATCHED */
1163 lat
.lock
->unlock(); /* !LATCHED */
1167 lsubdout(get_context(), rgw
, 17)
1168 << __func__
<< " 2 " << *fh
1172 } /* lookup_fh(RGWFileHandle*, const char *, const uint32_t) */
1174 inline void unref(RGWFileHandle
* fh
) {
1175 if (likely(! fh
->is_mount())) {
1176 (void) fh_lru
.unref(fh
, cohort::lru::FLAG_NONE
);
1180 inline RGWFileHandle
* ref(RGWFileHandle
* fh
) {
1181 if (likely(! fh
->is_mount())) {
1182 fh_lru
.ref(fh
, cohort::lru::FLAG_NONE
);
1187 int getattr(RGWFileHandle
* rgw_fh
, struct stat
* st
);
1189 int setattr(RGWFileHandle
* rgw_fh
, struct stat
* st
, uint32_t mask
,
1192 int getxattrs(RGWFileHandle
* rgw_fh
, rgw_xattrlist
* attrs
,
1193 rgw_getxattr_cb cb
, void *cb_arg
, uint32_t flags
);
1195 int lsxattrs(RGWFileHandle
* rgw_fh
, rgw_xattrstr
*filter_prefix
,
1196 rgw_getxattr_cb cb
, void *cb_arg
, uint32_t flags
);
1198 int setxattrs(RGWFileHandle
* rgw_fh
, rgw_xattrlist
* attrs
, uint32_t flags
);
1200 int rmxattrs(RGWFileHandle
* rgw_fh
, rgw_xattrlist
* attrs
, uint32_t flags
);
1202 void update_fh(RGWFileHandle
*rgw_fh
);
1204 LookupFHResult
stat_bucket(RGWFileHandle
* parent
, const char *path
,
1205 RGWLibFS::BucketStats
& bs
,
1208 LookupFHResult
fake_leaf(RGWFileHandle
* parent
, const char *path
,
1209 enum rgw_fh_type type
= RGW_FS_TYPE_NIL
,
1210 struct stat
*st
= nullptr, uint32_t mask
= 0,
1211 uint32_t flags
= RGWFileHandle::FLAG_NONE
);
1213 LookupFHResult
stat_leaf(RGWFileHandle
* parent
, const char *path
,
1214 enum rgw_fh_type type
= RGW_FS_TYPE_NIL
,
1215 uint32_t flags
= RGWFileHandle::FLAG_NONE
);
1217 int read(RGWFileHandle
* rgw_fh
, uint64_t offset
, size_t length
,
1218 size_t* bytes_read
, void* buffer
, uint32_t flags
);
1220 int readlink(RGWFileHandle
* rgw_fh
, uint64_t offset
, size_t length
,
1221 size_t* bytes_read
, void* buffer
, uint32_t flags
);
1223 int rename(RGWFileHandle
* old_fh
, RGWFileHandle
* new_fh
,
1224 const char *old_name
, const char *new_name
);
1226 MkObjResult
create(RGWFileHandle
* parent
, const char *name
, struct stat
*st
,
1227 uint32_t mask
, uint32_t flags
);
1229 MkObjResult
symlink(RGWFileHandle
* parent
, const char *name
,
1230 const char *link_path
, struct stat
*st
, uint32_t mask
, uint32_t flags
);
1232 MkObjResult
mkdir(RGWFileHandle
* parent
, const char *name
, struct stat
*st
,
1233 uint32_t mask
, uint32_t flags
);
1235 int unlink(RGWFileHandle
* rgw_fh
, const char *name
,
1236 uint32_t flags
= FLAG_NONE
);
1238 /* find existing RGWFileHandle */
1239 RGWFileHandle
* lookup_handle(struct rgw_fh_hk fh_hk
) {
1241 if (state
.flags
& FLAG_CLOSED
)
1244 RGWFileHandle::FHCache::Latch lat
;
1249 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
1250 fhk
/* key */, lat
/* serializer */,
1251 RGWFileHandle::FHCache::FLAG_LOCK
);
1254 if (unlikely(fhk
== root_fh
.fh
.fh_hk
)) {
1255 /* lookup for root of this fs */
1259 lsubdout(get_context(), rgw
, 0)
1260 << __func__
<< " handle lookup failed " << fhk
1265 if (fh
->flags
& RGWFileHandle::FLAG_DELETED
) {
1266 /* for now, delay briefly and retry */
1268 fh
->mtx
.unlock(); /* !LOCKED */
1269 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1270 goto retry
; /* !LATCHED */
1272 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
1275 goto retry
; /* !LATCHED */
1278 fh
->mtx
.unlock(); /* !LOCKED */
1280 lat
.lock
->unlock(); /* !LATCHED */
1282 /* special case: lookup root_fh */
1284 if (unlikely(fh_hk
== root_fh
.fh
.fh_hk
)) {
1292 CephContext
* get_context() {
1296 struct rgw_fs
* get_fs() { return &fs
; }
1298 RGWFileHandle
& get_fh() { return root_fh
; }
1300 uint64_t get_fsid() { return root_fh
.state
.dev
; }
1302 RGWUserInfo
* get_user() { return &user
->get_info(); }
1304 void update_user(const DoutPrefixProvider
*dpp
) {
1305 (void) rgwlib
.get_store()->get_user_by_access_key(dpp
, key
.id
, null_yield
, &user
);
1312 static inline std::string
make_uri(const std::string
& bucket_name
,
1313 const std::string
& object_name
) {
1314 std::string
uri("/");
1315 uri
.reserve(bucket_name
.length() + object_name
.length() + 2);
1323 read directory content (buckets)
1326 class RGWListBucketsRequest
: public RGWLibRequest
,
1327 public RGWListBuckets
/* RGWOp */
1330 RGWFileHandle
* rgw_fh
;
1331 RGWFileHandle::readdir_offset offset
;
1337 bool rcb_eof
; // caller forced early stop in readdir cycle
1339 RGWListBucketsRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
1340 RGWFileHandle
* _rgw_fh
, rgw_readdir_cb _rcb
,
1341 void* _cb_arg
, RGWFileHandle::readdir_offset
& _offset
)
1342 : RGWLibRequest(_cct
, std::move(_user
)), rgw_fh(_rgw_fh
), offset(_offset
),
1343 cb_arg(_cb_arg
), rcb(_rcb
), ioff(nullptr), ix(0), d_count(0),
1348 if (unlikely(!! get
<uint64_t*>(&offset
))) {
1349 ioff
= get
<uint64_t*>(offset
);
1350 const auto& mk
= rgw_fh
->find_marker(*ioff
);
1355 const char* mk
= get
<const char*>(offset
);
1363 bool only_bucket() override
{ return false; }
1365 int op_init() override
{
1366 // assign store, s, and dialect_handler
1367 RGWObjectCtx
* rados_ctx
1368 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1369 // framework promises to call op_init after parent init
1370 ceph_assert(rados_ctx
);
1371 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1372 op
= this; // assign self as op: REQUIRED
1376 int header_init() override
{
1377 struct req_state
* state
= get_state();
1378 state
->info
.method
= "GET";
1381 /* XXX derp derp derp */
1382 state
->relative_uri
= "/";
1383 state
->info
.request_uri
= "/"; // XXX
1384 state
->info
.effective_uri
= "/";
1385 state
->info
.request_params
= "";
1386 state
->info
.domain
= ""; /* XXX ? */
1391 int get_params(optional_yield
) override
{
1392 limit
= -1; /* no limit */
1396 void send_response_begin(bool has_buckets
) override
{
1400 void send_response_data(rgw::sal::BucketList
& buckets
) override
{
1403 auto& m
= buckets
.get_buckets();
1404 for (const auto& iter
: m
) {
1405 std::string_view marker
{iter
.first
};
1406 auto& ent
= iter
.second
;
1407 if (! this->operator()(ent
->get_name(), marker
)) {
1408 /* caller cannot accept more */
1409 lsubdout(cct
, rgw
, 5) << "ListBuckets rcb failed"
1410 << " dirent=" << ent
->get_name()
1411 << " call count=" << ix
1418 } /* send_response_data */
1420 void send_response_end() override
{
1424 int operator()(const std::string_view
& name
,
1425 const std::string_view
& marker
) {
1426 uint64_t off
= XXH64(name
.data(), name
.length(), fh_key::seed
);
1430 /* update traversal cache */
1431 rgw_fh
->add_marker(off
, rgw_obj_key
{marker
.data(), ""},
1432 RGW_FS_TYPE_DIRECTORY
);
1434 return rcb(name
.data(), cb_arg
, off
, nullptr, 0, RGW_LOOKUP_FLAG_DIR
);
1440 if (unlikely(cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15))) {
1442 unlikely(! get
<const char*>(&offset
)) ||
1443 !! get
<const char*>(offset
);
1444 lsubdout(cct
, rgw
, 15) << "READDIR offset: " <<
1445 ((is_offset
) ? offset
: "(nil)")
1446 << " is_truncated: " << is_truncated
1449 return !is_truncated
&& !rcb_eof
;
1452 }; /* RGWListBucketsRequest */
1455 read directory content (bucket objects)
1458 class RGWReaddirRequest
: public RGWLibRequest
,
1459 public RGWListBucket
/* RGWOp */
1462 RGWFileHandle
* rgw_fh
;
1463 RGWFileHandle::readdir_offset offset
;
1469 bool rcb_eof
; // caller forced early stop in readdir cycle
1471 RGWReaddirRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
1472 RGWFileHandle
* _rgw_fh
, rgw_readdir_cb _rcb
,
1473 void* _cb_arg
, RGWFileHandle::readdir_offset
& _offset
)
1474 : RGWLibRequest(_cct
, std::move(_user
)), rgw_fh(_rgw_fh
), offset(_offset
),
1475 cb_arg(_cb_arg
), rcb(_rcb
), ioff(nullptr), ix(0), d_count(0),
1480 if (unlikely(!! get
<uint64_t*>(&offset
))) {
1481 ioff
= get
<uint64_t*>(offset
);
1482 const auto& mk
= rgw_fh
->find_marker(*ioff
);
1487 const char* mk
= get
<const char*>(offset
);
1489 std::string tmark
{rgw_fh
->relative_object_name()};
1490 if (tmark
.length() > 0)
1493 marker
= rgw_obj_key
{std::move(tmark
), "", ""};
1497 default_max
= 1000; // XXX was being omitted
1501 bool only_bucket() override
{ return true; }
1503 int op_init() override
{
1504 // assign store, s, and dialect_handler
1505 RGWObjectCtx
* rados_ctx
1506 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1507 // framework promises to call op_init after parent init
1508 ceph_assert(rados_ctx
);
1509 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1510 op
= this; // assign self as op: REQUIRED
1514 int header_init() override
{
1515 struct req_state
* state
= get_state();
1516 state
->info
.method
= "GET";
1519 /* XXX derp derp derp */
1520 std::string uri
= "/" + rgw_fh
->bucket_name() + "/";
1521 state
->relative_uri
= uri
;
1522 state
->info
.request_uri
= uri
; // XXX
1523 state
->info
.effective_uri
= uri
;
1524 state
->info
.request_params
= "";
1525 state
->info
.domain
= ""; /* XXX ? */
1527 prefix
= rgw_fh
->relative_object_name();
1528 if (prefix
.length() > 0)
1535 int operator()(const std::string_view name
, const rgw_obj_key
& marker
,
1536 const ceph::real_time
& t
, const uint64_t fsz
, uint8_t type
) {
1538 assert(name
.length() > 0); // all cases handled in callers
1540 /* hash offset of name in parent (short name) for NFS readdir cookie */
1541 uint64_t off
= XXH64(name
.data(), name
.length(), fh_key::seed
);
1542 if (unlikely(!! ioff
)) {
1546 /* update traversal cache */
1547 rgw_fh
->add_marker(off
, marker
, type
);
1550 /* set c/mtime and size from bucket index entry */
1551 struct stat st
= {};
1552 #ifdef HAVE_STAT_ST_MTIMESPEC_TV_NSEC
1553 st
.st_atimespec
= ceph::real_clock::to_timespec(t
);
1554 st
.st_mtimespec
= st
.st_atimespec
;
1555 st
.st_ctimespec
= st
.st_atimespec
;
1557 st
.st_atim
= ceph::real_clock::to_timespec(t
);
1558 st
.st_mtim
= st
.st_atim
;
1559 st
.st_ctim
= st
.st_atim
;
1563 return rcb(name
.data(), cb_arg
, off
, &st
, RGWFileHandle::RCB_MASK
,
1564 (type
== RGW_FS_TYPE_DIRECTORY
) ?
1565 RGW_LOOKUP_FLAG_DIR
:
1566 RGW_LOOKUP_FLAG_FILE
);
1569 int get_params(optional_yield
) override
{
1574 void send_response() override
{
1575 struct req_state
* state
= get_state();
1576 auto cnow
= real_clock::now();
1578 /* enumerate objs and common_prefixes in parallel,
1579 * avoiding increment on and end iterator, which is
1584 std::vector
<rgw_bucket_dir_entry
>& objs
;
1585 std::vector
<rgw_bucket_dir_entry
>::iterator obj_iter
;
1587 std::map
<std::string
, bool>& common_prefixes
;
1588 std::map
<string
, bool>::iterator cp_iter
;
1590 boost::optional
<std::string_view
> obj_sref
;
1591 boost::optional
<std::string_view
> cp_sref
;
1596 DirIterator(std::vector
<rgw_bucket_dir_entry
>& objs
,
1597 std::map
<string
, bool>& common_prefixes
)
1598 : objs(objs
), common_prefixes(common_prefixes
), _skip_cp(false)
1600 obj_iter
= objs
.begin();
1602 cp_iter
= common_prefixes
.begin();
1607 return (obj_iter
!= objs
.end());
1611 return (cp_iter
!= common_prefixes
.end());
1615 return ((!is_obj()) && (!is_cp()));
1620 std::string_view sref
{obj_iter
->key
.name
};
1621 size_t last_del
= sref
.find_last_of('/');
1622 if (last_del
!= string::npos
)
1623 sref
.remove_prefix(last_del
+1);
1635 /* leading-/ skip case */
1636 if (cp_iter
->first
== "/") {
1642 /* it's safest to modify the element in place--a suffix-modifying
1643 * string_ref operation is problematic since ULP rgw_file callers
1644 * will ultimately need a c-string */
1645 if (cp_iter
->first
.back() == '/')
1646 const_cast<std::string
&>(cp_iter
->first
).pop_back();
1648 std::string_view sref
{cp_iter
->first
};
1649 size_t last_del
= sref
.find_last_of('/');
1650 if (last_del
!= string::npos
)
1651 sref
.remove_prefix(last_del
+1);
1665 bool entry_is_obj() {
1668 (obj_sref
.get() < cp_sref
.get())));
1671 std::string_view
get_obj_sref() {
1672 return obj_sref
.get();
1675 std::string_view
get_cp_sref() {
1676 return cp_sref
.get();
1679 std::vector
<rgw_bucket_dir_entry
>::iterator
& get_obj_iter() {
1683 std::map
<string
, bool>::iterator
& get_cp_iter() {
1687 }; /* DirIterator */
1689 DirIterator di
{objs
, common_prefixes
};
1697 /* assert: one of is_obj() || is_cp() holds */
1698 if (di
.entry_is_obj()) {
1699 auto sref
= di
.get_obj_sref();
1701 /* recursive list of a leaf dir (iirc), do nothing */
1703 /* send a file entry */
1704 auto obj_entry
= *(di
.get_obj_iter());
1706 lsubdout(cct
, rgw
, 15) << "RGWReaddirRequest "
1708 << "list uri=" << state
->relative_uri
<< " "
1709 << " prefix=" << prefix
<< " "
1710 << " obj path=" << obj_entry
.key
.name
1711 << " (" << sref
<< ")" << ""
1713 << real_clock::to_time_t(obj_entry
.meta
.mtime
)
1714 << " size=" << obj_entry
.meta
.accounted_size
1717 if (! this->operator()(sref
, next_marker
, obj_entry
.meta
.mtime
,
1718 obj_entry
.meta
.accounted_size
,
1719 RGW_FS_TYPE_FILE
)) {
1720 /* caller cannot accept more */
1721 lsubdout(cct
, rgw
, 5) << "readdir rcb caller signalled stop"
1722 << " dirent=" << sref
.data()
1723 << " call count=" << ix
1729 di
.next_obj(); // and advance object
1731 /* send a dir entry */
1732 if (! di
.skip_cp()) {
1733 auto sref
= di
.get_cp_sref();
1735 lsubdout(cct
, rgw
, 15) << "RGWReaddirRequest "
1737 << "list uri=" << state
->relative_uri
<< " "
1738 << " prefix=" << prefix
<< " "
1739 << " cpref=" << sref
1743 /* null path segment--could be created in S3 but has no NFS
1746 if (! this->operator()(sref
, next_marker
, cnow
, 0,
1747 RGW_FS_TYPE_DIRECTORY
)) {
1748 /* caller cannot accept more */
1749 lsubdout(cct
, rgw
, 5) << "readdir rcb caller signalled stop"
1750 << " dirent=" << sref
.data()
1751 << " call count=" << ix
1758 di
.next_cp(); // and advance common_prefixes
1759 } /* ! di.entry_is_obj() */
1763 virtual void send_versioned_response() {
1770 if (unlikely(cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15))) {
1772 unlikely(! get
<const char*>(&offset
)) ||
1773 !! get
<const char*>(offset
);
1774 lsubdout(cct
, rgw
, 15) << "READDIR offset: " <<
1775 ((is_offset
) ? offset
: "(nil)")
1776 << " next marker: " << next_marker
1777 << " is_truncated: " << is_truncated
1780 return !is_truncated
&& !rcb_eof
;
1783 }; /* RGWReaddirRequest */
1786 dir has-children predicate (bucket objects)
1789 class RGWRMdirCheck
: public RGWLibRequest
,
1790 public RGWListBucket
/* RGWOp */
1793 const RGWFileHandle
* rgw_fh
;
1797 RGWRMdirCheck (CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
1798 const RGWFileHandle
* _rgw_fh
)
1799 : RGWLibRequest(_cct
, std::move(_user
)), rgw_fh(_rgw_fh
), valid(false),
1800 has_children(false) {
1805 bool only_bucket() override
{ return true; }
1807 int op_init() override
{
1808 // assign store, s, and dialect_handler
1809 RGWObjectCtx
* rados_ctx
1810 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1811 // framework promises to call op_init after parent init
1812 ceph_assert(rados_ctx
);
1813 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1814 op
= this; // assign self as op: REQUIRED
1818 int header_init() override
{
1819 struct req_state
* state
= get_state();
1820 state
->info
.method
= "GET";
1823 std::string uri
= "/" + rgw_fh
->bucket_name() + "/";
1824 state
->relative_uri
= uri
;
1825 state
->info
.request_uri
= uri
;
1826 state
->info
.effective_uri
= uri
;
1827 state
->info
.request_params
= "";
1828 state
->info
.domain
= ""; /* XXX ? */
1830 prefix
= rgw_fh
->relative_object_name();
1831 if (prefix
.length() > 0)
1838 int get_params(optional_yield
) override
{
1843 void send_response() override
{
1845 if ((objs
.size() > 1) ||
1847 (objs
.front().key
.name
!= prefix
))) {
1848 has_children
= true;
1851 for (auto& iter
: common_prefixes
) {
1852 /* readdir never produces a name for this case */
1853 if (iter
.first
== "/")
1855 has_children
= true;
1860 virtual void send_versioned_response() {
1864 }; /* RGWRMdirCheck */
1870 class RGWCreateBucketRequest
: public RGWLibRequest
,
1871 public RGWCreateBucket
/* RGWOp */
1874 const std::string
& bucket_name
;
1876 RGWCreateBucketRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
1877 std::string
& _bname
)
1878 : RGWLibRequest(_cct
, std::move(_user
)), bucket_name(_bname
) {
1882 bool only_bucket() override
{ return false; }
1884 int read_permissions(RGWOp
* op_obj
, optional_yield
) override
{
1885 /* we ARE a 'create bucket' request (cf. rgw_rest.cc, ll. 1305-6) */
1889 int op_init() override
{
1890 // assign store, s, and dialect_handler
1891 RGWObjectCtx
* rados_ctx
1892 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1893 // framework promises to call op_init after parent init
1894 ceph_assert(rados_ctx
);
1895 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1896 op
= this; // assign self as op: REQUIRED
1900 int header_init() override
{
1902 struct req_state
* state
= get_state();
1903 state
->info
.method
= "PUT";
1906 string uri
= "/" + bucket_name
;
1907 /* XXX derp derp derp */
1908 state
->relative_uri
= uri
;
1909 state
->info
.request_uri
= uri
; // XXX
1910 state
->info
.effective_uri
= uri
;
1911 state
->info
.request_params
= "";
1912 state
->info
.domain
= ""; /* XXX ? */
1917 int get_params(optional_yield
) override
{
1918 struct req_state
* state
= get_state();
1919 RGWAccessControlPolicy_S3
s3policy(state
->cct
);
1920 /* we don't have (any) headers, so just create canned ACLs */
1921 int ret
= s3policy
.create_canned(state
->owner
, state
->bucket_owner
, state
->canned_acl
);
1926 void send_response() override
{
1927 /* TODO: something (maybe) */
1929 }; /* RGWCreateBucketRequest */
1935 class RGWDeleteBucketRequest
: public RGWLibRequest
,
1936 public RGWDeleteBucket
/* RGWOp */
1939 const std::string
& bucket_name
;
1941 RGWDeleteBucketRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
1942 std::string
& _bname
)
1943 : RGWLibRequest(_cct
, std::move(_user
)), bucket_name(_bname
) {
1947 bool only_bucket() override
{ return true; }
1949 int op_init() override
{
1950 // assign store, s, and dialect_handler
1951 RGWObjectCtx
* rados_ctx
1952 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1953 // framework promises to call op_init after parent init
1954 ceph_assert(rados_ctx
);
1955 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
1956 op
= this; // assign self as op: REQUIRED
1960 int header_init() override
{
1962 struct req_state
* state
= get_state();
1963 state
->info
.method
= "DELETE";
1964 state
->op
= OP_DELETE
;
1966 string uri
= "/" + bucket_name
;
1967 /* XXX derp derp derp */
1968 state
->relative_uri
= uri
;
1969 state
->info
.request_uri
= uri
; // XXX
1970 state
->info
.effective_uri
= uri
;
1971 state
->info
.request_params
= "";
1972 state
->info
.domain
= ""; /* XXX ? */
1977 void send_response() override
{}
1979 }; /* RGWDeleteBucketRequest */
1984 class RGWPutObjRequest
: public RGWLibRequest
,
1985 public RGWPutObj
/* RGWOp */
1988 const std::string
& bucket_name
;
1989 const std::string
& obj_name
;
1990 buffer::list
& bl
; /* XXX */
1991 size_t bytes_written
;
1993 RGWPutObjRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
1994 const std::string
& _bname
, const std::string
& _oname
,
1996 : RGWLibRequest(_cct
, std::move(_user
)), bucket_name(_bname
), obj_name(_oname
),
1997 bl(_bl
), bytes_written(0) {
2001 bool only_bucket() override
{ return true; }
2003 int op_init() override
{
2004 // assign store, s, and dialect_handler
2005 RGWObjectCtx
* rados_ctx
2006 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2007 // framework promises to call op_init after parent init
2008 ceph_assert(rados_ctx
);
2009 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2010 op
= this; // assign self as op: REQUIRED
2012 int rc
= valid_s3_object_name(obj_name
);
2019 int header_init() override
{
2021 struct req_state
* state
= get_state();
2022 state
->info
.method
= "PUT";
2025 /* XXX derp derp derp */
2026 std::string uri
= make_uri(bucket_name
, obj_name
);
2027 state
->relative_uri
= uri
;
2028 state
->info
.request_uri
= uri
; // XXX
2029 state
->info
.effective_uri
= uri
;
2030 state
->info
.request_params
= "";
2031 state
->info
.domain
= ""; /* XXX ? */
2033 /* XXX required in RGWOp::execute() */
2034 state
->content_length
= bl
.length();
2039 int get_params(optional_yield
) override
{
2040 struct req_state
* state
= get_state();
2041 RGWAccessControlPolicy_S3
s3policy(state
->cct
);
2042 /* we don't have (any) headers, so just create canned ACLs */
2043 int ret
= s3policy
.create_canned(state
->owner
, state
->bucket_owner
, state
->canned_acl
);
2048 int get_data(buffer::list
& _bl
) override
{
2049 /* XXX for now, use sharing semantics */
2050 _bl
= std::move(bl
);
2051 uint32_t len
= _bl
.length();
2052 bytes_written
+= len
;
2056 void send_response() override
{}
2058 int verify_params() override
{
2059 if (bl
.length() > cct
->_conf
->rgw_max_put_size
)
2060 return -ERR_TOO_LARGE
;
2064 buffer::list
* get_attr(const std::string
& k
) {
2065 auto iter
= attrs
.find(k
);
2066 return (iter
!= attrs
.end()) ? &(iter
->second
) : nullptr;
2069 }; /* RGWPutObjRequest */
2075 class RGWReadRequest
: public RGWLibRequest
,
2076 public RGWGetObj
/* RGWOp */
2079 RGWFileHandle
* rgw_fh
;
2082 size_t read_resid
; /* initialize to len, <= sizeof(ulp_buffer) */
2083 bool do_hexdump
= false;
2085 RGWReadRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
2086 RGWFileHandle
* _rgw_fh
, uint64_t off
, uint64_t len
,
2088 : RGWLibRequest(_cct
, std::move(_user
)), rgw_fh(_rgw_fh
), ulp_buffer(_ulp_buffer
),
2089 nread(0), read_resid(len
) {
2092 /* fixup RGWGetObj (already know range parameters) */
2093 RGWGetObj::range_parsed
= true;
2094 RGWGetObj::get_data
= true; // XXX
2095 RGWGetObj::partial_content
= true;
2096 RGWGetObj::ofs
= off
;
2097 RGWGetObj::end
= off
+ len
;
2100 bool only_bucket() override
{ return false; }
2102 int op_init() override
{
2103 // assign store, s, and dialect_handler
2104 RGWObjectCtx
* rados_ctx
2105 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2106 // framework promises to call op_init after parent init
2107 ceph_assert(rados_ctx
);
2108 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2109 op
= this; // assign self as op: REQUIRED
2113 int header_init() override
{
2115 struct req_state
* state
= get_state();
2116 state
->info
.method
= "GET";
2119 /* XXX derp derp derp */
2120 state
->relative_uri
= make_uri(rgw_fh
->bucket_name(),
2121 rgw_fh
->relative_object_name());
2122 state
->info
.request_uri
= state
->relative_uri
; // XXX
2123 state
->info
.effective_uri
= state
->relative_uri
;
2124 state
->info
.request_params
= "";
2125 state
->info
.domain
= ""; /* XXX ? */
2130 int get_params(optional_yield
) override
{
2134 int send_response_data(ceph::buffer::list
& bl
, off_t bl_off
,
2135 off_t bl_len
) override
{
2137 for (auto& bp
: bl
.buffers()) {
2138 /* if for some reason bl_off indicates the start-of-data is not at
2139 * the current buffer::ptr, skip it and account */
2140 if (bl_off
> bp
.length()) {
2141 bl_off
-= bp
.length();
2144 /* read no more than read_resid */
2145 bytes
= std::min(read_resid
, size_t(bp
.length()-bl_off
));
2146 memcpy(static_cast<char*>(ulp_buffer
)+nread
, bp
.c_str()+bl_off
, bytes
);
2147 read_resid
-= bytes
; /* reduce read_resid by bytes read */
2150 /* stop if we have no residual ulp_buffer */
2157 int send_response_data_error(optional_yield
) override
{
2158 /* S3 implementation just sends nothing--there is no side effect
2159 * to simulate here */
2163 bool prefetch_data() override
{ return false; }
2165 }; /* RGWReadRequest */
2171 class RGWDeleteObjRequest
: public RGWLibRequest
,
2172 public RGWDeleteObj
/* RGWOp */
2175 const std::string
& bucket_name
;
2176 const std::string
& obj_name
;
2178 RGWDeleteObjRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
2179 const std::string
& _bname
, const std::string
& _oname
)
2180 : RGWLibRequest(_cct
, std::move(_user
)), bucket_name(_bname
), obj_name(_oname
) {
2184 bool only_bucket() override
{ return true; }
2186 int op_init() override
{
2187 // assign store, s, and dialect_handler
2188 RGWObjectCtx
* rados_ctx
2189 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2190 // framework promises to call op_init after parent init
2191 ceph_assert(rados_ctx
);
2192 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2193 op
= this; // assign self as op: REQUIRED
2197 int header_init() override
{
2199 struct req_state
* state
= get_state();
2200 state
->info
.method
= "DELETE";
2201 state
->op
= OP_DELETE
;
2203 /* XXX derp derp derp */
2204 std::string uri
= make_uri(bucket_name
, obj_name
);
2205 state
->relative_uri
= uri
;
2206 state
->info
.request_uri
= uri
; // XXX
2207 state
->info
.effective_uri
= uri
;
2208 state
->info
.request_params
= "";
2209 state
->info
.domain
= ""; /* XXX ? */
2214 void send_response() override
{}
2216 }; /* RGWDeleteObjRequest */
2218 class RGWStatObjRequest
: public RGWLibRequest
,
2219 public RGWGetObj
/* RGWOp */
2222 const std::string
& bucket_name
;
2223 const std::string
& obj_name
;
2227 static constexpr uint32_t FLAG_NONE
= 0x000;
2229 RGWStatObjRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
2230 const std::string
& _bname
, const std::string
& _oname
,
2232 : RGWLibRequest(_cct
, std::move(_user
)), bucket_name(_bname
), obj_name(_oname
),
2233 _size(0), flags(_flags
) {
2236 /* fixup RGWGetObj (already know range parameters) */
2237 RGWGetObj::range_parsed
= true;
2238 RGWGetObj::get_data
= false; // XXX
2239 RGWGetObj::partial_content
= true;
2241 RGWGetObj::end
= UINT64_MAX
;
2244 const char* name() const override
{ return "stat_obj"; }
2245 RGWOpType
get_type() override
{ return RGW_OP_STAT_OBJ
; }
2247 real_time
get_mtime() const {
2252 uint64_t get_size() { return _size
; }
2253 real_time
ctime() { return mod_time
; } // XXX
2254 real_time
mtime() { return mod_time
; }
2255 std::map
<string
, bufferlist
>& get_attrs() { return attrs
; }
2257 buffer::list
* get_attr(const std::string
& k
) {
2258 auto iter
= attrs
.find(k
);
2259 return (iter
!= attrs
.end()) ? &(iter
->second
) : nullptr;
2262 bool only_bucket() override
{ return false; }
2264 int op_init() override
{
2265 // assign store, s, and dialect_handler
2266 RGWObjectCtx
* rados_ctx
2267 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2268 // framework promises to call op_init after parent init
2269 ceph_assert(rados_ctx
);
2270 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2271 op
= this; // assign self as op: REQUIRED
2275 int header_init() override
{
2277 struct req_state
* state
= get_state();
2278 state
->info
.method
= "GET";
2281 /* XXX derp derp derp */
2282 state
->relative_uri
= make_uri(bucket_name
, obj_name
);
2283 state
->info
.request_uri
= state
->relative_uri
; // XXX
2284 state
->info
.effective_uri
= state
->relative_uri
;
2285 state
->info
.request_params
= "";
2286 state
->info
.domain
= ""; /* XXX ? */
2291 int get_params(optional_yield
) override
{
2295 int send_response_data(ceph::buffer::list
& _bl
, off_t s_off
,
2296 off_t e_off
) override
{
2298 /* XXX save attrs? */
2302 int send_response_data_error(optional_yield
) override
{
2307 void execute(optional_yield y
) override
{
2308 RGWGetObj::execute(y
);
2309 _size
= get_state()->obj_size
;
2312 }; /* RGWStatObjRequest */
2314 class RGWStatBucketRequest
: public RGWLibRequest
,
2315 public RGWStatBucket
/* RGWOp */
2319 std::map
<std::string
, buffer::list
> attrs
;
2320 RGWLibFS::BucketStats
& bs
;
2322 RGWStatBucketRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
2323 const std::string
& _path
,
2324 RGWLibFS::BucketStats
& _stats
)
2325 : RGWLibRequest(_cct
, std::move(_user
)), bs(_stats
) {
2330 buffer::list
* get_attr(const std::string
& k
) {
2331 auto iter
= attrs
.find(k
);
2332 return (iter
!= attrs
.end()) ? &(iter
->second
) : nullptr;
2335 real_time
get_ctime() const {
2336 return bucket
->get_creation_time();
2339 bool only_bucket() override
{ return false; }
2341 int op_init() override
{
2342 // assign store, s, and dialect_handler
2343 RGWObjectCtx
* rados_ctx
2344 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2345 // framework promises to call op_init after parent init
2346 ceph_assert(rados_ctx
);
2347 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2348 op
= this; // assign self as op: REQUIRED
2352 int header_init() override
{
2354 struct req_state
* state
= get_state();
2355 state
->info
.method
= "GET";
2358 /* XXX derp derp derp */
2359 state
->relative_uri
= uri
;
2360 state
->info
.request_uri
= uri
; // XXX
2361 state
->info
.effective_uri
= uri
;
2362 state
->info
.request_params
= "";
2363 state
->info
.domain
= ""; /* XXX ? */
2368 virtual int get_params() {
2372 void send_response() override
{
2373 bucket
->get_creation_time() = get_state()->bucket
->get_info().creation_time
;
2374 bs
.size
= bucket
->get_size();
2375 bs
.size_rounded
= bucket
->get_size_rounded();
2376 bs
.creation_time
= bucket
->get_creation_time();
2377 bs
.num_entries
= bucket
->get_count();
2378 std::swap(attrs
, get_state()->bucket_attrs
);
2382 return (bucket
->get_name().length() > 0);
2385 }; /* RGWStatBucketRequest */
2387 class RGWStatLeafRequest
: public RGWLibRequest
,
2388 public RGWListBucket
/* RGWOp */
2391 RGWFileHandle
* rgw_fh
;
2397 RGWStatLeafRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
2398 RGWFileHandle
* _rgw_fh
, const std::string
& _path
)
2399 : RGWLibRequest(_cct
, std::move(_user
)), rgw_fh(_rgw_fh
), path(_path
),
2400 matched(false), is_dir(false), exact_matched(false) {
2401 default_max
= 1000; // logical max {"foo", "foo/"}
2405 bool only_bucket() override
{ return true; }
2407 int op_init() override
{
2408 // assign store, s, and dialect_handler
2409 RGWObjectCtx
* rados_ctx
2410 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2411 // framework promises to call op_init after parent init
2412 ceph_assert(rados_ctx
);
2413 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2414 op
= this; // assign self as op: REQUIRED
2418 int header_init() override
{
2420 struct req_state
* state
= get_state();
2421 state
->info
.method
= "GET";
2424 /* XXX derp derp derp */
2425 std::string uri
= "/" + rgw_fh
->bucket_name() + "/";
2426 state
->relative_uri
= uri
;
2427 state
->info
.request_uri
= uri
; // XXX
2428 state
->info
.effective_uri
= uri
;
2429 state
->info
.request_params
= "";
2430 state
->info
.domain
= ""; /* XXX ? */
2432 prefix
= rgw_fh
->relative_object_name();
2433 if (prefix
.length() > 0)
2441 int get_params(optional_yield
) override
{
2446 void send_response() override
{
2447 struct req_state
* state
= get_state();
2449 for (const auto& iter
: objs
) {
2450 auto& name
= iter
.key
.name
;
2451 lsubdout(cct
, rgw
, 15) << "RGWStatLeafRequest "
2453 << "list uri=" << state
->relative_uri
<< " "
2454 << " prefix=" << prefix
<< " "
2455 << " obj path=" << name
<< ""
2456 << " target = " << path
<< ""
2458 /* XXX is there a missing match-dir case (trailing '/')? */
2461 exact_matched
= true;
2466 for (auto& iter
: common_prefixes
) {
2467 auto& name
= iter
.first
;
2468 lsubdout(cct
, rgw
, 15) << "RGWStatLeafRequest "
2470 << "list uri=" << state
->relative_uri
<< " "
2471 << " prefix=" << prefix
<< " "
2472 << " pref path=" << name
<< " (not chomped)"
2473 << " target = " << path
<< ""
2476 /* match-dir case (trailing '/') */
2477 if (name
== prefix
+ "/") {
2478 exact_matched
= true;
2485 virtual void send_versioned_response() {
2488 }; /* RGWStatLeafRequest */
2494 class RGWWriteRequest
: public RGWLibContinuedReq
,
2495 public RGWPutObj
/* RGWOp */
2498 const std::string
& bucket_name
;
2499 const std::string
& obj_name
;
2500 RGWFileHandle
* rgw_fh
;
2501 std::optional
<rgw::BlockingAioThrottle
> aio
;
2502 std::unique_ptr
<rgw::sal::Writer
> processor
;
2503 rgw::sal::DataProcessor
* filter
;
2504 boost::optional
<RGWPutObj_Compress
> compressor
;
2505 CompressorRef plugin
;
2510 size_t bytes_written
;
2513 RGWWriteRequest(rgw::sal::Store
* store
,
2514 std::unique_ptr
<rgw::sal::User
> _user
,
2515 RGWFileHandle
* _fh
, const std::string
& _bname
,
2516 const std::string
& _oname
)
2517 : RGWLibContinuedReq(store
->ctx(), std::move(_user
)),
2518 bucket_name(_bname
), obj_name(_oname
),
2519 rgw_fh(_fh
), filter(nullptr), timer_id(0), real_ofs(0),
2520 bytes_written(0), eio(false) {
2522 // in ctr this is not a virtual call
2523 // invoking this classes's header_init()
2524 (void) RGWWriteRequest::header_init();
2526 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
2527 hash
.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
);
2530 bool only_bucket() override
{ return true; }
2532 int op_init() override
{
2533 // assign store, s, and dialect_handler
2534 RGWObjectCtx
* rados_ctx
2535 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2536 // framework promises to call op_init after parent init
2537 ceph_assert(rados_ctx
);
2538 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2539 op
= this; // assign self as op: REQUIRED
2543 int header_init() override
{
2545 struct req_state
* state
= get_state();
2546 state
->info
.method
= "PUT";
2549 /* XXX derp derp derp */
2550 std::string uri
= make_uri(bucket_name
, obj_name
);
2551 state
->relative_uri
= uri
;
2552 state
->info
.request_uri
= uri
; // XXX
2553 state
->info
.effective_uri
= uri
;
2554 state
->info
.request_params
= "";
2555 state
->info
.domain
= ""; /* XXX ? */
2560 int get_params(optional_yield
) override
{
2561 struct req_state
* state
= get_state();
2562 RGWAccessControlPolicy_S3
s3policy(state
->cct
);
2563 /* we don't have (any) headers, so just create canned ACLs */
2564 int ret
= s3policy
.create_canned(state
->owner
, state
->bucket_owner
, state
->canned_acl
);
2569 int get_data(buffer::list
& _bl
) override
{
2570 /* XXX for now, use sharing semantics */
2571 uint32_t len
= data
.length();
2572 _bl
= std::move(data
);
2573 bytes_written
+= len
;
2577 void put_data(off_t off
, buffer::list
& _bl
) {
2578 if (off
!= real_ofs
) {
2581 data
= std::move(_bl
);
2582 real_ofs
+= data
.length();
2583 ofs
= off
; /* consumed in exec_continue() */
2586 int exec_start() override
;
2587 int exec_continue() override
;
2588 int exec_finish() override
;
2590 void send_response() override
{}
2592 int verify_params() override
{
2595 }; /* RGWWriteRequest */
2600 class RGWCopyObjRequest
: public RGWLibRequest
,
2601 public RGWCopyObj
/* RGWOp */
2604 RGWFileHandle
* src_parent
;
2605 RGWFileHandle
* dst_parent
;
2606 const std::string
& src_name
;
2607 const std::string
& dst_name
;
2609 RGWCopyObjRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
2610 RGWFileHandle
* _src_parent
, RGWFileHandle
* _dst_parent
,
2611 const std::string
& _src_name
, const std::string
& _dst_name
)
2612 : RGWLibRequest(_cct
, std::move(_user
)), src_parent(_src_parent
),
2613 dst_parent(_dst_parent
), src_name(_src_name
), dst_name(_dst_name
) {
2614 /* all requests have this */
2617 /* allow this request to replace selected attrs */
2618 attrs_mod
= rgw::sal::ATTRSMOD_MERGE
;
2621 bool only_bucket() override
{ return true; }
2623 int op_init() override
{
2624 // assign store, s, and dialect_handler
2625 RGWObjectCtx
* rados_ctx
2626 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2627 // framework promises to call op_init after parent init
2628 ceph_assert(rados_ctx
);
2629 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2630 op
= this; // assign self as op: REQUIRED
2635 int header_init() override
{
2637 struct req_state
* state
= get_state();
2638 state
->info
.method
= "PUT"; // XXX check
2641 src_bucket_name
= src_parent
->bucket_name();
2642 state
->src_bucket_name
= src_bucket_name
;
2643 dest_bucket_name
= dst_parent
->bucket_name();
2644 state
->bucket_name
= dest_bucket_name
;
2645 dest_obj_name
= dst_parent
->format_child_name(dst_name
, false);
2647 int rc
= valid_s3_object_name(dest_obj_name
);
2651 /* XXX and fixup key attr (could optimize w/string ref and
2653 buffer::list ux_key
;
2654 fh_key fhk
= dst_parent
->make_fhk(dst_name
);
2655 rgw::encode(fhk
, ux_key
);
2656 emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
2658 #if 0 /* XXX needed? */
2659 state
->relative_uri
= uri
;
2660 state
->info
.request_uri
= uri
; // XXX
2661 state
->info
.effective_uri
= uri
;
2662 state
->info
.request_params
= "";
2663 state
->info
.domain
= ""; /* XXX ? */
2669 int get_params(optional_yield
) override
{
2670 struct req_state
* s
= get_state();
2671 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2672 /* we don't have (any) headers, so just create canned ACLs */
2673 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
2674 dest_policy
= s3policy
;
2675 /* src_object required before RGWCopyObj::verify_permissions() */
2676 rgw_obj_key k
= rgw_obj_key(src_name
);
2677 s
->src_object
= s
->bucket
->get_object(k
);
2678 s
->object
= s
->src_object
->clone(); // needed to avoid trap at rgw_op.cc:5150
2682 void send_response() override
{}
2683 void send_partial_response(off_t ofs
) override
{}
2685 }; /* RGWCopyObjRequest */
2687 class RGWGetAttrsRequest
: public RGWLibRequest
,
2688 public RGWGetAttrs
/* RGWOp */
2691 const std::string
& bucket_name
;
2692 const std::string
& obj_name
;
2694 RGWGetAttrsRequest(CephContext
* _cct
,
2695 std::unique_ptr
<rgw::sal::User
> _user
,
2696 const std::string
& _bname
, const std::string
& _oname
)
2697 : RGWLibRequest(_cct
, std::move(_user
)), RGWGetAttrs(),
2698 bucket_name(_bname
), obj_name(_oname
) {
2702 const flat_map
<std::string
, std::optional
<buffer::list
>>& get_attrs() {
2706 virtual bool only_bucket() { return false; }
2708 virtual int op_init() {
2709 // assign store, s, and dialect_handler
2710 RGWObjectCtx
* rados_ctx
2711 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2712 // framework promises to call op_init after parent init
2714 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2715 op
= this; // assign self as op: REQUIRED
2719 virtual int header_init() {
2721 struct req_state
* s
= get_state();
2722 s
->info
.method
= "GET";
2725 std::string uri
= make_uri(bucket_name
, obj_name
);
2726 s
->relative_uri
= uri
;
2727 s
->info
.request_uri
= uri
;
2728 s
->info
.effective_uri
= uri
;
2729 s
->info
.request_params
= "";
2730 s
->info
.domain
= ""; /* XXX ? */
2735 virtual int get_params() {
2739 virtual void send_response() {}
2741 }; /* RGWGetAttrsRequest */
2743 class RGWSetAttrsRequest
: public RGWLibRequest
,
2744 public RGWSetAttrs
/* RGWOp */
2747 const std::string
& bucket_name
;
2748 const std::string
& obj_name
;
2750 RGWSetAttrsRequest(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
2751 const std::string
& _bname
, const std::string
& _oname
)
2752 : RGWLibRequest(_cct
, std::move(_user
)), bucket_name(_bname
), obj_name(_oname
) {
2756 const std::map
<std::string
, buffer::list
>& get_attrs() {
2760 bool only_bucket() override
{ return false; }
2762 int op_init() override
{
2763 // assign store, s, and dialect_handler
2764 RGWObjectCtx
* rados_ctx
2765 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2766 // framework promises to call op_init after parent init
2767 ceph_assert(rados_ctx
);
2768 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2769 op
= this; // assign self as op: REQUIRED
2773 int header_init() override
{
2775 struct req_state
* state
= get_state();
2776 state
->info
.method
= "PUT";
2779 /* XXX derp derp derp */
2780 std::string uri
= make_uri(bucket_name
, obj_name
);
2781 state
->relative_uri
= uri
;
2782 state
->info
.request_uri
= uri
; // XXX
2783 state
->info
.effective_uri
= uri
;
2784 state
->info
.request_params
= "";
2785 state
->info
.domain
= ""; /* XXX ? */
2790 int get_params(optional_yield
) override
{
2794 void send_response() override
{}
2796 }; /* RGWSetAttrsRequest */
2798 class RGWRMAttrsRequest
: public RGWLibRequest
,
2799 public RGWRMAttrs
/* RGWOp */
2802 const std::string
& bucket_name
;
2803 const std::string
& obj_name
;
2805 RGWRMAttrsRequest(CephContext
* _cct
,
2806 std::unique_ptr
<rgw::sal::User
> _user
,
2807 const std::string
& _bname
, const std::string
& _oname
)
2808 : RGWLibRequest(_cct
, std::move(_user
)), RGWRMAttrs(),
2809 bucket_name(_bname
), obj_name(_oname
) {
2813 const rgw::sal::Attrs
& get_attrs() {
2817 virtual bool only_bucket() { return false; }
2819 virtual int op_init() {
2820 // assign store, s, and dialect_handler
2821 RGWObjectCtx
* rados_ctx
2822 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2823 // framework promises to call op_init after parent init
2825 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2826 op
= this; // assign self as op: REQUIRED
2830 virtual int header_init() {
2832 struct req_state
* s
= get_state();
2833 s
->info
.method
= "DELETE";
2836 std::string uri
= make_uri(bucket_name
, obj_name
);
2837 s
->relative_uri
= uri
;
2838 s
->info
.request_uri
= uri
;
2839 s
->info
.effective_uri
= uri
;
2840 s
->info
.request_params
= "";
2841 s
->info
.domain
= ""; /* XXX ? */
2846 virtual int get_params() {
2850 virtual void send_response() {}
2852 }; /* RGWRMAttrsRequest */
2855 * Send request to get the rados cluster stats
2857 class RGWGetClusterStatReq
: public RGWLibRequest
,
2858 public RGWGetClusterStat
{
2860 struct rados_cluster_stat_t
& stats_req
;
2861 RGWGetClusterStatReq(CephContext
* _cct
, std::unique_ptr
<rgw::sal::User
> _user
,
2862 rados_cluster_stat_t
& _stats
):
2863 RGWLibRequest(_cct
, std::move(_user
)), stats_req(_stats
){
2867 int op_init() override
{
2868 // assign store, s, and dialect_handler
2869 RGWObjectCtx
* rados_ctx
2870 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2871 // framework promises to call op_init after parent init
2872 ceph_assert(rados_ctx
);
2873 RGWOp::init(rados_ctx
->get_store(), get_state(), this);
2874 op
= this; // assign self as op: REQUIRED
2878 int header_init() override
{
2879 struct req_state
* state
= get_state();
2880 state
->info
.method
= "GET";
2885 int get_params(optional_yield
) override
{ return 0; }
2886 bool only_bucket() override
{ return false; }
2887 void send_response() override
{
2888 stats_req
.kb
= stats_op
.kb
;
2889 stats_req
.kb_avail
= stats_op
.kb_avail
;
2890 stats_req
.kb_used
= stats_op
.kb_used
;
2891 stats_req
.num_objects
= stats_op
.num_objects
;
2893 }; /* RGWGetClusterStatReq */
2896 } /* namespace rgw */
2898 #endif /* RGW_FILE_H */