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"
40 * ASSERT_H somehow not defined after all the above (which bring
41 * in common/debug.h [e.g., dout])
43 #include "include/assert.h"
46 #define RGW_RWXMODE (S_IRWXU | S_IRWXG | S_IRWXO)
48 #define RGW_RWMODE (RGW_RWXMODE & \
49 ~(S_IXUSR | S_IXGRP | S_IXOTH))
55 static inline void ignore(T
&&) {}
58 namespace bi
= boost::intrusive
;
62 class RGWWriteRequest
;
64 static inline bool operator <(const struct timespec
& lhs
,
65 const struct timespec
& rhs
) {
66 if (lhs
.tv_sec
== rhs
.tv_sec
)
67 return lhs
.tv_nsec
< rhs
.tv_nsec
;
69 return lhs
.tv_sec
< rhs
.tv_sec
;
72 static inline bool operator ==(const struct timespec
& lhs
,
73 const struct timespec
& rhs
) {
74 return ((lhs
.tv_sec
== rhs
.tv_sec
) &&
75 (lhs
.tv_nsec
== rhs
.tv_nsec
));
80 * The current 64-bit, non-cryptographic hash used here is intended
81 * for prototyping only.
83 * However, the invariant being prototyped is that objects be
84 * identifiable by their hash components alone. We believe this can
85 * be legitimately implemented using 128-hash values for bucket and
86 * object components, together with a cluster-resident cryptographic
87 * key. Since an MD5 or SHA-1 key is 128 bits and the (fast),
88 * non-cryptographic CityHash128 hash algorithm takes a 128-bit seed,
89 * speculatively we could use that for the final hash computations.
95 static constexpr uint64_t seed
= 8675309;
99 fh_key(const rgw_fh_hk
& _hk
)
104 fh_key(const uint64_t bk
, const uint64_t ok
) {
109 fh_key(const uint64_t bk
, const char *_o
) {
111 fh_hk
.object
= XXH64(_o
, ::strlen(_o
), seed
);
114 fh_key(const std::string
& _b
, const std::string
& _o
) {
115 fh_hk
.bucket
= XXH64(_b
.c_str(), _o
.length(), seed
);
116 fh_hk
.object
= XXH64(_o
.c_str(), _o
.length(), seed
);
119 void encode(buffer::list
& bl
) const {
120 ENCODE_START(1, 1, bl
);
121 ::encode(fh_hk
.bucket
, bl
);
122 ::encode(fh_hk
.object
, bl
);
126 void decode(bufferlist::iterator
& bl
) {
128 ::decode(fh_hk
.bucket
, bl
);
129 ::decode(fh_hk
.object
, bl
);
134 WRITE_CLASS_ENCODER(fh_key
);
136 inline bool operator<(const fh_key
& lhs
, const fh_key
& rhs
)
138 return ((lhs
.fh_hk
.bucket
< rhs
.fh_hk
.bucket
) ||
139 ((lhs
.fh_hk
.bucket
== rhs
.fh_hk
.bucket
) &&
140 (lhs
.fh_hk
.object
< rhs
.fh_hk
.object
)));
143 inline bool operator>(const fh_key
& lhs
, const fh_key
& rhs
)
148 inline bool operator==(const fh_key
& lhs
, const fh_key
& rhs
)
150 return ((lhs
.fh_hk
.bucket
== rhs
.fh_hk
.bucket
) &&
151 (lhs
.fh_hk
.object
== rhs
.fh_hk
.object
));
154 inline bool operator!=(const fh_key
& lhs
, const fh_key
& rhs
)
156 return !(lhs
== rhs
);
159 inline bool operator<=(const fh_key
& lhs
, const fh_key
& rhs
)
161 return (lhs
< rhs
) || (lhs
== rhs
);
164 using boost::variant
;
165 using boost::container::flat_map
;
167 class RGWFileHandle
: public cohort::lru::Object
169 struct rgw_file_handle fh
;
173 RGWFileHandle
* bucket
;
174 RGWFileHandle
* parent
;
175 /* const */ std::string name
; /* XXX file or bucket name */
176 /* const */ fh_key fhk
;
178 using lock_guard
= std::lock_guard
<std::mutex
>;
179 using unique_lock
= std::unique_lock
<std::mutex
>;
181 /* TODO: keeping just the last marker is sufficient for
182 * nfs-ganesha 2.4.5; in the near future, nfs-ganesha will
183 * be able to hint the name of the next dirent required,
184 * from which we can directly synthesize a RADOS marker.
185 * using marker_cache_t = flat_map<uint64_t, rgw_obj_key>;
192 uint32_t owner_uid
; /* XXX need Unix attr */
193 uint32_t owner_gid
; /* XXX need Unix attr */
195 struct timespec ctime
;
196 struct timespec mtime
;
197 struct timespec atime
;
198 State() : dev(0), size(0), nlink(1), owner_uid(0), owner_gid(0),
199 ctime
{0,0}, mtime
{0,0}, atime
{0,0} {}
203 RGWWriteRequest
* write_req
;
204 file() : write_req(nullptr) {}
210 static constexpr uint32_t FLAG_NONE
= 0x0000;
213 rgw_obj_key last_marker
;
214 struct timespec last_readdir
;
216 directory() : flags(FLAG_NONE
), last_readdir
{0,0} {}
221 boost::variant
<file
, directory
> variant_type
;
227 const static std::string root_name
;
229 static constexpr uint16_t MAX_DEPTH
= 256;
231 static constexpr uint32_t FLAG_NONE
= 0x0000;
232 static constexpr uint32_t FLAG_OPEN
= 0x0001;
233 static constexpr uint32_t FLAG_ROOT
= 0x0002;
234 static constexpr uint32_t FLAG_CREATE
= 0x0004;
235 static constexpr uint32_t FLAG_CREATING
= 0x0008;
236 static constexpr uint32_t FLAG_DIRECTORY
= 0x0010;
237 static constexpr uint32_t FLAG_BUCKET
= 0x0020;
238 static constexpr uint32_t FLAG_LOCK
= 0x0040;
239 static constexpr uint32_t FLAG_DELETED
= 0x0080;
240 static constexpr uint32_t FLAG_UNLINK_THIS
= 0x0100;
241 static constexpr uint32_t FLAG_LOCKED
= 0x0200;
242 static constexpr uint32_t FLAG_STATELESS_OPEN
= 0x0400;
243 static constexpr uint32_t FLAG_EXACT_MATCH
= 0x0800;
245 #define CREATE_FLAGS(x) \
246 ((x) & ~(RGWFileHandle::FLAG_CREATE|RGWFileHandle::FLAG_LOCK))
248 friend class RGWLibFS
;
251 RGWFileHandle(RGWLibFS
* _fs
, uint32_t fs_inst
)
252 : fs(_fs
), bucket(nullptr), parent(nullptr), variant_type
{directory()},
253 depth(0), flags(FLAG_ROOT
)
256 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
257 variant_type
= directory();
260 state
.unix_mode
= RGW_RWXMODE
|S_IFDIR
;
261 /* pointer to self */
262 fh
.fh_private
= this;
265 void init_rootfs(std::string
& fsid
, const std::string
& object_name
) {
267 fh
.fh_hk
.bucket
= XXH64(fsid
.c_str(), fsid
.length(), fh_key::seed
);
268 fh
.fh_hk
.object
= XXH64(object_name
.c_str(), object_name
.length(),
275 RGWFileHandle(RGWLibFS
* fs
, uint32_t fs_inst
, RGWFileHandle
* _parent
,
276 const fh_key
& _fhk
, std::string
& _name
, uint32_t _flags
)
277 : fs(fs
), bucket(nullptr), parent(_parent
), name(std::move(_name
)),
278 fhk(_fhk
), flags(_flags
) {
280 if (parent
->is_root()) {
281 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
282 variant_type
= directory();
283 flags
|= FLAG_BUCKET
;
285 bucket
= parent
->is_bucket() ? parent
287 if (flags
& FLAG_DIRECTORY
) {
288 fh
.fh_type
= RGW_FS_TYPE_DIRECTORY
;
289 variant_type
= directory();
291 fh
.fh_type
= RGW_FS_TYPE_FILE
;
292 variant_type
= file();
296 depth
= parent
->depth
+ 1;
298 /* save constant fhk */
299 fh
.fh_hk
= fhk
.fh_hk
; /* XXX redundant in fh_hk */
304 switch (fh
.fh_type
) {
305 case RGW_FS_TYPE_DIRECTORY
:
306 state
.unix_mode
= RGW_RWXMODE
|S_IFDIR
;
308 case RGW_FS_TYPE_FILE
:
309 state
.unix_mode
= RGW_RWMODE
|S_IFREG
;
314 /* pointer to self */
315 fh
.fh_private
= this;
318 const fh_key
& get_key() const {
322 directory
* get_directory() {
323 return get
<directory
>(&variant_type
);
326 size_t get_size() const { return state
.size
; }
328 const char* stype() {
329 return is_dir() ? "DIR" : "FILE";
332 uint16_t get_depth() const { return depth
; }
334 struct rgw_file_handle
* get_fh() { return &fh
; }
336 RGWLibFS
* get_fs() { return fs
; }
338 RGWFileHandle
* get_parent() { return parent
; }
340 uint32_t get_owner_uid() const { return state
.owner_uid
; }
341 uint32_t get_owner_gid() const { return state
.owner_gid
; }
343 struct timespec
get_ctime() const { return state
.ctime
; }
344 struct timespec
get_mtime() const { return state
.mtime
; }
346 void create_stat(struct stat
* st
, uint32_t mask
) {
347 if (mask
& RGW_SETATTR_UID
)
348 state
.owner_uid
= st
->st_uid
;
350 if (mask
& RGW_SETATTR_GID
)
351 state
.owner_gid
= st
->st_gid
;
353 if (mask
& RGW_SETATTR_MODE
) {
354 switch (fh
.fh_type
) {
355 case RGW_FS_TYPE_DIRECTORY
:
356 state
.unix_mode
= st
->st_mode
|S_IFDIR
;
358 case RGW_FS_TYPE_FILE
:
359 state
.unix_mode
= st
->st_mode
|S_IFREG
;
365 if (mask
& RGW_SETATTR_ATIME
)
366 state
.atime
= st
->st_atim
;
367 if (mask
& RGW_SETATTR_MTIME
)
368 state
.mtime
= st
->st_mtim
;
369 if (mask
& RGW_SETATTR_CTIME
)
370 state
.ctime
= st
->st_ctim
;
373 int stat(struct stat
* st
) {
374 /* partial Unix attrs */
375 memset(st
, 0, sizeof(struct stat
));
376 st
->st_dev
= state
.dev
;
377 st
->st_ino
= fh
.fh_hk
.object
; // XXX
379 st
->st_uid
= state
.owner_uid
;
380 st
->st_gid
= state
.owner_gid
;
382 st
->st_mode
= state
.unix_mode
;
384 #ifdef HAVE_STAT_ST_MTIMESPEC_TV_NSEC
385 st
->st_atimespec
= state
.atime
;
386 st
->st_mtimespec
= state
.mtime
;
387 st
->st_ctimespec
= state
.ctime
;
389 st
->st_atim
= state
.atime
;
390 st
->st_mtim
= state
.mtime
;
391 st
->st_ctim
= state
.ctime
;
394 switch (fh
.fh_type
) {
395 case RGW_FS_TYPE_DIRECTORY
:
396 st
->st_nlink
= state
.nlink
;
398 case RGW_FS_TYPE_FILE
:
400 st
->st_blksize
= 4096;
401 st
->st_size
= state
.size
;
402 st
->st_blocks
= (state
.size
) / 512;
410 const std::string
& bucket_name() const {
415 return bucket
->object_name();
418 const std::string
& object_name() const { return name
; }
420 std::string
full_object_name(bool omit_bucket
= false) const {
422 std::vector
<const std::string
*> segments
;
424 const RGWFileHandle
* tfh
= this;
425 while (tfh
&& !tfh
->is_root() && !(tfh
->is_bucket() && omit_bucket
)) {
426 segments
.push_back(&tfh
->object_name());
427 reserve
+= (1 + tfh
->object_name().length());
431 path
.reserve(reserve
);
432 for (auto& s
: boost::adaptors::reverse(segments
)) {
436 if (!omit_bucket
&& (path
.front() != '/')) // pretty-print
445 inline std::string
relative_object_name() const {
446 return full_object_name(true /* omit_bucket */);
449 inline std::string
format_child_name(const std::string
& cbasename
,
451 std::string child_name
{relative_object_name()};
452 if ((child_name
.size() > 0) &&
453 (child_name
.back() != '/'))
455 child_name
+= cbasename
;
461 inline std::string
make_key_name(const char *name
) const {
462 std::string key_name
{full_object_name()};
463 if (key_name
.length() > 0)
469 fh_key
make_fhk(const std::string
& name
) const {
471 return fh_key(fhk
.fh_hk
.object
, name
.c_str());
473 std::string key_name
= make_key_name(name
.c_str());
474 return fh_key(fhk
.fh_hk
.bucket
, key_name
.c_str());
478 void add_marker(uint64_t off
, const rgw_obj_key
& marker
,
481 directory
* d
= get
<directory
>(&variant_type
);
483 unique_lock
guard(mtx
);
484 d
->last_marker
= marker
;
488 const rgw_obj_key
* find_marker(uint64_t off
) const {
491 const directory
* d
= get
<directory
>(&variant_type
);
493 return &d
->last_marker
;
499 bool is_open() const { return flags
& FLAG_OPEN
; }
500 bool is_root() const { return flags
& FLAG_ROOT
; }
501 bool is_bucket() const { return flags
& FLAG_BUCKET
; }
502 bool is_object() const { return !is_bucket(); }
503 bool is_file() const { return (fh
.fh_type
== RGW_FS_TYPE_FILE
); }
504 bool is_dir() const { return (fh
.fh_type
== RGW_FS_TYPE_DIRECTORY
); }
505 bool creating() const { return flags
& FLAG_CREATING
; }
506 bool deleted() const { return flags
& FLAG_DELETED
; }
507 bool stateless_open() const { return flags
& FLAG_STATELESS_OPEN
; }
508 bool has_children() const;
510 int open(uint32_t gsh_flags
) {
511 lock_guard
guard(mtx
);
513 if (gsh_flags
& RGW_OPEN_FLAG_V3
) {
514 flags
|= FLAG_STATELESS_OPEN
;
522 int readdir(rgw_readdir_cb rcb
, void *cb_arg
, uint64_t *offset
, bool *eof
,
524 int write(uint64_t off
, size_t len
, size_t *nbytes
, void *buffer
);
526 int commit(uint64_t offset
, uint64_t length
, uint32_t flags
) {
527 /* NFS3 and NFSv4 COMMIT implementation
528 * the current atomic update strategy doesn't actually permit
529 * clients to read-stable until either CLOSE (NFSv4+) or the
530 * expiration of the active write timer (NFS3). In the
531 * interim, the client may send an arbitrary number of COMMIT
532 * operations which must return a success result */
536 int write_finish(uint32_t flags
= FLAG_NONE
);
539 void open_for_create() {
540 lock_guard
guard(mtx
);
541 flags
|= FLAG_CREATING
;
544 void clear_creating() {
545 lock_guard
guard(mtx
);
546 flags
&= ~FLAG_CREATING
;
549 void inc_nlink(const uint64_t n
) {
553 void set_nlink(const uint64_t n
) {
557 void set_size(const size_t size
) {
561 void set_times(real_time t
) {
562 state
.ctime
= real_clock::to_timespec(t
);
563 state
.mtime
= state
.ctime
;
564 state
.atime
= state
.ctime
;
567 void set_ctime(const struct timespec
&ts
) {
571 void set_mtime(const struct timespec
&ts
) {
575 void set_atime(const struct timespec
&ts
) {
579 void encode(buffer::list
& bl
) const {
580 ENCODE_START(1, 1, bl
);
581 ::encode(uint32_t(fh
.fh_type
), bl
);
582 ::encode(state
.dev
, bl
);
583 ::encode(state
.size
, bl
);
584 ::encode(state
.nlink
, bl
);
585 ::encode(state
.owner_uid
, bl
);
586 ::encode(state
.owner_gid
, bl
);
587 ::encode(state
.unix_mode
, bl
);
588 for (const auto& t
: { state
.ctime
, state
.mtime
, state
.atime
}) {
589 ::encode(real_clock::from_timespec(t
), bl
);
594 void decode(bufferlist::iterator
& bl
) {
597 ::decode(fh_type
, bl
);
598 assert(fh
.fh_type
== fh_type
);
599 ::decode(state
.dev
, bl
);
600 ::decode(state
.size
, bl
);
601 ::decode(state
.nlink
, bl
);
602 ::decode(state
.owner_uid
, bl
);
603 ::decode(state
.owner_gid
, bl
);
604 ::decode(state
.unix_mode
, bl
);
605 ceph::real_time enc_time
;
606 for (auto t
: { &(state
.ctime
), &(state
.mtime
), &(state
.atime
) }) {
607 ::decode(enc_time
, bl
);
608 *t
= real_clock::to_timespec(enc_time
);
613 void encode_attrs(ceph::buffer::list
& ux_key1
,
614 ceph::buffer::list
& ux_attrs1
);
616 void decode_attrs(const ceph::buffer::list
* ux_key1
,
617 const ceph::buffer::list
* ux_attrs1
);
621 bool reclaim() override
;
623 typedef cohort::lru::LRU
<std::mutex
> FhLRU
;
627 // for internal ordering
628 bool operator()(const RGWFileHandle
& lhs
, const RGWFileHandle
& rhs
) const
629 { return (lhs
.get_key() < rhs
.get_key()); }
631 // for external search by fh_key
632 bool operator()(const fh_key
& k
, const RGWFileHandle
& fh
) const
633 { return k
< fh
.get_key(); }
635 bool operator()(const RGWFileHandle
& fh
, const fh_key
& k
) const
636 { return fh
.get_key() < k
; }
641 bool operator()(const RGWFileHandle
& lhs
, const RGWFileHandle
& rhs
) const
642 { return (lhs
.get_key() == rhs
.get_key()); }
644 bool operator()(const fh_key
& k
, const RGWFileHandle
& fh
) const
645 { return k
== fh
.get_key(); }
647 bool operator()(const RGWFileHandle
& fh
, const fh_key
& k
) const
648 { return fh
.get_key() == k
; }
651 typedef bi::link_mode
<bi::safe_link
> link_mode
; /* XXX normal */
652 #if defined(FHCACHE_AVL)
653 typedef bi::avl_set_member_hook
<link_mode
> tree_hook_type
;
656 typedef bi::set_member_hook
<link_mode
> tree_hook_type
;
658 tree_hook_type fh_hook
;
660 typedef bi::member_hook
<
661 RGWFileHandle
, tree_hook_type
, &RGWFileHandle::fh_hook
> FhHook
;
663 #if defined(FHCACHE_AVL)
664 typedef bi::avltree
<RGWFileHandle
, bi::compare
<FhLT
>, FhHook
> FHTree
;
666 typedef bi::rbtree
<RGWFileHandle
, bi::compare
<FhLT
>, FhHook
> FhTree
;
668 typedef cohort::lru::TreeX
<RGWFileHandle
, FhTree
, FhLT
, FhEQ
, fh_key
,
671 ~RGWFileHandle() override
;
673 friend std::ostream
& operator<<(std::ostream
&os
,
674 RGWFileHandle
const &rgw_fh
);
676 class Factory
: public cohort::lru::ObjectFactory
681 RGWFileHandle
* parent
;
688 Factory(RGWLibFS
* fs
, uint32_t fs_inst
, RGWFileHandle
* parent
,
689 const fh_key
& fhk
, std::string
& name
, uint32_t flags
)
690 : fs(fs
), fs_inst(fs_inst
), parent(parent
), fhk(fhk
), name(name
),
693 void recycle (cohort::lru::Object
* o
) override
{
694 /* re-use an existing object */
695 o
->~Object(); // call lru::Object virtual dtor
697 new (o
) RGWFileHandle(fs
, fs_inst
, parent
, fhk
, name
, flags
);
700 cohort::lru::Object
* alloc() override
{
701 return new RGWFileHandle(fs
, fs_inst
, parent
, fhk
, name
, flags
);
705 }; /* RGWFileHandle */
707 WRITE_CLASS_ENCODER(RGWFileHandle
);
709 static inline RGWFileHandle
* get_rgwfh(struct rgw_file_handle
* fh
) {
710 return static_cast<RGWFileHandle
*>(fh
->fh_private
);
713 static inline enum rgw_fh_type
fh_type_of(uint32_t flags
) {
714 enum rgw_fh_type fh_type
;
715 switch(flags
& RGW_LOOKUP_TYPE_FLAGS
)
717 case RGW_LOOKUP_FLAG_DIR
:
718 fh_type
= RGW_FS_TYPE_DIRECTORY
;
720 case RGW_LOOKUP_FLAG_FILE
:
721 fh_type
= RGW_FS_TYPE_FILE
;
724 fh_type
= RGW_FS_TYPE_NIL
;
729 typedef std::tuple
<RGWFileHandle
*, uint32_t> LookupFHResult
;
730 typedef std::tuple
<RGWFileHandle
*, int> MkObjResult
;
736 RGWFileHandle root_fh
;
737 rgw_fh_callback_t invalidate_cb
;
738 void *invalidate_arg
;
741 mutable std::atomic
<uint64_t> refcnt
;
743 RGWFileHandle::FHCache fh_cache
;
744 RGWFileHandle::FhLRU fh_lru
;
746 std::string uid
; // should match user.user_id, iiuc
749 RGWAccessKey key
; // XXXX acc_key
751 static std::atomic
<uint32_t> fs_inst_counter
;
753 static uint32_t write_completion_interval_s
;
756 using lock_guard
= std::lock_guard
<std::mutex
>;
757 using unique_lock
= std::unique_lock
<std::mutex
>;
761 enum class type
: uint8_t { READDIR
} ;
765 event(type t
, const fh_key
& k
, const struct timespec
& ts
)
766 : t(t
), fhk(k
), ts(ts
) {}
769 friend std::ostream
& operator<<(std::ostream
&os
,
770 RGWLibFS::event
const &ev
);
772 using event_vector
= /* boost::small_vector<event, 16> */
775 struct WriteCompletion
777 RGWFileHandle
& rgw_fh
;
779 WriteCompletion(RGWFileHandle
& _fh
) : rgw_fh(_fh
) {
780 rgw_fh
.get_fs()->ref(&rgw_fh
);
784 rgw_fh
.close(); /* will finish in-progress write */
785 rgw_fh
.get_fs()->unref(&rgw_fh
);
789 static ceph::timer
<ceph::mono_clock
> write_timer
;
793 std::atomic
<uint32_t> flags
;
794 std::deque
<event
> events
;
796 State() : flags(0) {}
798 void push_event(const event
& ev
) {
799 events
.push_back(ev
);
803 uint32_t new_inst() {
804 return ++fs_inst_counter
;
807 friend class RGWFileHandle
;
808 friend class RGWLibProcess
;
812 static constexpr uint32_t FLAG_NONE
= 0x0000;
813 static constexpr uint32_t FLAG_CLOSED
= 0x0001;
818 real_time creation_time
;
819 uint64_t num_entries
;
822 RGWLibFS(CephContext
* _cct
, const char *_uid
, const char *_user_id
,
824 : cct(_cct
), root_fh(this, new_inst()), invalidate_cb(nullptr),
825 invalidate_arg(nullptr), shutdown(false), refcnt(1),
826 fh_cache(cct
->_conf
->rgw_nfs_fhcache_partitions
,
827 cct
->_conf
->rgw_nfs_fhcache_size
),
828 fh_lru(cct
->_conf
->rgw_nfs_lru_lanes
,
829 cct
->_conf
->rgw_nfs_lru_lane_hiwat
),
830 uid(_uid
), key(_user_id
, _key
) {
832 /* no bucket may be named rgw_fs_inst-(.*) */
833 fsid
= RGWFileHandle::root_name
+ "rgw_fs_inst-" +
834 std::to_string(get_inst());
836 root_fh
.init_rootfs(fsid
/* bucket */, RGWFileHandle::root_name
);
838 /* pointer to self */
839 fs
.fs_private
= this;
841 /* expose public root fh */
842 fs
.root_fh
= root_fh
.get_fh();
845 friend void intrusive_ptr_add_ref(const RGWLibFS
* fs
) {
846 fs
->refcnt
.fetch_add(1, std::memory_order_relaxed
);
849 friend void intrusive_ptr_release(const RGWLibFS
* fs
) {
850 if (fs
->refcnt
.fetch_sub(1, std::memory_order_release
) == 0) {
851 std::atomic_thread_fence(std::memory_order_acquire
);
857 intrusive_ptr_add_ref(this);
862 intrusive_ptr_release(this);
865 void stop() { shutdown
= true; }
867 void release_evict(RGWFileHandle
* fh
) {
868 /* remove from cache, releases sentinel ref */
869 fh_cache
.remove(fh
->fh
.fh_hk
.object
, fh
,
870 RGWFileHandle::FHCache::FLAG_LOCK
);
871 /* release call-path ref */
872 (void) fh_lru
.unref(fh
, cohort::lru::FLAG_NONE
);
875 int authorize(RGWRados
* store
) {
876 int ret
= rgw_get_user_info_by_access_key(store
, key
.id
, user
);
878 RGWAccessKey
* key0
= user
.get_key0();
880 (key0
->key
!= key
.key
))
883 return -ERR_USER_SUSPENDED
;
885 /* try external authenticators (ldap for now) */
886 rgw::LDAPHelper
* ldh
= rgwlib
.get_ldh(); /* !nullptr */
888 /* boost filters and/or string_ref may throw on invalid input */
890 token
= rgw::from_base64(key
.id
);
892 token
= std::string("");
894 if (token
.valid() && (ldh
->auth(token
.id
, token
.key
) == 0)) {
895 /* try to store user if it doesn't already exist */
896 if (rgw_get_user_info_by_uid(store
, token
.id
, user
) < 0) {
897 int ret
= rgw_store_user_info(store
, user
, NULL
, NULL
, real_time(),
900 lsubdout(get_context(), rgw
, 10)
901 << "NOTICE: failed to store new user's info: ret=" << ret
910 int register_invalidate(rgw_fh_callback_t cb
, void *arg
, uint32_t flags
) {
912 invalidate_arg
= arg
;
916 /* find RGWFileHandle by id */
917 LookupFHResult
lookup_fh(const fh_key
& fhk
,
918 const uint32_t flags
= RGWFileHandle::FLAG_NONE
) {
921 // cast int32_t(RGWFileHandle::FLAG_NONE) due to strictness of Clang
922 // the cast transfers a lvalue into a rvalue in the ctor
923 // check the commit message for the full details
924 LookupFHResult fhr
{ nullptr, uint32_t(RGWFileHandle::FLAG_NONE
) };
926 RGWFileHandle::FHCache::Latch lat
;
927 bool fh_locked
= flags
& RGWFileHandle::FLAG_LOCKED
;
931 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
932 fhk
/* key */, lat
/* serializer */,
933 RGWFileHandle::FHCache::FLAG_LOCK
);
936 if (likely(! fh_locked
))
937 fh
->mtx
.lock(); // XXX !RAII because may-return-LOCKED
938 /* need initial ref from LRU (fast path) */
939 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
941 if (likely(! fh_locked
))
943 goto retry
; /* !LATCHED */
945 /* LATCHED, LOCKED */
946 if (! (flags
& RGWFileHandle::FLAG_LOCK
))
947 fh
->mtx
.unlock(); /* ! LOCKED */
949 lat
.lock
->unlock(); /* !LATCHED */
952 lsubdout(get_context(), rgw
, 17)
953 << __func__
<< " 1 " << *fh
957 } /* lookup_fh(const fh_key&) */
959 /* find or create an RGWFileHandle */
960 LookupFHResult
lookup_fh(RGWFileHandle
* parent
, const char *name
,
961 const uint32_t flags
= RGWFileHandle::FLAG_NONE
) {
964 // cast int32_t(RGWFileHandle::FLAG_NONE) due to strictness of Clang
965 // the cast transfers a lvalue into a rvalue in the ctor
966 // check the commit message for the full details
967 LookupFHResult fhr
{ nullptr, uint32_t(RGWFileHandle::FLAG_NONE
) };
969 /* mount is stale? */
970 if (state
.flags
& FLAG_CLOSED
)
973 RGWFileHandle::FHCache::Latch lat
;
974 bool fh_locked
= flags
& RGWFileHandle::FLAG_LOCKED
;
976 std::string obj_name
{name
};
977 std::string key_name
{parent
->make_key_name(name
)};
979 lsubdout(get_context(), rgw
, 10)
980 << __func__
<< " lookup called on "
981 << parent
->object_name() << " for " << key_name
982 << " (" << obj_name
<< ")"
985 fh_key fhk
= parent
->make_fhk(key_name
);
989 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
990 fhk
/* key */, lat
/* serializer */,
991 RGWFileHandle::FHCache::FLAG_LOCK
);
994 if (likely(! fh_locked
))
995 fh
->mtx
.lock(); // XXX !RAII because may-return-LOCKED
996 if (fh
->flags
& RGWFileHandle::FLAG_DELETED
) {
997 /* for now, delay briefly and retry */
999 if (likely(! fh_locked
))
1001 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1002 goto retry
; /* !LATCHED */
1004 /* need initial ref from LRU (fast path) */
1005 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
1007 if (likely(! fh_locked
))
1009 goto retry
; /* !LATCHED */
1011 /* LATCHED, LOCKED */
1012 if (! (flags
& RGWFileHandle::FLAG_LOCK
))
1013 if (likely(! fh_locked
))
1014 fh
->mtx
.unlock(); /* ! LOCKED */
1016 /* make or re-use handle */
1017 RGWFileHandle::Factory
prototype(this, get_inst(), parent
, fhk
,
1018 obj_name
, CREATE_FLAGS(flags
));
1019 fh
= static_cast<RGWFileHandle
*>(
1020 fh_lru
.insert(&prototype
,
1021 cohort::lru::Edge::MRU
,
1022 cohort::lru::FLAG_INITIAL
));
1024 /* lock fh (LATCHED) */
1025 if (flags
& RGWFileHandle::FLAG_LOCK
)
1027 /* inserts, releasing latch */
1028 fh_cache
.insert_latched(fh
, lat
, RGWFileHandle::FHCache::FLAG_UNLOCK
);
1029 get
<1>(fhr
) |= RGWFileHandle::FLAG_CREATE
;
1030 /* ref parent (non-initial ref cannot fail on valid object) */
1031 if (! parent
->is_root()) {
1032 (void) fh_lru
.ref(parent
, cohort::lru::FLAG_NONE
);
1034 goto out
; /* !LATCHED */
1037 goto retry
; /* !LATCHED */
1040 lat
.lock
->unlock(); /* !LATCHED */
1044 lsubdout(get_context(), rgw
, 17)
1045 << __func__
<< " 2 " << *fh
1049 } /* lookup_fh(RGWFileHandle*, const char *, const uint32_t) */
1051 inline void unref(RGWFileHandle
* fh
) {
1052 if (likely(! fh
->is_root())) {
1053 (void) fh_lru
.unref(fh
, cohort::lru::FLAG_NONE
);
1057 inline RGWFileHandle
* ref(RGWFileHandle
* fh
) {
1058 if (likely(! fh
->is_root())) {
1059 fh_lru
.ref(fh
, cohort::lru::FLAG_NONE
);
1064 int getattr(RGWFileHandle
* rgw_fh
, struct stat
* st
);
1066 int setattr(RGWFileHandle
* rgw_fh
, struct stat
* st
, uint32_t mask
,
1069 LookupFHResult
stat_bucket(RGWFileHandle
* parent
, const char *path
,
1070 RGWLibFS::BucketStats
& bs
,
1073 LookupFHResult
stat_leaf(RGWFileHandle
* parent
, const char *path
,
1074 enum rgw_fh_type type
= RGW_FS_TYPE_NIL
,
1075 uint32_t flags
= RGWFileHandle::FLAG_NONE
);
1077 int read(RGWFileHandle
* rgw_fh
, uint64_t offset
, size_t length
,
1078 size_t* bytes_read
, void* buffer
, uint32_t flags
);
1080 int rename(RGWFileHandle
* old_fh
, RGWFileHandle
* new_fh
,
1081 const char *old_name
, const char *new_name
);
1083 MkObjResult
create(RGWFileHandle
* parent
, const char *name
, struct stat
*st
,
1084 uint32_t mask
, uint32_t flags
);
1086 MkObjResult
mkdir(RGWFileHandle
* parent
, const char *name
, struct stat
*st
,
1087 uint32_t mask
, uint32_t flags
);
1089 int unlink(RGWFileHandle
* rgw_fh
, const char *name
,
1090 uint32_t flags
= FLAG_NONE
);
1092 /* find existing RGWFileHandle */
1093 RGWFileHandle
* lookup_handle(struct rgw_fh_hk fh_hk
) {
1095 if (state
.flags
& FLAG_CLOSED
)
1098 RGWFileHandle::FHCache::Latch lat
;
1103 fh_cache
.find_latch(fhk
.fh_hk
.object
/* partition selector*/,
1104 fhk
/* key */, lat
/* serializer */,
1105 RGWFileHandle::FHCache::FLAG_LOCK
);
1108 lsubdout(get_context(), rgw
, 0)
1109 << __func__
<< " handle lookup failed <"
1110 << fhk
.fh_hk
.bucket
<< "," << fhk
.fh_hk
.object
<< ">"
1111 << "(need persistent handles)"
1116 if (fh
->flags
& RGWFileHandle::FLAG_DELETED
) {
1117 /* for now, delay briefly and retry */
1119 fh
->mtx
.unlock(); /* !LOCKED */
1120 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1121 goto retry
; /* !LATCHED */
1123 if (! fh_lru
.ref(fh
, cohort::lru::FLAG_INITIAL
)) {
1126 goto retry
; /* !LATCHED */
1129 fh
->mtx
.unlock(); /* !LOCKED */
1131 lat
.lock
->unlock(); /* !LATCHED */
1133 /* special case: lookup root_fh */
1135 if (unlikely(fh_hk
== root_fh
.fh
.fh_hk
)) {
1143 CephContext
* get_context() {
1147 struct rgw_fs
* get_fs() { return &fs
; }
1149 uint32_t get_inst() { return root_fh
.state
.dev
; }
1151 RGWUserInfo
* get_user() { return &user
; }
1157 static inline std::string
make_uri(const std::string
& bucket_name
,
1158 const std::string
& object_name
) {
1159 std::string
uri("/");
1160 uri
.reserve(bucket_name
.length() + object_name
.length() + 2);
1168 read directory content (buckets)
1171 class RGWListBucketsRequest
: public RGWLibRequest
,
1172 public RGWListBuckets
/* RGWOp */
1175 RGWFileHandle
* rgw_fh
;
1182 RGWListBucketsRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1183 RGWFileHandle
* _rgw_fh
, rgw_readdir_cb _rcb
,
1184 void* _cb_arg
, uint64_t* _offset
)
1185 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), offset(_offset
),
1186 cb_arg(_cb_arg
), rcb(_rcb
), ix(0), d_count(0) {
1187 const auto& mk
= rgw_fh
->find_marker(*offset
);
1194 bool only_bucket() override
{ return false; }
1196 int op_init() override
{
1197 // assign store, s, and dialect_handler
1198 RGWObjectCtx
* rados_ctx
1199 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1200 // framework promises to call op_init after parent init
1202 RGWOp::init(rados_ctx
->store
, get_state(), this);
1203 op
= this; // assign self as op: REQUIRED
1207 int header_init() override
{
1208 struct req_state
* s
= get_state();
1209 s
->info
.method
= "GET";
1212 /* XXX derp derp derp */
1213 s
->relative_uri
= "/";
1214 s
->info
.request_uri
= "/"; // XXX
1215 s
->info
.effective_uri
= "/";
1216 s
->info
.request_params
= "";
1217 s
->info
.domain
= ""; /* XXX ? */
1225 int get_params() override
{
1226 limit
= -1; /* no limit */
1230 void send_response_begin(bool has_buckets
) override
{
1234 void send_response_data(RGWUserBuckets
& buckets
) override
{
1237 map
<string
, RGWBucketEnt
>& m
= buckets
.get_buckets();
1238 for (const auto& iter
: m
) {
1239 boost::string_ref marker
{iter
.first
};
1240 const RGWBucketEnt
& ent
= iter
.second
;
1241 if (! this->operator()(ent
.bucket
.name
, marker
)) {
1242 /* caller cannot accept more */
1243 lsubdout(cct
, rgw
, 5) << "ListBuckets rcb failed"
1244 << " dirent=" << ent
.bucket
.name
1245 << " call count=" << ix
1251 } /* send_response_data */
1253 void send_response_end() override
{
1257 int operator()(const boost::string_ref
& name
,
1258 const boost::string_ref
& marker
) {
1259 uint64_t off
= XXH64(name
.data(), name
.length(), fh_key::seed
);
1261 /* update traversal cache */
1262 rgw_fh
->add_marker(off
, rgw_obj_key
{marker
.data(), ""},
1263 RGW_FS_TYPE_DIRECTORY
);
1265 return rcb(name
.data(), cb_arg
, off
, RGW_LOOKUP_FLAG_DIR
);
1269 lsubdout(cct
, rgw
, 15) << "READDIR offset: " << *offset
1270 << " is_truncated: " << is_truncated
1272 return !is_truncated
;
1275 }; /* RGWListBucketsRequest */
1278 read directory content (bucket objects)
1281 class RGWReaddirRequest
: public RGWLibRequest
,
1282 public RGWListBucket
/* RGWOp */
1285 RGWFileHandle
* rgw_fh
;
1292 RGWReaddirRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1293 RGWFileHandle
* _rgw_fh
, rgw_readdir_cb _rcb
,
1294 void* _cb_arg
, uint64_t* _offset
)
1295 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), offset(_offset
),
1296 cb_arg(_cb_arg
), rcb(_rcb
), ix(0), d_count(0) {
1297 const auto& mk
= rgw_fh
->find_marker(*offset
);
1301 default_max
= 1000; // XXX was being omitted
1305 bool only_bucket() override
{ return false; }
1307 int op_init() override
{
1308 // assign store, s, and dialect_handler
1309 RGWObjectCtx
* rados_ctx
1310 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1311 // framework promises to call op_init after parent init
1313 RGWOp::init(rados_ctx
->store
, get_state(), this);
1314 op
= this; // assign self as op: REQUIRED
1318 int header_init() override
{
1319 struct req_state
* s
= get_state();
1320 s
->info
.method
= "GET";
1323 /* XXX derp derp derp */
1324 std::string uri
= "/" + rgw_fh
->bucket_name() + "/";
1325 s
->relative_uri
= uri
;
1326 s
->info
.request_uri
= uri
; // XXX
1327 s
->info
.effective_uri
= uri
;
1328 s
->info
.request_params
= "";
1329 s
->info
.domain
= ""; /* XXX ? */
1334 prefix
= rgw_fh
->relative_object_name();
1335 if (prefix
.length() > 0)
1342 int operator()(const boost::string_ref name
, const rgw_obj_key
& marker
,
1345 assert(name
.length() > 0); // XXX
1347 /* hash offset of name in parent (short name) for NFS readdir cookie */
1348 uint64_t off
= XXH64(name
.data(), name
.length(), fh_key::seed
);
1350 /* update traversal cache */
1351 rgw_fh
->add_marker(off
, marker
, type
);
1353 return rcb(name
.data(), cb_arg
, off
,
1354 (type
== RGW_FS_TYPE_DIRECTORY
) ?
1355 RGW_LOOKUP_FLAG_DIR
:
1356 RGW_LOOKUP_FLAG_FILE
);
1359 int get_params() override
{
1364 void send_response() override
{
1365 struct req_state
* s
= get_state();
1366 for (const auto& iter
: objs
) {
1368 boost::string_ref sref
{iter
.key
.name
};
1370 lsubdout(cct
, rgw
, 15) << "readdir objects prefix: " << prefix
1371 << " obj: " << sref
<< dendl
;
1373 size_t last_del
= sref
.find_last_of('/');
1374 if (last_del
!= string::npos
)
1375 sref
.remove_prefix(last_del
+1);
1377 /* leaf directory? */
1381 lsubdout(cct
, rgw
, 15) << "RGWReaddirRequest "
1383 << "list uri=" << s
->relative_uri
<< " "
1384 << " prefix=" << prefix
<< " "
1385 << " obj path=" << iter
.key
.name
1386 << " (" << sref
<< ")" << ""
1389 if(! this->operator()(sref
, next_marker
, RGW_FS_TYPE_FILE
)) {
1390 /* caller cannot accept more */
1391 lsubdout(cct
, rgw
, 5) << "readdir rcb failed"
1392 << " dirent=" << sref
.data()
1393 << " call count=" << ix
1399 for (auto& iter
: common_prefixes
) {
1401 lsubdout(cct
, rgw
, 15) << "readdir common prefixes prefix: " << prefix
1402 << " iter first: " << iter
.first
1403 << " iter second: " << iter
.second
1406 /* XXX aieee--I have seen this case! */
1407 if (iter
.first
== "/")
1410 /* it's safest to modify the element in place--a suffix-modifying
1411 * string_ref operation is problematic since ULP rgw_file callers
1412 * will ultimately need a c-string */
1413 if (iter
.first
.back() == '/')
1414 const_cast<std::string
&>(iter
.first
).pop_back();
1416 boost::string_ref sref
{iter
.first
};
1418 size_t last_del
= sref
.find_last_of('/');
1419 if (last_del
!= string::npos
)
1420 sref
.remove_prefix(last_del
+1);
1422 lsubdout(cct
, rgw
, 15) << "RGWReaddirRequest "
1424 << "list uri=" << s
->relative_uri
<< " "
1425 << " prefix=" << prefix
<< " "
1426 << " cpref=" << sref
1429 this->operator()(sref
, next_marker
, RGW_FS_TYPE_DIRECTORY
);
1434 virtual void send_versioned_response() {
1439 lsubdout(cct
, rgw
, 15) << "READDIR offset: " << *offset
1440 << " next marker: " << next_marker
1441 << " is_truncated: " << is_truncated
1443 return !is_truncated
;
1446 }; /* RGWReaddirRequest */
1449 dir has-children predicate (bucket objects)
1452 class RGWRMdirCheck
: public RGWLibRequest
,
1453 public RGWListBucket
/* RGWOp */
1456 const RGWFileHandle
* rgw_fh
;
1460 RGWRMdirCheck (CephContext
* _cct
, RGWUserInfo
*_user
,
1461 const RGWFileHandle
* _rgw_fh
)
1462 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), valid(false),
1463 has_children(false) {
1468 bool only_bucket() override
{ return false; }
1470 int op_init() override
{
1471 // assign store, s, and dialect_handler
1472 RGWObjectCtx
* rados_ctx
1473 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1474 // framework promises to call op_init after parent init
1476 RGWOp::init(rados_ctx
->store
, get_state(), this);
1477 op
= this; // assign self as op: REQUIRED
1481 int header_init() override
{
1482 struct req_state
* s
= get_state();
1483 s
->info
.method
= "GET";
1486 std::string uri
= "/" + rgw_fh
->bucket_name() + "/";
1487 s
->relative_uri
= uri
;
1488 s
->info
.request_uri
= uri
;
1489 s
->info
.effective_uri
= uri
;
1490 s
->info
.request_params
= "";
1491 s
->info
.domain
= ""; /* XXX ? */
1495 prefix
= rgw_fh
->relative_object_name();
1496 if (prefix
.length() > 0)
1503 int get_params() override
{
1508 void send_response() override
{
1510 if ((objs
.size() > 1) ||
1512 (objs
.front().key
.name
!= prefix
))) {
1513 has_children
= true;
1516 for (auto& iter
: common_prefixes
) {
1517 /* readdir never produces a name for this case */
1518 if (iter
.first
== "/")
1520 has_children
= true;
1525 virtual void send_versioned_response() {
1529 }; /* RGWRMdirCheck */
1535 class RGWCreateBucketRequest
: public RGWLibRequest
,
1536 public RGWCreateBucket
/* RGWOp */
1539 const std::string
& bucket_name
;
1541 RGWCreateBucketRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1542 std::string
& _bname
)
1543 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
) {
1547 bool only_bucket() override
{ return false; }
1549 int read_permissions(RGWOp
* op_obj
) override
{
1550 /* we ARE a 'create bucket' request (cf. rgw_rest.cc, ll. 1305-6) */
1554 int op_init() override
{
1555 // assign store, s, and dialect_handler
1556 RGWObjectCtx
* rados_ctx
1557 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1558 // framework promises to call op_init after parent init
1560 RGWOp::init(rados_ctx
->store
, get_state(), this);
1561 op
= this; // assign self as op: REQUIRED
1565 int header_init() override
{
1567 struct req_state
* s
= get_state();
1568 s
->info
.method
= "PUT";
1571 string uri
= "/" + bucket_name
;
1572 /* XXX derp derp derp */
1573 s
->relative_uri
= uri
;
1574 s
->info
.request_uri
= uri
; // XXX
1575 s
->info
.effective_uri
= uri
;
1576 s
->info
.request_params
= "";
1577 s
->info
.domain
= ""; /* XXX ? */
1585 int get_params() override
{
1586 struct req_state
* s
= get_state();
1587 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1588 /* we don't have (any) headers, so just create canned ACLs */
1589 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
1594 void send_response() override
{
1595 /* TODO: something (maybe) */
1597 }; /* RGWCreateBucketRequest */
1603 class RGWDeleteBucketRequest
: public RGWLibRequest
,
1604 public RGWDeleteBucket
/* RGWOp */
1607 const std::string
& bucket_name
;
1609 RGWDeleteBucketRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1610 std::string
& _bname
)
1611 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
) {
1615 bool only_bucket() override
{ return true; }
1617 int op_init() override
{
1618 // assign store, s, and dialect_handler
1619 RGWObjectCtx
* rados_ctx
1620 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1621 // framework promises to call op_init after parent init
1623 RGWOp::init(rados_ctx
->store
, get_state(), this);
1624 op
= this; // assign self as op: REQUIRED
1628 int header_init() override
{
1630 struct req_state
* s
= get_state();
1631 s
->info
.method
= "DELETE";
1634 string uri
= "/" + bucket_name
;
1635 /* XXX derp derp derp */
1636 s
->relative_uri
= uri
;
1637 s
->info
.request_uri
= uri
; // XXX
1638 s
->info
.effective_uri
= uri
;
1639 s
->info
.request_params
= "";
1640 s
->info
.domain
= ""; /* XXX ? */
1648 void send_response() override
{}
1650 }; /* RGWDeleteBucketRequest */
1655 class RGWPutObjRequest
: public RGWLibRequest
,
1656 public RGWPutObj
/* RGWOp */
1659 const std::string
& bucket_name
;
1660 const std::string
& obj_name
;
1661 buffer::list
& bl
; /* XXX */
1662 size_t bytes_written
;
1664 RGWPutObjRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1665 const std::string
& _bname
, const std::string
& _oname
,
1667 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
),
1668 bl(_bl
), bytes_written(0) {
1672 bool only_bucket() override
{ return true; }
1674 int op_init() override
{
1675 // assign store, s, and dialect_handler
1676 RGWObjectCtx
* rados_ctx
1677 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1678 // framework promises to call op_init after parent init
1680 RGWOp::init(rados_ctx
->store
, get_state(), this);
1681 op
= this; // assign self as op: REQUIRED
1683 int rc
= valid_s3_object_name(obj_name
);
1690 int header_init() override
{
1692 struct req_state
* s
= get_state();
1693 s
->info
.method
= "PUT";
1696 /* XXX derp derp derp */
1697 std::string uri
= make_uri(bucket_name
, obj_name
);
1698 s
->relative_uri
= uri
;
1699 s
->info
.request_uri
= uri
; // XXX
1700 s
->info
.effective_uri
= uri
;
1701 s
->info
.request_params
= "";
1702 s
->info
.domain
= ""; /* XXX ? */
1704 /* XXX required in RGWOp::execute() */
1705 s
->content_length
= bl
.length();
1713 int get_params() override
{
1714 struct req_state
* s
= get_state();
1715 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1716 /* we don't have (any) headers, so just create canned ACLs */
1717 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
1722 int get_data(buffer::list
& _bl
) override
{
1723 /* XXX for now, use sharing semantics */
1725 uint32_t len
= _bl
.length();
1726 bytes_written
+= len
;
1730 void send_response() override
{}
1732 int verify_params() override
{
1733 if (bl
.length() > cct
->_conf
->rgw_max_put_size
)
1734 return -ERR_TOO_LARGE
;
1738 }; /* RGWPutObjRequest */
1744 class RGWReadRequest
: public RGWLibRequest
,
1745 public RGWGetObj
/* RGWOp */
1748 RGWFileHandle
* rgw_fh
;
1751 size_t read_resid
; /* initialize to len, <= sizeof(ulp_buffer) */
1752 bool do_hexdump
= false;
1754 RGWReadRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1755 RGWFileHandle
* _rgw_fh
, uint64_t off
, uint64_t len
,
1757 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), ulp_buffer(_ulp_buffer
),
1758 nread(0), read_resid(len
) {
1761 /* fixup RGWGetObj (already know range parameters) */
1762 RGWGetObj::range_parsed
= true;
1763 RGWGetObj::get_data
= true; // XXX
1764 RGWGetObj::partial_content
= true;
1765 RGWGetObj::ofs
= off
;
1766 RGWGetObj::end
= off
+ len
;
1769 bool only_bucket() override
{ return false; }
1771 int op_init() override
{
1772 // assign store, s, and dialect_handler
1773 RGWObjectCtx
* rados_ctx
1774 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1775 // framework promises to call op_init after parent init
1777 RGWOp::init(rados_ctx
->store
, get_state(), this);
1778 op
= this; // assign self as op: REQUIRED
1782 int header_init() override
{
1784 struct req_state
* s
= get_state();
1785 s
->info
.method
= "GET";
1788 /* XXX derp derp derp */
1789 s
->relative_uri
= make_uri(rgw_fh
->bucket_name(),
1790 rgw_fh
->relative_object_name());
1791 s
->info
.request_uri
= s
->relative_uri
; // XXX
1792 s
->info
.effective_uri
= s
->relative_uri
;
1793 s
->info
.request_params
= "";
1794 s
->info
.domain
= ""; /* XXX ? */
1802 int get_params() override
{
1806 int send_response_data(ceph::buffer::list
& bl
, off_t bl_off
,
1807 off_t bl_len
) override
{
1809 for (auto& bp
: bl
.buffers()) {
1810 /* if for some reason bl_off indicates the start-of-data is not at
1811 * the current buffer::ptr, skip it and account */
1812 if (bl_off
> bp
.length()) {
1813 bl_off
-= bp
.length();
1816 /* read no more than read_resid */
1817 bytes
= std::min(read_resid
, size_t(bp
.length()-bl_off
));
1818 memcpy(static_cast<char*>(ulp_buffer
)+nread
, bp
.c_str()+bl_off
, bytes
);
1819 read_resid
-= bytes
; /* reduce read_resid by bytes read */
1822 /* stop if we have no residual ulp_buffer */
1829 int send_response_data_error() override
{
1830 /* S3 implementation just sends nothing--there is no side effect
1831 * to simulate here */
1835 }; /* RGWReadRequest */
1841 class RGWDeleteObjRequest
: public RGWLibRequest
,
1842 public RGWDeleteObj
/* RGWOp */
1845 const std::string
& bucket_name
;
1846 const std::string
& obj_name
;
1848 RGWDeleteObjRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1849 const std::string
& _bname
, const std::string
& _oname
)
1850 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
) {
1854 bool only_bucket() override
{ return true; }
1856 int op_init() override
{
1857 // assign store, s, and dialect_handler
1858 RGWObjectCtx
* rados_ctx
1859 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1860 // framework promises to call op_init after parent init
1862 RGWOp::init(rados_ctx
->store
, get_state(), this);
1863 op
= this; // assign self as op: REQUIRED
1867 int header_init() override
{
1869 struct req_state
* s
= get_state();
1870 s
->info
.method
= "DELETE";
1873 /* XXX derp derp derp */
1874 std::string uri
= make_uri(bucket_name
, obj_name
);
1875 s
->relative_uri
= uri
;
1876 s
->info
.request_uri
= uri
; // XXX
1877 s
->info
.effective_uri
= uri
;
1878 s
->info
.request_params
= "";
1879 s
->info
.domain
= ""; /* XXX ? */
1887 void send_response() override
{}
1889 }; /* RGWDeleteObjRequest */
1891 class RGWStatObjRequest
: public RGWLibRequest
,
1892 public RGWGetObj
/* RGWOp */
1895 const std::string
& bucket_name
;
1896 const std::string
& obj_name
;
1900 static constexpr uint32_t FLAG_NONE
= 0x000;
1902 RGWStatObjRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1903 const std::string
& _bname
, const std::string
& _oname
,
1905 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
),
1906 _size(0), flags(_flags
) {
1909 /* fixup RGWGetObj (already know range parameters) */
1910 RGWGetObj::range_parsed
= true;
1911 RGWGetObj::get_data
= false; // XXX
1912 RGWGetObj::partial_content
= true;
1914 RGWGetObj::end
= UINT64_MAX
;
1917 const string
name() override
{ return "stat_obj"; }
1918 RGWOpType
get_type() override
{ return RGW_OP_STAT_OBJ
; }
1920 real_time
get_mtime() const {
1925 uint64_t get_size() { return _size
; }
1926 real_time
ctime() { return mod_time
; } // XXX
1927 real_time
mtime() { return mod_time
; }
1928 std::map
<string
, bufferlist
>& get_attrs() { return attrs
; }
1930 buffer::list
* get_attr(const std::string
& k
) {
1931 auto iter
= attrs
.find(k
);
1932 return (iter
!= attrs
.end()) ? &(iter
->second
) : nullptr;
1935 bool only_bucket() override
{ return false; }
1937 int op_init() override
{
1938 // assign store, s, and dialect_handler
1939 RGWObjectCtx
* rados_ctx
1940 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
1941 // framework promises to call op_init after parent init
1943 RGWOp::init(rados_ctx
->store
, get_state(), this);
1944 op
= this; // assign self as op: REQUIRED
1948 int header_init() override
{
1950 struct req_state
* s
= get_state();
1951 s
->info
.method
= "GET";
1954 /* XXX derp derp derp */
1955 s
->relative_uri
= make_uri(bucket_name
, obj_name
);
1956 s
->info
.request_uri
= s
->relative_uri
; // XXX
1957 s
->info
.effective_uri
= s
->relative_uri
;
1958 s
->info
.request_params
= "";
1959 s
->info
.domain
= ""; /* XXX ? */
1967 int get_params() override
{
1971 int send_response_data(ceph::buffer::list
& _bl
, off_t s_off
,
1972 off_t e_off
) override
{
1974 /* XXX save attrs? */
1978 int send_response_data_error() override
{
1983 void execute() override
{
1984 RGWGetObj::execute();
1985 _size
= get_state()->obj_size
;
1988 }; /* RGWStatObjRequest */
1990 class RGWStatBucketRequest
: public RGWLibRequest
,
1991 public RGWStatBucket
/* RGWOp */
1995 std::map
<std::string
, buffer::list
> attrs
;
1996 RGWLibFS::BucketStats
& bs
;
1998 RGWStatBucketRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
1999 const std::string
& _path
,
2000 RGWLibFS::BucketStats
& _stats
)
2001 : RGWLibRequest(_cct
, _user
), bs(_stats
) {
2006 buffer::list
* get_attr(const std::string
& k
) {
2007 auto iter
= attrs
.find(k
);
2008 return (iter
!= attrs
.end()) ? &(iter
->second
) : nullptr;
2011 real_time
get_ctime() const {
2012 return bucket
.creation_time
;
2015 bool only_bucket() override
{ return false; }
2017 int op_init() override
{
2018 // assign store, s, and dialect_handler
2019 RGWObjectCtx
* rados_ctx
2020 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2021 // framework promises to call op_init after parent init
2023 RGWOp::init(rados_ctx
->store
, get_state(), this);
2024 op
= this; // assign self as op: REQUIRED
2028 int header_init() override
{
2030 struct req_state
* s
= get_state();
2031 s
->info
.method
= "GET";
2034 /* XXX derp derp derp */
2035 s
->relative_uri
= uri
;
2036 s
->info
.request_uri
= uri
; // XXX
2037 s
->info
.effective_uri
= uri
;
2038 s
->info
.request_params
= "";
2039 s
->info
.domain
= ""; /* XXX ? */
2047 virtual int get_params() {
2051 void send_response() override
{
2052 bucket
.creation_time
= get_state()->bucket_info
.creation_time
;
2053 bs
.size
= bucket
.size
;
2054 bs
.size_rounded
= bucket
.size_rounded
;
2055 bs
.creation_time
= bucket
.creation_time
;
2056 bs
.num_entries
= bucket
.count
;
2057 std::swap(attrs
, get_state()->bucket_attrs
);
2061 return (bucket
.bucket
.name
.length() > 0);
2064 }; /* RGWStatBucketRequest */
2066 class RGWStatLeafRequest
: public RGWLibRequest
,
2067 public RGWListBucket
/* RGWOp */
2070 RGWFileHandle
* rgw_fh
;
2076 RGWStatLeafRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2077 RGWFileHandle
* _rgw_fh
, const std::string
& _path
)
2078 : RGWLibRequest(_cct
, _user
), rgw_fh(_rgw_fh
), path(_path
),
2079 matched(false), is_dir(false), exact_matched(false) {
2080 default_max
= 1000; // logical max {"foo", "foo/"}
2084 bool only_bucket() override
{ return false; }
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
2092 RGWOp::init(rados_ctx
->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
= "GET";
2103 /* XXX derp derp derp */
2104 std::string uri
= "/" + rgw_fh
->bucket_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 ? */
2114 prefix
= rgw_fh
->relative_object_name();
2115 if (prefix
.length() > 0)
2123 int get_params() override
{
2128 void send_response() override
{
2129 struct req_state
* s
= get_state();
2131 for (const auto& iter
: objs
) {
2132 auto& name
= iter
.key
.name
;
2133 lsubdout(cct
, rgw
, 15) << "RGWStatLeafRequest "
2135 << "list uri=" << s
->relative_uri
<< " "
2136 << " prefix=" << prefix
<< " "
2137 << " obj path=" << name
<< ""
2138 << " target = " << path
<< ""
2140 /* XXX is there a missing match-dir case (trailing '/')? */
2143 exact_matched
= true;
2147 for (auto& iter
: common_prefixes
) {
2148 auto& name
= iter
.first
;
2149 lsubdout(cct
, rgw
, 15) << "RGWStatLeafRequest "
2151 << "list uri=" << s
->relative_uri
<< " "
2152 << " prefix=" << prefix
<< " "
2153 << " pref path=" << name
<< " (not chomped)"
2154 << " target = " << path
<< ""
2162 virtual void send_versioned_response() {
2165 }; /* RGWStatLeafRequest */
2171 class RGWWriteRequest
: public RGWLibContinuedReq
,
2172 public RGWPutObj
/* RGWOp */
2175 const std::string
& bucket_name
;
2176 const std::string
& obj_name
;
2177 RGWFileHandle
* rgw_fh
;
2178 RGWPutObjProcessor
*processor
;
2183 size_t bytes_written
;
2187 RGWWriteRequest(CephContext
* _cct
, RGWUserInfo
*_user
, RGWFileHandle
* _fh
,
2188 const std::string
& _bname
, const std::string
& _oname
)
2189 : RGWLibContinuedReq(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
),
2190 rgw_fh(_fh
), processor(nullptr), real_ofs(0), bytes_written(0),
2191 multipart(false), eio(false) {
2193 int ret
= header_init();
2195 ret
= init_from_header(get_state());
2200 bool only_bucket() override
{ return true; }
2202 int op_init() override
{
2203 // assign store, s, and dialect_handler
2204 RGWObjectCtx
* rados_ctx
2205 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2206 // framework promises to call op_init after parent init
2208 RGWOp::init(rados_ctx
->store
, get_state(), this);
2209 op
= this; // assign self as op: REQUIRED
2213 int header_init() override
{
2215 struct req_state
* s
= get_state();
2216 s
->info
.method
= "PUT";
2219 /* XXX derp derp derp */
2220 std::string uri
= make_uri(bucket_name
, obj_name
);
2221 s
->relative_uri
= uri
;
2222 s
->info
.request_uri
= uri
; // XXX
2223 s
->info
.effective_uri
= uri
;
2224 s
->info
.request_params
= "";
2225 s
->info
.domain
= ""; /* XXX ? */
2233 RGWPutObjProcessor
*select_processor(RGWObjectCtx
& obj_ctx
,
2234 bool *is_multipart
) override
{
2235 struct req_state
* s
= get_state();
2236 uint64_t part_size
= s
->cct
->_conf
->rgw_obj_stripe_size
;
2237 RGWPutObjProcessor_Atomic
*processor
=
2238 new RGWPutObjProcessor_Atomic(obj_ctx
, s
->bucket_info
, s
->bucket
,
2239 s
->object
.name
, part_size
, s
->req_id
,
2240 s
->bucket_info
.versioning_enabled());
2241 processor
->set_olh_epoch(olh_epoch
);
2242 processor
->set_version_id(version_id
);
2246 int get_params() override
{
2247 struct req_state
* s
= get_state();
2248 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2249 /* we don't have (any) headers, so just create canned ACLs */
2250 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
2255 int get_data(buffer::list
& _bl
) override
{
2256 /* XXX for now, use sharing semantics */
2257 uint32_t len
= data
.length();
2259 bytes_written
+= len
;
2263 void put_data(off_t off
, buffer::list
& _bl
) {
2264 if (off
!= real_ofs
) {
2268 real_ofs
+= data
.length();
2269 ofs
= off
; /* consumed in exec_continue() */
2272 int exec_start() override
;
2273 int exec_continue() override
;
2274 int exec_finish() override
;
2276 void send_response() override
{}
2278 int verify_params() override
{
2281 }; /* RGWWriteRequest */
2286 class RGWCopyObjRequest
: public RGWLibRequest
,
2287 public RGWCopyObj
/* RGWOp */
2290 RGWFileHandle
* src_parent
;
2291 RGWFileHandle
* dst_parent
;
2292 const std::string
& src_name
;
2293 const std::string
& dst_name
;
2295 RGWCopyObjRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2296 RGWFileHandle
* _src_parent
, RGWFileHandle
* _dst_parent
,
2297 const std::string
& _src_name
, const std::string
& _dst_name
)
2298 : RGWLibRequest(_cct
, _user
), src_parent(_src_parent
),
2299 dst_parent(_dst_parent
), src_name(_src_name
), dst_name(_dst_name
) {
2300 /* all requests have this */
2303 /* allow this request to replace selected attrs */
2304 attrs_mod
= RGWRados::ATTRSMOD_MERGE
;
2307 bool only_bucket() override
{ return true; }
2309 int op_init() override
{
2310 // assign store, s, and dialect_handler
2311 RGWObjectCtx
* rados_ctx
2312 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2313 // framework promises to call op_init after parent init
2315 RGWOp::init(rados_ctx
->store
, get_state(), this);
2316 op
= this; // assign self as op: REQUIRED
2321 int header_init() override
{
2323 struct req_state
* s
= get_state();
2324 s
->info
.method
= "PUT"; // XXX check
2327 src_bucket_name
= src_parent
->bucket_name();
2328 // need s->src_bucket_name?
2329 src_object
.name
= src_parent
->format_child_name(src_name
, false);
2330 // need s->src_object?
2332 dest_bucket_name
= dst_parent
->bucket_name();
2333 // need s->bucket.name?
2334 dest_object
= dst_parent
->format_child_name(dst_name
, false);
2335 // need s->object_name?
2337 int rc
= valid_s3_object_name(dest_object
);
2341 /* XXX and fixup key attr (could optimize w/string ref and
2343 buffer::list ux_key
;
2344 std::string key_name
{dst_parent
->make_key_name(dst_name
.c_str())};
2345 fh_key fhk
= dst_parent
->make_fhk(key_name
);
2346 rgw::encode(fhk
, ux_key
);
2347 emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
2349 #if 0 /* XXX needed? */
2350 s
->relative_uri
= uri
;
2351 s
->info
.request_uri
= uri
; // XXX
2352 s
->info
.effective_uri
= uri
;
2353 s
->info
.request_params
= "";
2354 s
->info
.domain
= ""; /* XXX ? */
2363 int get_params() override
{
2364 struct req_state
* s
= get_state();
2365 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2366 /* we don't have (any) headers, so just create canned ACLs */
2367 int ret
= s3policy
.create_canned(s
->owner
, s
->bucket_owner
, s
->canned_acl
);
2368 dest_policy
= s3policy
;
2372 void send_response() override
{}
2373 void send_partial_response(off_t ofs
) override
{}
2375 }; /* RGWCopyObjRequest */
2377 class RGWSetAttrsRequest
: public RGWLibRequest
,
2378 public RGWSetAttrs
/* RGWOp */
2381 const std::string
& bucket_name
;
2382 const std::string
& obj_name
;
2384 RGWSetAttrsRequest(CephContext
* _cct
, RGWUserInfo
*_user
,
2385 const std::string
& _bname
, const std::string
& _oname
)
2386 : RGWLibRequest(_cct
, _user
), bucket_name(_bname
), obj_name(_oname
) {
2390 bool only_bucket() override
{ return false; }
2392 int op_init() override
{
2393 // assign store, s, and dialect_handler
2394 RGWObjectCtx
* rados_ctx
2395 = static_cast<RGWObjectCtx
*>(get_state()->obj_ctx
);
2396 // framework promises to call op_init after parent init
2398 RGWOp::init(rados_ctx
->store
, get_state(), this);
2399 op
= this; // assign self as op: REQUIRED
2403 int header_init() override
{
2405 struct req_state
* s
= get_state();
2406 s
->info
.method
= "PUT";
2409 /* XXX derp derp derp */
2410 std::string uri
= make_uri(bucket_name
, obj_name
);
2411 s
->relative_uri
= uri
;
2412 s
->info
.request_uri
= uri
; // XXX
2413 s
->info
.effective_uri
= uri
;
2414 s
->info
.request_params
= "";
2415 s
->info
.domain
= ""; /* XXX ? */
2423 int get_params() override
{
2427 void send_response() override
{}
2429 }; /* RGWSetAttrsRequest */
2431 } /* namespace rgw */
2433 #endif /* RGW_FILE_H */