1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
4 #include "include/compat.h"
5 #include "include/rados/rgw_file.h"
11 #include "rgw_rados.h"
12 #include "rgw_resolve.h"
16 #include "rgw_acl_s3.h"
17 #include "rgw_frontend.h"
18 #include "rgw_request.h"
19 #include "rgw_process.h"
20 #include "rgw_rest_user.h"
21 #include "rgw_rest_s3.h"
22 #include "rgw_os_lib.h"
23 #include "rgw_auth_s3.h"
25 #include "rgw_bucket.h"
28 #include "rgw_lib_frontend.h"
29 #include "rgw_perf_counters.h"
30 #include "common/errno.h"
32 #include "services/svc_zone.h"
36 #define dout_subsys ceph_subsys_rgw
44 const string
RGWFileHandle::root_name
= "/";
46 std::atomic
<uint32_t> RGWLibFS::fs_inst_counter
;
48 uint32_t RGWLibFS::write_completion_interval_s
= 10;
50 ceph::timer
<ceph::mono_clock
> RGWLibFS::write_timer
{
51 ceph::construct_suspended
};
53 inline int valid_fs_bucket_name(const string
& name
) {
54 int rc
= valid_s3_bucket_name(name
, false /* relaxed */);
56 if (name
.size() > 255)
63 inline int valid_fs_object_name(const string
& name
) {
64 int rc
= valid_s3_object_name(name
);
66 if (name
.size() > 1024)
73 LookupFHResult
RGWLibFS::stat_bucket(RGWFileHandle
* parent
, const char *path
,
74 RGWLibFS::BucketStats
& bs
,
77 LookupFHResult fhr
{nullptr, 0};
78 std::string bucket_name
{path
};
79 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
80 RGWStatBucketRequest
req(cct
, &ruser
, bucket_name
, bs
);
82 int rc
= rgwlib
.get_fe()->execute_req(&req
);
84 (req
.get_ret() == 0) &&
86 fhr
= lookup_fh(parent
, path
,
87 (flags
& RGWFileHandle::FLAG_LOCKED
)|
88 RGWFileHandle::FLAG_CREATE
|
89 RGWFileHandle::FLAG_BUCKET
);
91 RGWFileHandle
* rgw_fh
= get
<0>(fhr
);
92 if (! (flags
& RGWFileHandle::FLAG_LOCKED
)) {
95 rgw_fh
->set_times(req
.get_ctime());
96 /* restore attributes */
97 auto ux_key
= req
.get_attr(RGW_ATTR_UNIX_KEY1
);
98 auto ux_attrs
= req
.get_attr(RGW_ATTR_UNIX1
);
99 if (ux_key
&& ux_attrs
) {
100 DecodeAttrsResult dar
= rgw_fh
->decode_attrs(ux_key
, ux_attrs
);
101 if (get
<0>(dar
) || get
<1>(dar
)) {
105 if (! (flags
& RGWFileHandle::FLAG_LOCKED
)) {
106 rgw_fh
->mtx
.unlock();
113 LookupFHResult
RGWLibFS::fake_leaf(RGWFileHandle
* parent
,
115 enum rgw_fh_type type
,
116 struct stat
*st
, uint32_t st_mask
,
119 /* synthesize a minimal handle from parent, path, type, and st */
122 flags
|= RGWFileHandle::FLAG_CREATE
;
125 case RGW_FS_TYPE_DIRECTORY
:
126 flags
|= RGWFileHandle::FLAG_DIRECTORY
;
133 LookupFHResult fhr
= lookup_fh(parent
, path
, flags
);
135 RGWFileHandle
* rgw_fh
= get
<0>(fhr
);
137 lock_guard
guard(rgw_fh
->mtx
);
138 if (st_mask
& RGW_SETATTR_SIZE
) {
139 rgw_fh
->set_size(st
->st_size
);
141 if (st_mask
& RGW_SETATTR_MTIME
) {
142 rgw_fh
->set_times(st
->st_mtim
);
147 } /* RGWLibFS::fake_leaf */
149 LookupFHResult
RGWLibFS::stat_leaf(RGWFileHandle
* parent
,
151 enum rgw_fh_type type
,
154 /* find either-of <object_name>, <object_name/>, only one of
155 * which should exist; atomicity? */
158 LookupFHResult fhr
{nullptr, 0};
160 /* XXX the need for two round-trip operations to identify file or
161 * directory leaf objects is unecessary--the current proposed
162 * mechanism to avoid this is to store leaf object names with an
163 * object locator w/o trailing slash */
165 std::string obj_path
= parent
->format_child_name(path
, false);
166 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
168 for (auto ix
: { 0, 1, 2 }) {
173 if (type
== RGW_FS_TYPE_DIRECTORY
)
176 RGWStatObjRequest
req(cct
, &ruser
,
177 parent
->bucket_name(), obj_path
,
178 RGWStatObjRequest::FLAG_NONE
);
179 int rc
= rgwlib
.get_fe()->execute_req(&req
);
181 (req
.get_ret() == 0)) {
182 fhr
= lookup_fh(parent
, path
, RGWFileHandle::FLAG_CREATE
);
184 RGWFileHandle
* rgw_fh
= get
<0>(fhr
);
185 lock_guard
guard(rgw_fh
->mtx
);
186 rgw_fh
->set_size(req
.get_size());
187 rgw_fh
->set_times(req
.get_mtime());
188 /* restore attributes */
189 auto ux_key
= req
.get_attr(RGW_ATTR_UNIX_KEY1
);
190 auto ux_attrs
= req
.get_attr(RGW_ATTR_UNIX1
);
191 rgw_fh
->set_etag(*(req
.get_attr(RGW_ATTR_ETAG
)));
192 rgw_fh
->set_acls(*(req
.get_attr(RGW_ATTR_ACL
)));
193 if (ux_key
&& ux_attrs
) {
194 DecodeAttrsResult dar
= rgw_fh
->decode_attrs(ux_key
, ux_attrs
);
195 if (get
<0>(dar
) || get
<1>(dar
)) {
208 if (type
== RGW_FS_TYPE_FILE
)
212 RGWStatObjRequest
req(cct
, &ruser
,
213 parent
->bucket_name(), obj_path
,
214 RGWStatObjRequest::FLAG_NONE
);
215 int rc
= rgwlib
.get_fe()->execute_req(&req
);
217 (req
.get_ret() == 0)) {
218 fhr
= lookup_fh(parent
, path
, RGWFileHandle::FLAG_DIRECTORY
);
220 RGWFileHandle
* rgw_fh
= get
<0>(fhr
);
221 lock_guard
guard(rgw_fh
->mtx
);
222 rgw_fh
->set_size(req
.get_size());
223 rgw_fh
->set_times(req
.get_mtime());
224 /* restore attributes */
225 auto ux_key
= req
.get_attr(RGW_ATTR_UNIX_KEY1
);
226 auto ux_attrs
= req
.get_attr(RGW_ATTR_UNIX1
);
227 rgw_fh
->set_etag(*(req
.get_attr(RGW_ATTR_ETAG
)));
228 rgw_fh
->set_acls(*(req
.get_attr(RGW_ATTR_ACL
)));
229 if (ux_key
&& ux_attrs
) {
230 DecodeAttrsResult dar
= rgw_fh
->decode_attrs(ux_key
, ux_attrs
);
231 if (get
<0>(dar
) || get
<1>(dar
)) {
242 std::string object_name
{path
};
243 RGWStatLeafRequest
req(cct
, &ruser
, parent
, object_name
);
244 int rc
= rgwlib
.get_fe()->execute_req(&req
);
246 (req
.get_ret() == 0)) {
248 /* we need rgw object's key name equal to file name, if
250 if ((flags
& RGWFileHandle::FLAG_EXACT_MATCH
) &&
251 !req
.exact_matched
) {
252 lsubdout(get_context(), rgw
, 15)
254 << ": stat leaf not exact match file name = "
258 fhr
= lookup_fh(parent
, path
,
259 RGWFileHandle::FLAG_CREATE
|
261 RGWFileHandle::FLAG_DIRECTORY
:
262 RGWFileHandle::FLAG_NONE
));
263 /* XXX we don't have an object--in general, there need not
264 * be one (just a path segment in some other object). In
265 * actual leaf an object exists, but we'd need another round
266 * trip to get attrs */
268 /* for now use the parent object's mtime */
269 RGWFileHandle
* rgw_fh
= get
<0>(fhr
);
270 lock_guard
guard(rgw_fh
->mtx
);
271 rgw_fh
->set_mtime(parent
->get_mtime());
284 } /* RGWLibFS::stat_leaf */
286 int RGWLibFS::read(RGWFileHandle
* rgw_fh
, uint64_t offset
, size_t length
,
287 size_t* bytes_read
, void* buffer
, uint32_t flags
)
289 if (! rgw_fh
->is_file())
292 if (rgw_fh
->deleted())
295 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
296 RGWReadRequest
req(get_context(), &ruser
, rgw_fh
, offset
, length
,
299 int rc
= rgwlib
.get_fe()->execute_req(&req
);
301 (req
.get_ret() == 0)) {
302 lock_guard
guard(rgw_fh
->mtx
);
303 rgw_fh
->set_atime(real_clock::to_timespec(real_clock::now()));
304 *bytes_read
= req
.nread
;
310 int RGWLibFS::readlink(RGWFileHandle
* rgw_fh
, uint64_t offset
, size_t length
,
311 size_t* bytes_read
, void* buffer
, uint32_t flags
)
313 if (! rgw_fh
->is_link())
316 if (rgw_fh
->deleted())
319 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
320 RGWReadRequest
req(get_context(), &ruser
, rgw_fh
, offset
, length
,
323 int rc
= rgwlib
.get_fe()->execute_req(&req
);
325 (req
.get_ret() == 0)) {
326 lock_guard(rgw_fh
->mtx
);
327 rgw_fh
->set_atime(real_clock::to_timespec(real_clock::now()));
328 *bytes_read
= req
.nread
;
334 int RGWLibFS::unlink(RGWFileHandle
* rgw_fh
, const char* name
, uint32_t flags
)
338 RGWFileHandle
* parent
= nullptr;
339 RGWFileHandle
* bkt_fh
= nullptr;
341 if (unlikely(flags
& RGWFileHandle::FLAG_UNLINK_THIS
)) {
343 parent
= rgw_fh
->get_parent();
347 LookupFHResult fhr
= lookup_fh(parent
, name
, RGWFileHandle::FLAG_LOCK
);
348 rgw_fh
= get
<0>(fhr
);
352 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
353 if (parent
->is_root()) {
354 /* a bucket may have an object storing Unix attributes, check
355 * for and delete it */
357 fhr
= stat_bucket(parent
, name
, bs
, (rgw_fh
) ?
358 RGWFileHandle::FLAG_LOCKED
:
359 RGWFileHandle::FLAG_NONE
);
360 bkt_fh
= get
<0>(fhr
);
361 if (unlikely(! bkt_fh
)) {
362 /* implies !rgw_fh, so also !LOCKED */
366 if (bs
.num_entries
> 1) {
367 unref(bkt_fh
); /* return stat_bucket ref */
368 if (likely(!! rgw_fh
)) { /* return lock and ref from
369 * lookup_fh (or caller in the
371 * RGWFileHandle::FLAG_UNLINK_THIS) */
372 rgw_fh
->mtx
.unlock();
377 /* delete object w/key "<bucket>/" (uxattrs), if any */
379 RGWDeleteObjRequest
req(cct
, &ruser
, bkt_fh
->bucket_name(), oname
);
380 rc
= rgwlib
.get_fe()->execute_req(&req
);
381 /* don't care if ENOENT */
386 RGWDeleteBucketRequest
req(cct
, &ruser
, bname
);
387 rc
= rgwlib
.get_fe()->execute_req(&req
);
396 /* XXX for now, peform a hard lookup to deduce the type of
397 * object to be deleted ("foo" vs. "foo/")--also, ensures
398 * atomicity at this endpoint */
399 struct rgw_file_handle
*fh
;
400 rc
= rgw_lookup(get_fs(), parent
->get_fh(), name
, &fh
,
401 nullptr /* st */, 0 /* mask */,
402 RGW_LOOKUP_FLAG_NONE
);
407 rgw_fh
= get_rgwfh(fh
);
408 rgw_fh
->mtx
.lock(); /* LOCKED */
411 std::string oname
= rgw_fh
->relative_object_name();
412 if (rgw_fh
->is_dir()) {
413 /* for the duration of our cache timer, trust positive
415 if (rgw_fh
->has_children()) {
416 rgw_fh
->mtx
.unlock();
422 RGWDeleteObjRequest
req(cct
, &ruser
, parent
->bucket_name(),
424 rc
= rgwlib
.get_fe()->execute_req(&req
);
430 /* ENOENT when raced with other s3 gateway */
431 if (! rc
|| rc
== -ENOENT
) {
432 rgw_fh
->flags
|= RGWFileHandle::FLAG_DELETED
;
433 fh_cache
.remove(rgw_fh
->fh
.fh_hk
.object
, rgw_fh
,
434 RGWFileHandle::FHCache::FLAG_LOCK
);
438 real_time t
= real_clock::now();
439 parent
->set_mtime(real_clock::to_timespec(t
));
440 parent
->set_ctime(real_clock::to_timespec(t
));
443 rgw_fh
->mtx
.unlock();
447 } /* RGWLibFS::unlink */
449 int RGWLibFS::rename(RGWFileHandle
* src_fh
, RGWFileHandle
* dst_fh
,
450 const char *_src_name
, const char *_dst_name
)
453 /* XXX initial implementation: try-copy, and delete if copy
456 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
460 std::string src_name
{_src_name
};
461 std::string dst_name
{_dst_name
};
464 LookupFHResult fhr
= lookup_fh(src_fh
, _src_name
, RGWFileHandle::FLAG_LOCK
);
465 RGWFileHandle
* rgw_fh
= get
<0>(fhr
);
467 /* should not happen */
469 ldout(get_context(), 0) << __func__
470 << " BUG no such src renaming path="
476 /* forbid renaming of directories (unreasonable at scale) */
477 if (rgw_fh
->is_dir()) {
478 ldout(get_context(), 12) << __func__
479 << " rejecting attempt to rename directory path="
480 << rgw_fh
->full_object_name()
486 /* forbid renaming open files (violates intent, for now) */
487 if (rgw_fh
->is_open()) {
488 ldout(get_context(), 12) << __func__
489 << " rejecting attempt to rename open file path="
490 << rgw_fh
->full_object_name()
496 t
= real_clock::now();
498 for (int ix
: {0, 1}) {
502 RGWCopyObjRequest
req(cct
, &ruser
, src_fh
, dst_fh
, src_name
,
504 int rc
= rgwlib
.get_fe()->execute_req(&req
);
506 ((rc
= req
.get_ret()) != 0)) {
507 ldout(get_context(), 1)
509 << " rename step 0 failed src="
510 << src_fh
->full_object_name() << " " << src_name
511 << " dst=" << dst_fh
->full_object_name()
517 ldout(get_context(), 12)
519 << " rename step 0 success src="
520 << src_fh
->full_object_name() << " " << src_name
521 << " dst=" << dst_fh
->full_object_name()
525 /* update dst change id */
526 dst_fh
->set_times(t
);
531 rc
= this->unlink(rgw_fh
/* LOCKED */, _src_name
,
532 RGWFileHandle::FLAG_UNLINK_THIS
);
535 ldout(get_context(), 12)
537 << " rename step 1 success src="
538 << src_fh
->full_object_name() << " " << src_name
539 << " dst=" << dst_fh
->full_object_name()
543 /* update src change id */
544 src_fh
->set_times(t
);
546 ldout(get_context(), 1)
548 << " rename step 1 failed src="
549 << src_fh
->full_object_name() << " " << src_name
550 << " dst=" << dst_fh
->full_object_name()
562 rgw_fh
->mtx
.unlock(); /* !LOCKED */
563 unref(rgw_fh
); /* -ref */
567 } /* RGWLibFS::rename */
569 MkObjResult
RGWLibFS::mkdir(RGWFileHandle
* parent
, const char *name
,
570 struct stat
*st
, uint32_t mask
, uint32_t flags
)
573 rgw_file_handle
*lfh
;
574 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
576 rc
= rgw_lookup(get_fs(), parent
->get_fh(), name
, &lfh
,
577 nullptr /* st */, 0 /* mask */,
578 RGW_LOOKUP_FLAG_NONE
);
581 rc
= rgw_fh_rele(get_fs(), lfh
, RGW_FH_RELE_FLAG_NONE
);
582 return MkObjResult
{nullptr, -EEXIST
};
585 MkObjResult mkr
{nullptr, -EINVAL
};
587 RGWFileHandle
* rgw_fh
= nullptr;
588 buffer::list ux_key
, ux_attrs
;
590 fhr
= lookup_fh(parent
, name
,
591 RGWFileHandle::FLAG_CREATE
|
592 RGWFileHandle::FLAG_DIRECTORY
|
593 RGWFileHandle::FLAG_LOCK
);
594 rgw_fh
= get
<0>(fhr
);
596 rgw_fh
->create_stat(st
, mask
);
597 rgw_fh
->set_times(real_clock::now());
599 rgw_fh
->encode_attrs(ux_key
, ux_attrs
);
601 rgw_fh
->stat(st
, RGWFileHandle::FLAG_LOCKED
);
602 get
<0>(mkr
) = rgw_fh
;
608 if (parent
->is_root()) {
611 /* enforce S3 name restrictions */
612 rc
= valid_fs_bucket_name(bname
);
614 rgw_fh
->flags
|= RGWFileHandle::FLAG_DELETED
;
615 fh_cache
.remove(rgw_fh
->fh
.fh_hk
.object
, rgw_fh
,
616 RGWFileHandle::FHCache::FLAG_LOCK
);
617 rgw_fh
->mtx
.unlock();
619 get
<0>(mkr
) = nullptr;
624 RGWCreateBucketRequest
req(get_context(), &ruser
, bname
);
627 req
.emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
628 req
.emplace_attr(RGW_ATTR_UNIX1
, std::move(ux_attrs
));
630 rc
= rgwlib
.get_fe()->execute_req(&req
);
633 /* create an object representing the directory */
635 string dir_name
= parent
->format_child_name(name
, true);
637 /* need valid S3 name (characters, length <= 1024, etc) */
638 rc
= valid_fs_object_name(dir_name
);
640 rgw_fh
->flags
|= RGWFileHandle::FLAG_DELETED
;
641 fh_cache
.remove(rgw_fh
->fh
.fh_hk
.object
, rgw_fh
,
642 RGWFileHandle::FHCache::FLAG_LOCK
);
643 rgw_fh
->mtx
.unlock();
645 get
<0>(mkr
) = nullptr;
650 RGWPutObjRequest
req(get_context(), &ruser
, parent
->bucket_name(),
654 req
.emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
655 req
.emplace_attr(RGW_ATTR_UNIX1
, std::move(ux_attrs
));
657 rc
= rgwlib
.get_fe()->execute_req(&req
);
664 rgw_fh
->flags
|= RGWFileHandle::FLAG_DELETED
;
665 rgw_fh
->mtx
.unlock(); /* !LOCKED */
667 get
<0>(mkr
) = nullptr;
672 real_time t
= real_clock::now();
673 parent
->set_mtime(real_clock::to_timespec(t
));
674 parent
->set_ctime(real_clock::to_timespec(t
));
675 rgw_fh
->mtx
.unlock(); /* !LOCKED */
681 } /* RGWLibFS::mkdir */
683 MkObjResult
RGWLibFS::create(RGWFileHandle
* parent
, const char *name
,
684 struct stat
*st
, uint32_t mask
, uint32_t flags
)
690 rgw_file_handle
*lfh
;
691 rc
= rgw_lookup(get_fs(), parent
->get_fh(), name
, &lfh
,
692 nullptr /* st */, 0 /* mask */,
693 RGW_LOOKUP_FLAG_NONE
);
696 rc
= rgw_fh_rele(get_fs(), lfh
, RGW_FH_RELE_FLAG_NONE
);
697 return MkObjResult
{nullptr, -EEXIST
};
700 /* expand and check name */
701 std::string obj_name
= parent
->format_child_name(name
, false);
702 rc
= valid_fs_object_name(obj_name
);
704 return MkObjResult
{nullptr, rc
};
709 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
710 RGWPutObjRequest
req(cct
, &ruser
, parent
->bucket_name(), obj_name
, bl
);
711 MkObjResult mkr
{nullptr, -EINVAL
};
713 rc
= rgwlib
.get_fe()->execute_req(&req
);
719 LookupFHResult fhr
= lookup_fh(parent
, name
, RGWFileHandle::FLAG_CREATE
|
720 RGWFileHandle::FLAG_LOCK
);
721 RGWFileHandle
* rgw_fh
= get
<0>(fhr
);
723 if (get
<1>(fhr
) & RGWFileHandle::FLAG_CREATE
) {
724 /* fill in stat data */
725 real_time t
= real_clock::now();
726 rgw_fh
->create_stat(st
, mask
);
727 rgw_fh
->set_times(t
);
729 parent
->set_mtime(real_clock::to_timespec(t
));
730 parent
->set_ctime(real_clock::to_timespec(t
));
733 (void) rgw_fh
->stat(st
, RGWFileHandle::FLAG_LOCKED
);
735 rgw_fh
->set_etag(*(req
.get_attr(RGW_ATTR_ETAG
)));
736 rgw_fh
->set_acls(*(req
.get_attr(RGW_ATTR_ACL
)));
738 get
<0>(mkr
) = rgw_fh
;
739 rgw_fh
->mtx
.unlock();
746 /* case like : quota exceed will be considered as fail too*/
751 } /* RGWLibFS::create */
753 MkObjResult
RGWLibFS::symlink(RGWFileHandle
* parent
, const char *name
,
754 const char* link_path
, struct stat
*st
, uint32_t mask
, uint32_t flags
)
760 rgw_file_handle
*lfh
;
761 rc
= rgw_lookup(get_fs(), parent
->get_fh(), name
, &lfh
,
762 nullptr /* st */, 0 /* mask */,
763 RGW_LOOKUP_FLAG_NONE
);
766 rc
= rgw_fh_rele(get_fs(), lfh
, RGW_FH_RELE_FLAG_NONE
);
767 return MkObjResult
{nullptr, -EEXIST
};
770 MkObjResult mkr
{nullptr, -EINVAL
};
772 RGWFileHandle
* rgw_fh
= nullptr;
773 buffer::list ux_key
, ux_attrs
;
775 fhr
= lookup_fh(parent
, name
,
776 RGWFileHandle::FLAG_CREATE
|
777 RGWFileHandle::FLAG_SYMBOLIC_LINK
|
778 RGWFileHandle::FLAG_LOCK
);
779 rgw_fh
= get
<0>(fhr
);
781 rgw_fh
->create_stat(st
, mask
);
782 rgw_fh
->set_times(real_clock::now());
784 rgw_fh
->encode_attrs(ux_key
, ux_attrs
);
787 get
<0>(mkr
) = rgw_fh
;
793 /* need valid S3 name (characters, length <= 1024, etc) */
794 rc
= valid_fs_object_name(name
);
796 rgw_fh
->flags
|= RGWFileHandle::FLAG_DELETED
;
797 fh_cache
.remove(rgw_fh
->fh
.fh_hk
.object
, rgw_fh
,
798 RGWFileHandle::FHCache::FLAG_LOCK
);
799 rgw_fh
->mtx
.unlock();
801 get
<0>(mkr
) = nullptr;
806 string obj_name
= std::string(name
);
807 /* create an object representing the directory */
813 buffer::create_static(len
, static_cast<char*>(buffer
)));
817 buffer::copy(link_path
, strlen(link_path
)));
820 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
821 RGWPutObjRequest
req(get_context(), &ruser
, parent
->bucket_name(),
825 req
.emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
826 req
.emplace_attr(RGW_ATTR_UNIX1
, std::move(ux_attrs
));
828 rc
= rgwlib
.get_fe()->execute_req(&req
);
833 rgw_fh
->flags
|= RGWFileHandle::FLAG_DELETED
;
834 rgw_fh
->mtx
.unlock(); /* !LOCKED */
836 get
<0>(mkr
) = nullptr;
841 real_time t
= real_clock::now();
842 parent
->set_mtime(real_clock::to_timespec(t
));
843 parent
->set_ctime(real_clock::to_timespec(t
));
844 rgw_fh
->mtx
.unlock(); /* !LOCKED */
850 } /* RGWLibFS::symlink */
852 int RGWLibFS::getattr(RGWFileHandle
* rgw_fh
, struct stat
* st
)
854 switch(rgw_fh
->fh
.fh_type
) {
855 case RGW_FS_TYPE_FILE
:
857 if (rgw_fh
->deleted())
864 /* if rgw_fh is a directory, mtime will be advanced */
865 return rgw_fh
->stat(st
);
866 } /* RGWLibFS::getattr */
868 int RGWLibFS::setattr(RGWFileHandle
* rgw_fh
, struct stat
* st
, uint32_t mask
,
872 buffer::list ux_key
, ux_attrs
;
873 buffer::list etag
= rgw_fh
->get_etag();
874 buffer::list acls
= rgw_fh
->get_acls();
876 lock_guard
guard(rgw_fh
->mtx
);
878 switch(rgw_fh
->fh
.fh_type
) {
879 case RGW_FS_TYPE_FILE
:
881 if (rgw_fh
->deleted())
889 string obj_name
{rgw_fh
->relative_object_name()};
891 if (rgw_fh
->is_dir() &&
892 (likely(! rgw_fh
->is_bucket()))) {
896 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
897 RGWSetAttrsRequest
req(cct
, &ruser
, rgw_fh
->bucket_name(), obj_name
);
899 rgw_fh
->create_stat(st
, mask
);
900 rgw_fh
->encode_attrs(ux_key
, ux_attrs
);
903 req
.emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
904 req
.emplace_attr(RGW_ATTR_UNIX1
, std::move(ux_attrs
));
905 req
.emplace_attr(RGW_ATTR_ETAG
, std::move(etag
));
906 req
.emplace_attr(RGW_ATTR_ACL
, std::move(acls
));
908 rc
= rgwlib
.get_fe()->execute_req(&req
);
912 /* special case: materialize placeholder dir */
914 RGWPutObjRequest
req(get_context(), &ruser
, rgw_fh
->bucket_name(),
917 rgw_fh
->encode_attrs(ux_key
, ux_attrs
); /* because std::moved */
920 req
.emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
921 req
.emplace_attr(RGW_ATTR_UNIX1
, std::move(ux_attrs
));
923 rc
= rgwlib
.get_fe()->execute_req(&req
);
927 if ((rc
!= 0) || (rc2
!= 0)) {
931 rgw_fh
->set_ctime(real_clock::to_timespec(real_clock::now()));
934 } /* RGWLibFS::setattr */
936 /* called under rgw_fh->mtx held */
937 void RGWLibFS::update_fh(RGWFileHandle
*rgw_fh
)
940 string obj_name
{rgw_fh
->relative_object_name()};
941 buffer::list ux_key
, ux_attrs
;
943 if (rgw_fh
->is_dir() &&
944 (likely(! rgw_fh
->is_bucket()))) {
948 lsubdout(get_context(), rgw
, 17)
950 << " update old versioned fh : " << obj_name
953 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), user
);
954 RGWSetAttrsRequest
req(cct
, &ruser
, rgw_fh
->bucket_name(), obj_name
);
956 rgw_fh
->encode_attrs(ux_key
, ux_attrs
);
958 req
.emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
959 req
.emplace_attr(RGW_ATTR_UNIX1
, std::move(ux_attrs
));
961 rc
= rgwlib
.get_fe()->execute_req(&req
);
964 if ((rc
!= 0) || (rc2
!= 0)) {
965 lsubdout(get_context(), rgw
, 17)
967 << " update fh failed : " << obj_name
970 } /* RGWLibFS::update_fh */
972 void RGWLibFS::close()
974 state
.flags
|= FLAG_CLOSED
;
980 explicit ObjUnref(RGWLibFS
* _fs
) : fs(_fs
) {}
981 void operator()(RGWFileHandle
* fh
) const {
982 lsubdout(fs
->get_context(), rgw
, 5)
985 << " before ObjUnref refs=" << fh
->get_refcnt()
991 /* force cache drain, forces objects to evict */
992 fh_cache
.drain(ObjUnref(this),
993 RGWFileHandle::FHCache::FLAG_LOCK
);
994 rgwlib
.get_fe()->get_process()->unregister_fs(this);
996 } /* RGWLibFS::close */
998 inline std::ostream
& operator<<(std::ostream
&os
, fh_key
const &fhk
) {
999 os
<< "<fh_key: bucket=";
1000 os
<< fhk
.fh_hk
.bucket
;
1002 os
<< fhk
.fh_hk
.object
;
1007 inline std::ostream
& operator<<(std::ostream
&os
, struct timespec
const &ts
) {
1008 os
<< "<timespec: tv_sec=";
1016 std::ostream
& operator<<(std::ostream
&os
, RGWLibFS::event
const &ev
) {
1019 case RGWLibFS::event::type::READDIR
:
1020 os
<< "type=READDIR;";
1023 os
<< "type=UNKNOWN;";
1026 os
<< "fid=" << ev
.fhk
.fh_hk
.bucket
<< ":" << ev
.fhk
.fh_hk
.object
1027 << ";ts=" << ev
.ts
<< ">";
1034 using directory
= RGWFileHandle::directory
;
1036 /* dirent invalidate timeout--basically, the upper-bound on
1037 * inconsistency with the S3 namespace */
1039 = get_context()->_conf
->rgw_nfs_namespace_expire_secs
;
1041 /* max events to gc in one cycle */
1042 uint32_t max_ev
= get_context()->_conf
->rgw_nfs_max_gc
;
1044 struct timespec now
, expire_ts
;
1047 std::deque
<event
> &events
= state
.events
;
1050 (void) clock_gettime(CLOCK_MONOTONIC_COARSE
, &now
);
1051 lsubdout(get_context(), rgw
, 15)
1052 << "GC: top of expire loop"
1054 << " expire_s=" << expire_s
1057 lock_guard
guard(state
.mtx
); /* LOCKED */
1058 lsubdout(get_context(), rgw
, 15)
1060 << " count=" << events
.size()
1063 /* just return if no events */
1064 if (events
.empty()) {
1068 (events
.size() < 500) ? max_ev
: (events
.size() / 4);
1069 for (uint32_t ix
= 0; (ix
< _max_ev
) && (events
.size() > 0); ++ix
) {
1070 event
& ev
= events
.front();
1072 expire_ts
.tv_sec
+= expire_s
;
1073 if (expire_ts
> now
) {
1082 for (auto& ev
: ve
) {
1083 lsubdout(get_context(), rgw
, 15)
1084 << "try-expire ev: " << ev
<< dendl
;
1085 if (likely(ev
.t
== event::type::READDIR
)) {
1086 RGWFileHandle
* rgw_fh
= lookup_handle(ev
.fhk
.fh_hk
);
1087 lsubdout(get_context(), rgw
, 15)
1088 << "ev rgw_fh: " << rgw_fh
<< dendl
;
1090 RGWFileHandle::directory
* d
;
1091 if (unlikely(! rgw_fh
->is_dir())) {
1092 lsubdout(get_context(), rgw
, 0)
1094 << " BUG non-directory found with READDIR event "
1095 << "(" << rgw_fh
->bucket_name() << ","
1096 << rgw_fh
->object_name() << ")"
1100 /* maybe clear state */
1101 d
= get
<directory
>(&rgw_fh
->variant_type
);
1103 struct timespec ev_ts
= ev
.ts
;
1104 lock_guard
guard(rgw_fh
->mtx
);
1105 struct timespec d_last_readdir
= d
->last_readdir
;
1106 if (unlikely(ev_ts
< d_last_readdir
)) {
1107 /* readdir cycle in progress, don't invalidate */
1108 lsubdout(get_context(), rgw
, 15)
1109 << "GC: delay expiration for "
1110 << rgw_fh
->object_name()
1111 << " ev.ts=" << ev_ts
1112 << " last_readdir=" << d_last_readdir
1116 lsubdout(get_context(), rgw
, 15)
1118 << rgw_fh
->object_name()
1120 rgw_fh
->clear_state();
1121 rgw_fh
->invalidate();
1127 } /* event::type::READDIR */
1130 } while (! (stop
|| shutdown
));
1131 } /* RGWLibFS::gc */
1133 std::ostream
& operator<<(std::ostream
&os
,
1134 RGWFileHandle
const &rgw_fh
)
1136 const auto& fhk
= rgw_fh
.get_key();
1137 const auto& fh
= const_cast<RGWFileHandle
&>(rgw_fh
).get_fh();
1138 os
<< "<RGWFileHandle:";
1139 os
<< "addr=" << &rgw_fh
<< ";";
1140 switch (fh
->fh_type
) {
1141 case RGW_FS_TYPE_DIRECTORY
:
1142 os
<< "type=DIRECTORY;";
1144 case RGW_FS_TYPE_FILE
:
1148 os
<< "type=UNKNOWN;";
1151 os
<< "fid=" << fhk
.fh_hk
.bucket
<< ":" << fhk
.fh_hk
.object
<< ";";
1152 os
<< "name=" << rgw_fh
.object_name() << ";";
1153 os
<< "refcnt=" << rgw_fh
.get_refcnt() << ";";
1158 RGWFileHandle::~RGWFileHandle() {
1159 /* !recycle case, handle may STILL be in handle table, BUT
1160 * the partition lock is not held in this path */
1161 if (fh_hook
.is_linked()) {
1162 fs
->fh_cache
.remove(fh
.fh_hk
.object
, this, FHCache::FLAG_LOCK
);
1164 /* cond-unref parent */
1165 if (parent
&& (! parent
->is_mount())) {
1166 /* safe because if parent->unref causes its deletion,
1167 * there are a) by refcnt, no other objects/paths pointing
1168 * to it and b) by the semantics of valid iteration of
1169 * fh_lru (observed, e.g., by cohort_lru<T,...>::drain())
1170 * no unsafe iterators reaching it either--n.b., this constraint
1171 * is binding oncode which may in future attempt to e.g.,
1172 * cause the eviction of objects in LRU order */
1173 (void) get_fs()->unref(parent
);
1177 fh_key
RGWFileHandle::make_fhk(const std::string
& name
)
1179 std::string tenant
= get_fs()->get_user()->user_id
.to_str();
1181 /* S3 bucket -- assert mount-at-bucket case reaches here */
1182 return fh_key(name
, name
, tenant
);
1184 std::string key_name
= make_key_name(name
.c_str());
1185 return fh_key(fhk
.fh_hk
.bucket
, key_name
.c_str(), tenant
);
1189 void RGWFileHandle::encode_attrs(ceph::buffer::list
& ux_key1
,
1190 ceph::buffer::list
& ux_attrs1
)
1193 fh_key
fhk(this->fh
.fh_hk
);
1194 encode(fhk
, ux_key1
);
1195 encode(*this, ux_attrs1
);
1196 } /* RGWFileHandle::encode_attrs */
1198 DecodeAttrsResult
RGWFileHandle::decode_attrs(const ceph::buffer::list
* ux_key1
,
1199 const ceph::buffer::list
* ux_attrs1
)
1202 DecodeAttrsResult dar
{ false, false };
1204 auto bl_iter_key1
= ux_key1
->cbegin();
1205 decode(fhk
, bl_iter_key1
);
1208 auto bl_iter_unix1
= ux_attrs1
->cbegin();
1209 decode(*this, bl_iter_unix1
);
1210 if (this->state
.version
< 2) {
1215 } /* RGWFileHandle::decode_attrs */
1217 bool RGWFileHandle::reclaim(const cohort::lru::ObjectFactory
* newobj_fac
) {
1218 lsubdout(fs
->get_context(), rgw
, 17)
1219 << __func__
<< " " << *this
1221 auto factory
= dynamic_cast<const RGWFileHandle::Factory
*>(newobj_fac
);
1222 if (factory
== nullptr) {
1225 /* make sure the reclaiming object is the same partiton with newobject factory,
1226 * then we can recycle the object, and replace with newobject */
1227 if (!fs
->fh_cache
.is_same_partition(factory
->fhk
.fh_hk
.object
, fh
.fh_hk
.object
)) {
1230 /* in the non-delete case, handle may still be in handle table */
1231 if (fh_hook
.is_linked()) {
1232 /* in this case, we are being called from a context which holds
1233 * the partition lock */
1234 fs
->fh_cache
.remove(fh
.fh_hk
.object
, this, FHCache::FLAG_NONE
);
1237 } /* RGWFileHandle::reclaim */
1239 bool RGWFileHandle::has_children() const
1241 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), *fs
->get_user());
1242 if (unlikely(! is_dir()))
1245 RGWRMdirCheck
req(fs
->get_context(), &ruser
, this);
1246 int rc
= rgwlib
.get_fe()->execute_req(&req
);
1248 return req
.valid
&& req
.has_children
;
1254 std::ostream
& operator<<(std::ostream
&os
,
1255 RGWFileHandle::readdir_offset
const &offset
)
1258 if (unlikely(!! get
<uint64_t*>(&offset
))) {
1259 uint64_t* ioff
= get
<uint64_t*>(offset
);
1263 os
<< get
<const char*>(offset
);
1267 int RGWFileHandle::readdir(rgw_readdir_cb rcb
, void *cb_arg
,
1268 readdir_offset offset
,
1269 bool *eof
, uint32_t flags
)
1271 using event
= RGWLibFS::event
;
1274 struct timespec now
;
1275 CephContext
* cct
= fs
->get_context();
1276 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), *fs
->get_user());
1278 lsubdout(cct
, rgw
, 10)
1279 << __func__
<< " readdir called on "
1283 directory
* d
= get
<directory
>(&variant_type
);
1285 (void) clock_gettime(CLOCK_MONOTONIC_COARSE
, &now
); /* !LOCKED */
1286 lock_guard
guard(mtx
);
1287 d
->last_readdir
= now
;
1293 if (likely(!! get
<const char*>(&offset
))) {
1294 mk
= const_cast<char*>(get
<const char*>(offset
));
1297 initial_off
= (*get
<uint64_t*>(offset
) == 0);
1301 RGWListBucketsRequest
req(cct
, &ruser
, this, rcb
, cb_arg
,
1303 rc
= rgwlib
.get_fe()->execute_req(&req
);
1305 (void) clock_gettime(CLOCK_MONOTONIC_COARSE
, &now
); /* !LOCKED */
1306 lock_guard
guard(mtx
);
1310 inc_nlink(req
.d_count
);
1314 RGWReaddirRequest
req(cct
, &ruser
, this, rcb
, cb_arg
, offset
);
1315 rc
= rgwlib
.get_fe()->execute_req(&req
);
1317 (void) clock_gettime(CLOCK_MONOTONIC_COARSE
, &now
); /* !LOCKED */
1318 lock_guard
guard(mtx
);
1322 inc_nlink(req
.d_count
);
1327 event
ev(event::type::READDIR
, get_key(), state
.atime
);
1328 lock_guard
sguard(fs
->state
.mtx
);
1329 fs
->state
.push_event(ev
);
1331 lsubdout(fs
->get_context(), rgw
, 15)
1333 << " final link count=" << state
.nlink
1337 } /* RGWFileHandle::readdir */
1339 int RGWFileHandle::write(uint64_t off
, size_t len
, size_t *bytes_written
,
1343 using WriteCompletion
= RGWLibFS::WriteCompletion
;
1344 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), *fs
->get_user());
1346 lock_guard
guard(mtx
);
1350 file
* f
= get
<file
>(&variant_type
);
1355 lsubdout(fs
->get_context(), rgw
, 5)
1357 << " write attempted on deleted object "
1358 << this->object_name()
1360 /* zap write transaction, if any */
1362 delete f
->write_req
;
1363 f
->write_req
= nullptr;
1368 if (! f
->write_req
) {
1369 /* guard--we do not support (e.g., COW-backed) partial writes */
1371 lsubdout(fs
->get_context(), rgw
, 5)
1373 << " " << object_name()
1374 << " non-0 initial write position " << off
1375 << " (mounting with -o sync required)"
1381 std::string object_name
= relative_object_name();
1383 new RGWWriteRequest(fs
->get_context(), &ruser
, this,
1384 bucket_name(), object_name
);
1385 rc
= rgwlib
.get_fe()->start_req(f
->write_req
);
1387 lsubdout(fs
->get_context(), rgw
, 5)
1389 << this->object_name()
1390 << " write start failed " << off
1391 << " (" << rc
<< ")"
1393 /* zap failed write transaction */
1394 delete f
->write_req
;
1395 f
->write_req
= nullptr;
1398 if (stateless_open()) {
1399 /* start write timer */
1400 f
->write_req
->timer_id
=
1401 RGWLibFS::write_timer
.add_event(
1402 std::chrono::seconds(RGWLibFS::write_completion_interval_s
),
1403 WriteCompletion(*this));
1409 if ((static_cast<off_t
>(off
) < f
->write_req
->real_ofs
) &&
1410 ((f
->write_req
->real_ofs
- off
) <= len
)) {
1411 overlap
= f
->write_req
->real_ofs
- off
;
1412 off
= f
->write_req
->real_ofs
;
1413 buffer
= static_cast<char*>(buffer
) + overlap
;
1421 buffer::create_static(len
, static_cast<char*>(buffer
)));
1424 buffer::copy(static_cast<char*>(buffer
), len
));
1427 f
->write_req
->put_data(off
, bl
);
1428 rc
= f
->write_req
->exec_continue();
1431 size_t min_size
= off
+ len
;
1432 if (min_size
> get_size())
1434 if (stateless_open()) {
1435 /* bump write timer */
1436 RGWLibFS::write_timer
.adjust_event(
1437 f
->write_req
->timer_id
, std::chrono::seconds(10));
1440 /* continuation failed (e.g., non-contiguous write position) */
1441 lsubdout(fs
->get_context(), rgw
, 5)
1444 << " failed write at position " << off
1445 << " (fails write transaction) "
1447 /* zap failed write transaction */
1448 delete f
->write_req
;
1449 f
->write_req
= nullptr;
1453 *bytes_written
= (rc
== 0) ? (len
+ overlap
) : 0;
1455 } /* RGWFileHandle::write */
1457 int RGWFileHandle::write_finish(uint32_t flags
)
1459 unique_lock guard
{mtx
, std::defer_lock
};
1462 if (! (flags
& FLAG_LOCKED
)) {
1466 file
* f
= get
<file
>(&variant_type
);
1467 if (f
&& (f
->write_req
)) {
1468 lsubdout(fs
->get_context(), rgw
, 10)
1470 << " finishing write trans on " << object_name()
1472 rc
= rgwlib
.get_fe()->finish_req(f
->write_req
);
1474 rc
= f
->write_req
->get_ret();
1476 delete f
->write_req
;
1477 f
->write_req
= nullptr;
1481 } /* RGWFileHandle::write_finish */
1483 int RGWFileHandle::close()
1485 lock_guard
guard(mtx
);
1487 int rc
= write_finish(FLAG_LOCKED
);
1489 flags
&= ~FLAG_OPEN
;
1490 flags
&= ~FLAG_STATELESS_OPEN
;
1493 } /* RGWFileHandle::close */
1495 RGWFileHandle::file::~file()
1500 void RGWFileHandle::clear_state()
1502 directory
* d
= get
<directory
>(&variant_type
);
1505 d
->last_marker
= rgw_obj_key
{};
1509 void RGWFileHandle::advance_mtime(uint32_t flags
) {
1510 /* intended for use on directories, fast-forward mtime so as to
1511 * ensure a new, higher value for the change attribute */
1512 unique_lock
uniq(mtx
, std::defer_lock
);
1513 if (likely(! (flags
& RGWFileHandle::FLAG_LOCKED
))) {
1517 /* advance mtime only if stored mtime is older than the
1518 * configured namespace expiration */
1519 auto now
= real_clock::now();
1520 auto cmptime
= state
.mtime
;
1522 fs
->get_context()->_conf
->rgw_nfs_namespace_expire_secs
;
1523 if (cmptime
< real_clock::to_timespec(now
)) {
1524 /* sets ctime as well as mtime, to avoid masking updates should
1525 * ctime inexplicably hold a higher value */
1530 void RGWFileHandle::invalidate() {
1531 RGWLibFS
*fs
= get_fs();
1532 if (fs
->invalidate_cb
) {
1533 fs
->invalidate_cb(fs
->invalidate_arg
, get_key().fh_hk
);
1537 int RGWWriteRequest::exec_start() {
1538 struct req_state
* s
= get_state();
1540 auto compression_type
=
1541 get_store()->svc()->zone
->get_zone_params().get_compression_type(
1542 s
->bucket_info
.placement_rule
);
1544 /* not obviously supportable */
1545 ceph_assert(! dlo_manifest
);
1546 ceph_assert(! slo_info
);
1548 perfcounter
->inc(l_rgw_put
);
1550 rgw_obj obj
{s
->bucket
, s
->object
};
1552 if (s
->object
.empty()) {
1553 ldout(s
->cct
, 0) << __func__
<< " called on empty object" << dendl
;
1557 op_ret
= get_params();
1561 op_ret
= get_system_versioning_params(s
, &olh_epoch
, &version_id
);
1566 /* user-supplied MD5 check skipped (not supplied) */
1567 /* early quota check skipped--we don't have size yet */
1568 /* skipping user-supplied etag--we might have one in future, but
1569 * like data it and other attrs would arrive after open */
1571 aio
.emplace(s
->cct
->_conf
->rgw_put_obj_min_window_size
);
1573 if (s
->bucket_info
.versioning_enabled()) {
1574 if (!version_id
.empty()) {
1575 obj
.key
.set_instance(version_id
);
1577 get_store()->getRados()->gen_rand_obj_instance_name(&obj
);
1578 version_id
= obj
.key
.instance
;
1581 processor
.emplace(&*aio
, get_store(), s
->bucket_info
,
1583 s
->bucket_owner
.get_id(),
1584 *static_cast<RGWObjectCtx
*>(s
->obj_ctx
),
1585 obj
, olh_epoch
, s
->req_id
, this, s
->yield
);
1587 op_ret
= processor
->prepare(s
->yield
);
1589 ldout(s
->cct
, 20) << "processor->prepare() returned ret=" << op_ret
1593 filter
= &*processor
;
1594 if (compression_type
!= "none") {
1595 plugin
= Compressor::create(s
->cct
, compression_type
);
1597 ldout(s
->cct
, 1) << "Cannot load plugin for rgw_compression_type "
1598 << compression_type
<< dendl
;
1600 compressor
.emplace(s
->cct
, plugin
, filter
);
1601 filter
= &*compressor
;
1609 int RGWWriteRequest::exec_continue()
1611 struct req_state
* s
= get_state();
1614 /* check guards (e.g., contig write) */
1617 << " chunks arrived in wrong order"
1618 << " (mounting with -o sync required)"
1623 op_ret
= get_store()->getRados()->check_quota(s
->bucket_owner
.get_id(), s
->bucket
,
1624 user_quota
, bucket_quota
, real_ofs
, true);
1625 /* max_size exceed */
1629 size_t len
= data
.length();
1633 hash
.Update((const unsigned char *)data
.c_str(), data
.length());
1634 op_ret
= filter
->process(std::move(data
), ofs
);
1638 bytes_written
+= len
;
1642 } /* exec_continue */
1644 int RGWWriteRequest::exec_finish()
1646 buffer::list bl
, aclbl
, ux_key
, ux_attrs
;
1647 map
<string
, string
>::iterator iter
;
1648 char calc_md5
[CEPH_CRYPTO_MD5_DIGESTSIZE
* 2 + 1];
1649 unsigned char m
[CEPH_CRYPTO_MD5_DIGESTSIZE
];
1650 struct req_state
* s
= get_state();
1652 size_t osize
= rgw_fh
->get_size();
1653 struct timespec octime
= rgw_fh
->get_ctime();
1654 struct timespec omtime
= rgw_fh
->get_mtime();
1655 real_time appx_t
= real_clock::now();
1657 s
->obj_size
= bytes_written
;
1658 perfcounter
->inc(l_rgw_put_b
, s
->obj_size
);
1660 // flush data in filters
1661 op_ret
= filter
->process({}, s
->obj_size
);
1666 op_ret
= get_store()->getRados()->check_quota(s
->bucket_owner
.get_id(), s
->bucket
,
1667 user_quota
, bucket_quota
, s
->obj_size
, true);
1668 /* max_size exceed */
1675 if (compressor
&& compressor
->is_compressed()) {
1677 RGWCompressionInfo cs_info
;
1678 cs_info
.compression_type
= plugin
->get_type_name();
1679 cs_info
.orig_size
= s
->obj_size
;
1680 cs_info
.blocks
= std::move(compressor
->get_compression_blocks());
1681 encode(cs_info
, tmp
);
1682 attrs
[RGW_ATTR_COMPRESSION
] = tmp
;
1683 ldout(s
->cct
, 20) << "storing " << RGW_ATTR_COMPRESSION
1684 << " with type=" << cs_info
.compression_type
1685 << ", orig_size=" << cs_info
.orig_size
1686 << ", blocks=" << cs_info
.blocks
.size() << dendl
;
1689 buf_to_hex(m
, CEPH_CRYPTO_MD5_DIGESTSIZE
, calc_md5
);
1692 bl
.append(etag
.c_str(), etag
.size() + 1);
1693 emplace_attr(RGW_ATTR_ETAG
, std::move(bl
));
1695 policy
.encode(aclbl
);
1696 emplace_attr(RGW_ATTR_ACL
, std::move(aclbl
));
1699 rgw_fh
->set_mtime(real_clock::to_timespec(appx_t
));
1700 rgw_fh
->set_ctime(real_clock::to_timespec(appx_t
));
1701 rgw_fh
->set_size(bytes_written
);
1702 rgw_fh
->encode_attrs(ux_key
, ux_attrs
);
1704 emplace_attr(RGW_ATTR_UNIX_KEY1
, std::move(ux_key
));
1705 emplace_attr(RGW_ATTR_UNIX1
, std::move(ux_attrs
));
1707 for (iter
= s
->generic_attrs
.begin(); iter
!= s
->generic_attrs
.end();
1709 buffer::list
& attrbl
= attrs
[iter
->first
];
1710 const string
& val
= iter
->second
;
1711 attrbl
.append(val
.c_str(), val
.size() + 1);
1714 op_ret
= rgw_get_request_metadata(s
->cct
, s
->info
, attrs
);
1718 encode_delete_at_attr(delete_at
, attrs
);
1720 /* Add a custom metadata to expose the information whether an object
1721 * is an SLO or not. Appending the attribute must be performed AFTER
1722 * processing any input from user in order to prohibit overwriting. */
1723 if (unlikely(!! slo_info
)) {
1724 buffer::list slo_userindicator_bl
;
1726 encode("True", slo_userindicator_bl
);
1727 emplace_attr(RGW_ATTR_SLO_UINDICATOR
, std::move(slo_userindicator_bl
));
1730 op_ret
= processor
->complete(s
->obj_size
, etag
, &mtime
, real_time(), attrs
,
1731 (delete_at
? *delete_at
: real_time()),
1732 if_match
, if_nomatch
, nullptr, nullptr, nullptr,
1735 /* revert attr updates */
1736 rgw_fh
->set_mtime(omtime
);
1737 rgw_fh
->set_ctime(octime
);
1738 rgw_fh
->set_size(osize
);
1742 perfcounter
->tinc(l_rgw_put_lat
, s
->time_elapsed());
1746 } /* namespace rgw */
1751 void rgwfile_version(int *major
, int *minor
, int *extra
)
1754 *major
= LIBRGW_FILE_VER_MAJOR
;
1756 *minor
= LIBRGW_FILE_VER_MINOR
;
1758 *extra
= LIBRGW_FILE_VER_EXTRA
;
1762 attach rgw namespace
1764 int rgw_mount(librgw_t rgw
, const char *uid
, const char *acc_key
,
1765 const char *sec_key
, struct rgw_fs
**rgw_fs
,
1770 /* stash access data for "mount" */
1771 RGWLibFS
* new_fs
= new RGWLibFS(static_cast<CephContext
*>(rgw
), uid
, acc_key
,
1773 ceph_assert(new_fs
);
1775 rc
= new_fs
->authorize(rgwlib
.get_store());
1781 /* register fs for shared gc */
1782 rgwlib
.get_fe()->get_process()->register_fs(new_fs
);
1784 struct rgw_fs
*fs
= new_fs
->get_fs();
1787 /* XXX we no longer assume "/" is unique, but we aren't tracking the
1795 int rgw_mount2(librgw_t rgw
, const char *uid
, const char *acc_key
,
1796 const char *sec_key
, const char *root
, struct rgw_fs
**rgw_fs
,
1801 /* stash access data for "mount" */
1802 RGWLibFS
* new_fs
= new RGWLibFS(static_cast<CephContext
*>(rgw
), uid
, acc_key
,
1804 ceph_assert(new_fs
);
1806 rc
= new_fs
->authorize(rgwlib
.get_store());
1812 /* register fs for shared gc */
1813 rgwlib
.get_fe()->get_process()->register_fs(new_fs
);
1815 struct rgw_fs
*fs
= new_fs
->get_fs();
1818 /* XXX we no longer assume "/" is unique, but we aren't tracking the
1827 register invalidate callbacks
1829 int rgw_register_invalidate(struct rgw_fs
*rgw_fs
, rgw_fh_callback_t cb
,
1830 void *arg
, uint32_t flags
)
1833 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
1834 return fs
->register_invalidate(cb
, arg
, flags
);
1838 detach rgw namespace
1840 int rgw_umount(struct rgw_fs
*rgw_fs
, uint32_t flags
)
1842 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
1848 get filesystem attributes
1850 int rgw_statfs(struct rgw_fs
*rgw_fs
,
1851 struct rgw_file_handle
*parent_fh
,
1852 struct rgw_statvfs
*vfs_st
, uint32_t flags
)
1854 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
1855 struct rados_cluster_stat_t stats
;
1856 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), *fs
->get_user());
1858 RGWGetClusterStatReq
req(fs
->get_context(), &ruser
, stats
);
1859 int rc
= rgwlib
.get_fe()->execute_req(&req
);
1861 lderr(fs
->get_context()) << "ERROR: getting total cluster usage"
1862 << cpp_strerror(-rc
) << dendl
;
1866 //Set block size to 1M.
1867 constexpr uint32_t CEPH_BLOCK_SHIFT
= 20;
1868 vfs_st
->f_bsize
= 1 << CEPH_BLOCK_SHIFT
;
1869 vfs_st
->f_frsize
= 1 << CEPH_BLOCK_SHIFT
;
1870 vfs_st
->f_blocks
= stats
.kb
>> (CEPH_BLOCK_SHIFT
- 10);
1871 vfs_st
->f_bfree
= stats
.kb_avail
>> (CEPH_BLOCK_SHIFT
- 10);
1872 vfs_st
->f_bavail
= stats
.kb_avail
>> (CEPH_BLOCK_SHIFT
- 10);
1873 vfs_st
->f_files
= stats
.num_objects
;
1874 vfs_st
->f_ffree
= -1;
1875 vfs_st
->f_fsid
[0] = fs
->get_fsid();
1876 vfs_st
->f_fsid
[1] = fs
->get_fsid();
1878 vfs_st
->f_namemax
= 4096;
1883 generic create -- create an empty regular file
1885 int rgw_create(struct rgw_fs
*rgw_fs
, struct rgw_file_handle
*parent_fh
,
1886 const char *name
, struct stat
*st
, uint32_t mask
,
1887 struct rgw_file_handle
**fh
, uint32_t posix_flags
,
1892 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
1893 RGWFileHandle
* parent
= get_rgwfh(parent_fh
);
1896 (parent
->is_root()) ||
1897 (parent
->is_file())) {
1902 MkObjResult fhr
= fs
->create(parent
, name
, st
, mask
, flags
);
1903 RGWFileHandle
*nfh
= get
<0>(fhr
); // nullptr if !success
1906 *fh
= nfh
->get_fh();
1912 create a symbolic link
1914 int rgw_symlink(struct rgw_fs
*rgw_fs
, struct rgw_file_handle
*parent_fh
,
1915 const char *name
, const char *link_path
, struct stat
*st
, uint32_t mask
,
1916 struct rgw_file_handle
**fh
, uint32_t posix_flags
,
1921 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
1922 RGWFileHandle
* parent
= get_rgwfh(parent_fh
);
1925 (parent
->is_root()) ||
1926 (parent
->is_file())) {
1931 MkObjResult fhr
= fs
->symlink(parent
, name
, link_path
, st
, mask
, flags
);
1932 RGWFileHandle
*nfh
= get
<0>(fhr
); // nullptr if !success
1935 *fh
= nfh
->get_fh();
1941 create a new directory
1943 int rgw_mkdir(struct rgw_fs
*rgw_fs
,
1944 struct rgw_file_handle
*parent_fh
,
1945 const char *name
, struct stat
*st
, uint32_t mask
,
1946 struct rgw_file_handle
**fh
, uint32_t flags
)
1950 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
1951 RGWFileHandle
* parent
= get_rgwfh(parent_fh
);
1958 MkObjResult fhr
= fs
->mkdir(parent
, name
, st
, mask
, flags
);
1959 RGWFileHandle
*nfh
= get
<0>(fhr
); // nullptr if !success
1962 *fh
= nfh
->get_fh();
1970 int rgw_rename(struct rgw_fs
*rgw_fs
,
1971 struct rgw_file_handle
*src
, const char* src_name
,
1972 struct rgw_file_handle
*dst
, const char* dst_name
,
1975 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
1977 RGWFileHandle
* src_fh
= get_rgwfh(src
);
1978 RGWFileHandle
* dst_fh
= get_rgwfh(dst
);
1980 return fs
->rename(src_fh
, dst_fh
, src_name
, dst_name
);
1984 remove file or directory
1986 int rgw_unlink(struct rgw_fs
*rgw_fs
, struct rgw_file_handle
*parent_fh
,
1987 const char *name
, uint32_t flags
)
1989 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
1990 RGWFileHandle
* parent
= get_rgwfh(parent_fh
);
1992 return fs
->unlink(parent
, name
);
1996 lookup object by name (POSIX style)
1998 int rgw_lookup(struct rgw_fs
*rgw_fs
,
1999 struct rgw_file_handle
*parent_fh
, const char* path
,
2000 struct rgw_file_handle
**fh
,
2001 struct stat
*st
, uint32_t mask
, uint32_t flags
)
2003 //CephContext* cct = static_cast<CephContext*>(rgw_fs->rgw);
2004 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2006 RGWFileHandle
* parent
= get_rgwfh(parent_fh
);
2008 (! parent
->is_dir())) {
2013 RGWFileHandle
* rgw_fh
;
2016 if (parent
->is_root()) {
2017 /* special: parent lookup--note lack of ref()! */
2018 if (unlikely((strcmp(path
, "..") == 0) ||
2019 (strcmp(path
, "/") == 0))) {
2022 RGWLibFS::BucketStats bstat
;
2023 fhr
= fs
->stat_bucket(parent
, path
, bstat
, RGWFileHandle::FLAG_NONE
);
2024 rgw_fh
= get
<0>(fhr
);
2029 /* special: after readdir--note extra ref()! */
2030 if (unlikely((strcmp(path
, "..") == 0))) {
2032 lsubdout(fs
->get_context(), rgw
, 17)
2033 << __func__
<< " BANG"<< *rgw_fh
2037 enum rgw_fh_type fh_type
= fh_type_of(flags
);
2039 uint32_t sl_flags
= (flags
& RGW_LOOKUP_FLAG_RCB
)
2040 ? RGWFileHandle::FLAG_NONE
2041 : RGWFileHandle::FLAG_EXACT_MATCH
;
2043 bool fast_attrs
= fs
->get_context()->_conf
->rgw_nfs_s3_fast_attrs
;
2045 if ((flags
& RGW_LOOKUP_FLAG_RCB
) && fast_attrs
) {
2046 /* FAKE STAT--this should mean, interpolate special
2047 * owner, group, and perms masks */
2048 fhr
= fs
->fake_leaf(parent
, path
, fh_type
, st
, mask
, sl_flags
);
2050 if ((fh_type
== RGW_FS_TYPE_DIRECTORY
) && fast_attrs
) {
2051 /* trust cached dir, if present */
2052 fhr
= fs
->lookup_fh(parent
, path
, RGWFileHandle::FLAG_DIRECTORY
);
2054 rgw_fh
= get
<0>(fhr
);
2058 fhr
= fs
->stat_leaf(parent
, path
, fh_type
, sl_flags
);
2060 if (! get
<0>(fhr
)) {
2061 if (! (flags
& RGW_LOOKUP_FLAG_CREATE
))
2064 fhr
= fs
->lookup_fh(parent
, path
, RGWFileHandle::FLAG_CREATE
);
2066 rgw_fh
= get
<0>(fhr
);
2071 struct rgw_file_handle
*rfh
= rgw_fh
->get_fh();
2078 lookup object by handle (NFS style)
2080 int rgw_lookup_handle(struct rgw_fs
*rgw_fs
, struct rgw_fh_hk
*fh_hk
,
2081 struct rgw_file_handle
**fh
, uint32_t flags
)
2083 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2085 RGWFileHandle
* rgw_fh
= fs
->lookup_handle(*fh_hk
);
2091 struct rgw_file_handle
*rfh
= rgw_fh
->get_fh();
2098 * release file handle
2100 int rgw_fh_rele(struct rgw_fs
*rgw_fs
, struct rgw_file_handle
*fh
,
2103 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2104 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2106 lsubdout(fs
->get_context(), rgw
, 17)
2107 << __func__
<< " " << *rgw_fh
2115 get unix attributes for object
2117 int rgw_getattr(struct rgw_fs
*rgw_fs
,
2118 struct rgw_file_handle
*fh
, struct stat
*st
, uint32_t flags
)
2120 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2121 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2123 return fs
->getattr(rgw_fh
, st
);
2127 set unix attributes for object
2129 int rgw_setattr(struct rgw_fs
*rgw_fs
,
2130 struct rgw_file_handle
*fh
, struct stat
*st
,
2131 uint32_t mask
, uint32_t flags
)
2133 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2134 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2136 return fs
->setattr(rgw_fh
, st
, mask
, flags
);
2142 int rgw_truncate(struct rgw_fs
*rgw_fs
,
2143 struct rgw_file_handle
*fh
, uint64_t size
, uint32_t flags
)
2151 int rgw_open(struct rgw_fs
*rgw_fs
,
2152 struct rgw_file_handle
*fh
, uint32_t posix_flags
, uint32_t flags
)
2154 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2157 * need to track specific opens--at least read opens and
2158 * a write open; we need to know when a write open is returned,
2159 * that closes a write transaction
2161 * for now, we will support single-open only, it's preferable to
2162 * anything we can otherwise do without access to the NFS state
2164 if (! rgw_fh
->is_file())
2167 return rgw_fh
->open(flags
);
2173 int rgw_close(struct rgw_fs
*rgw_fs
,
2174 struct rgw_file_handle
*fh
, uint32_t flags
)
2176 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2177 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2178 int rc
= rgw_fh
->close(/* XXX */);
2180 if (flags
& RGW_CLOSE_FLAG_RELE
)
2186 int rgw_readdir(struct rgw_fs
*rgw_fs
,
2187 struct rgw_file_handle
*parent_fh
, uint64_t *offset
,
2188 rgw_readdir_cb rcb
, void *cb_arg
, bool *eof
,
2191 RGWFileHandle
* parent
= get_rgwfh(parent_fh
);
2197 lsubdout(parent
->get_fs()->get_context(), rgw
, 15)
2199 << " offset=" << *offset
2202 if ((*offset
== 0) &&
2203 (flags
& RGW_READDIR_FLAG_DOTDOT
)) {
2204 /* send '.' and '..' with their NFS-defined offsets */
2205 rcb(".", cb_arg
, 1, nullptr, 0, RGW_LOOKUP_FLAG_DIR
);
2206 rcb("..", cb_arg
, 2, nullptr, 0, RGW_LOOKUP_FLAG_DIR
);
2209 int rc
= parent
->readdir(rcb
, cb_arg
, offset
, eof
, flags
);
2213 /* enumeration continuing from name */
2214 int rgw_readdir2(struct rgw_fs
*rgw_fs
,
2215 struct rgw_file_handle
*parent_fh
, const char *name
,
2216 rgw_readdir_cb rcb
, void *cb_arg
, bool *eof
,
2219 RGWFileHandle
* parent
= get_rgwfh(parent_fh
);
2225 lsubdout(parent
->get_fs()->get_context(), rgw
, 15)
2227 << " offset=" << ((name
) ? name
: "(nil)")
2231 (flags
& RGW_READDIR_FLAG_DOTDOT
)) {
2232 /* send '.' and '..' with their NFS-defined offsets */
2233 rcb(".", cb_arg
, 1, nullptr, 0, RGW_LOOKUP_FLAG_DIR
);
2234 rcb("..", cb_arg
, 2, nullptr, 0, RGW_LOOKUP_FLAG_DIR
);
2237 int rc
= parent
->readdir(rcb
, cb_arg
, name
, eof
, flags
);
2239 } /* rgw_readdir2 */
2241 /* project offset of dirent name */
2242 int rgw_dirent_offset(struct rgw_fs
*rgw_fs
,
2243 struct rgw_file_handle
*parent_fh
,
2244 const char *name
, int64_t *offset
,
2247 RGWFileHandle
* parent
= get_rgwfh(parent_fh
);
2252 std::string sname
{name
};
2253 int rc
= parent
->offset_of(sname
, offset
, flags
);
2260 int rgw_read(struct rgw_fs
*rgw_fs
,
2261 struct rgw_file_handle
*fh
, uint64_t offset
,
2262 size_t length
, size_t *bytes_read
, void *buffer
,
2265 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2266 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2268 return fs
->read(rgw_fh
, offset
, length
, bytes_read
, buffer
, flags
);
2274 int rgw_readlink(struct rgw_fs
*rgw_fs
,
2275 struct rgw_file_handle
*fh
, uint64_t offset
,
2276 size_t length
, size_t *bytes_read
, void *buffer
,
2279 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2280 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2282 return fs
->readlink(rgw_fh
, offset
, length
, bytes_read
, buffer
, flags
);
2288 int rgw_write(struct rgw_fs
*rgw_fs
,
2289 struct rgw_file_handle
*fh
, uint64_t offset
,
2290 size_t length
, size_t *bytes_written
, void *buffer
,
2293 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2298 if (! rgw_fh
->is_file())
2301 if (! rgw_fh
->is_open()) {
2302 if (flags
& RGW_OPEN_FLAG_V3
) {
2303 rc
= rgw_fh
->open(flags
);
2310 rc
= rgw_fh
->write(offset
, length
, bytes_written
, buffer
);
2316 read data from file (vector)
2321 struct rgw_vio
* vio
;
2324 RGWReadV(buffer::list
& _bl
, rgw_vio
* _vio
) : vio(_vio
) {
2328 struct rgw_vio
* get_vio() { return vio
; }
2330 const auto& buffers() { return bl
.buffers(); }
2332 unsigned /* XXX */ length() { return bl
.length(); }
2336 void rgw_readv_rele(struct rgw_uio
*uio
, uint32_t flags
)
2338 RGWReadV
* rdv
= static_cast<RGWReadV
*>(uio
->uio_p1
);
2340 ::operator delete(rdv
);
2343 int rgw_readv(struct rgw_fs
*rgw_fs
,
2344 struct rgw_file_handle
*fh
, rgw_uio
*uio
, uint32_t flags
)
2347 CephContext
* cct
= static_cast<CephContext
*>(rgw_fs
->rgw
);
2348 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2349 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2351 if (! rgw_fh
->is_file())
2357 RGWGetObjRequest
req(cct
, fs
->get_user(), rgw_fh
->bucket_name(),
2358 rgw_fh
->object_name(), uio
->uio_offset
, uio
->uio_resid
,
2360 req
.do_hexdump
= false;
2362 rc
= rgwlib
.get_fe()->execute_req(&req
);
2365 RGWReadV
* rdv
= static_cast<RGWReadV
*>(
2366 ::operator new(sizeof(RGWReadV
) +
2367 (bl
.buffers().size() * sizeof(struct rgw_vio
))));
2370 RGWReadV(bl
, reinterpret_cast<rgw_vio
*>(rdv
+sizeof(RGWReadV
)));
2373 uio
->uio_cnt
= rdv
->buffers().size();
2374 uio
->uio_resid
= rdv
->length();
2375 uio
->uio_vio
= rdv
->get_vio();
2376 uio
->uio_rele
= rgw_readv_rele
;
2379 auto& buffers
= rdv
->buffers();
2380 for (auto& bp
: buffers
) {
2381 rgw_vio
*vio
= &(uio
->uio_vio
[ix
]);
2382 vio
->vio_base
= const_cast<char*>(bp
.c_str());
2383 vio
->vio_len
= bp
.length();
2384 vio
->vio_u1
= nullptr;
2385 vio
->vio_p1
= nullptr;
2397 write data to file (vector)
2399 int rgw_writev(struct rgw_fs
*rgw_fs
, struct rgw_file_handle
*fh
,
2400 rgw_uio
*uio
, uint32_t flags
)
2405 CephContext
* cct
= static_cast<CephContext
*>(rgw_fs
->rgw
);
2406 RGWLibFS
*fs
= static_cast<RGWLibFS
*>(rgw_fs
->fs_private
);
2407 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2408 rgw::sal::RGWRadosUser
ruser(rgwlib
.get_store(), *fs
->get_user());
2410 if (! rgw_fh
->is_file())
2414 for (unsigned int ix
= 0; ix
< uio
->uio_cnt
; ++ix
) {
2415 rgw_vio
*vio
= &(uio
->uio_vio
[ix
]);
2417 buffer::create_static(vio
->vio_len
,
2418 static_cast<char*>(vio
->vio_base
)));
2421 std::string oname
= rgw_fh
->relative_object_name();
2422 RGWPutObjRequest
req(cct
, &ruser
, rgw_fh
->bucket_name(),
2425 int rc
= rgwlib
.get_fe()->execute_req(&req
);
2427 /* XXX update size (in request) */
2435 int rgw_fsync(struct rgw_fs
*rgw_fs
, struct rgw_file_handle
*handle
,
2441 int rgw_commit(struct rgw_fs
*rgw_fs
, struct rgw_file_handle
*fh
,
2442 uint64_t offset
, uint64_t length
, uint32_t flags
)
2444 RGWFileHandle
* rgw_fh
= get_rgwfh(fh
);
2446 return rgw_fh
->commit(offset
, length
, RGWFileHandle::FLAG_NONE
);