return 0;
}
-int RGWPutObjProcessor::complete(string& etag, map<string, bufferlist>& attrs)
+int RGWPutObjProcessor::complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs)
{
- int r = do_complete(etag, attrs);
+ int r = do_complete(etag, mtime, attrs);
if (r < 0)
return r;
int prepare(RGWRados *store, struct req_state *s);
int handle_data(bufferlist& bl, off_t ofs, void **phandle);
int throttle_data(void *handle) { return 0; }
- int do_complete(string& etag, map<string, bufferlist>& attrs);
+ int do_complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs);
public:
RGWPutObjProcessor_Plain() : ofs(0) {}
return 0;
}
-int RGWPutObjProcessor_Plain::do_complete(string& etag, map<string, bufferlist>& attrs)
+int RGWPutObjProcessor_Plain::do_complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs)
{
- int r = store->put_obj_meta(s->obj_ctx, obj, data.length(), attrs,
+ int r = store->put_obj_meta(s->obj_ctx, obj, data.length(), mtime, attrs,
RGW_OBJ_CATEGORY_MAIN, PUT_OBJ_CREATE,
&data);
return r;
virtual bool immutable_head() { return false; }
int prepare(RGWRados *store, struct req_state *s);
- virtual int do_complete(string& etag, map<string, bufferlist>& attrs);
+ virtual int do_complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs);
void prepare_next_part(off_t ofs);
void complete_parts();
prepare_next_part(obj_len);
}
-int RGWPutObjProcessor_Atomic::do_complete(string& etag, map<string, bufferlist>& attrs)
+int RGWPutObjProcessor_Atomic::do_complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs)
{
complete_parts();
extra_params.data = &first_chunk;
extra_params.manifest = &manifest;
extra_params.ptag = &s->req_id; /* use req_id as operation tag */
+ extra_params.mtime = mtime;
int r = store->put_obj_meta(s->obj_ctx, head_obj, obj_len, attrs,
RGW_OBJ_CATEGORY_MAIN, PUT_OBJ_CREATE,
protected:
bool immutable_head() { return true; }
int prepare(RGWRados *store, struct req_state *s);
- int do_complete(string& etag, map<string, bufferlist>& attrs);
+ int do_complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs);
public:
RGWPutObjProcessor_Multipart(uint64_t _p) : RGWPutObjProcessor_Atomic(_p) {}
return 0;
}
-int RGWPutObjProcessor_Multipart::do_complete(string& etag, map<string, bufferlist>& attrs)
+int RGWPutObjProcessor_Multipart::do_complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs)
{
complete_parts();
- int r = store->put_obj_meta(s->obj_ctx, head_obj, s->obj_size, attrs, RGW_OBJ_CATEGORY_MAIN, 0);
+ int r = store->put_obj_meta(s->obj_ctx, head_obj, s->obj_size, mtime, attrs, RGW_OBJ_CATEGORY_MAIN, 0);
if (r < 0)
return r;
rgw_get_request_metadata(s->cct, s->info, attrs);
- ret = processor->complete(etag, attrs);
+ ret = processor->complete(etag, &mtime, attrs);
done:
dispose_processor(processor);
perfcounter->tinc(l_rgw_put_lat,
attrs[RGW_ATTR_CONTENT_TYPE] = ct_bl;
}
- ret = processor->complete(etag, attrs);
+ ret = processor->complete(etag, NULL, attrs);
done:
dispose_processor(processor);
obj.init_ns(s->bucket, tmp_obj_name, mp_ns);
// the meta object will be indexed with 0 size, we c
- ret = store->put_obj_meta(s->obj_ctx, obj, 0, attrs, RGW_OBJ_CATEGORY_MULTIMETA, PUT_OBJ_CREATE_EXCL);
+ ret = store->put_obj_meta(s->obj_ctx, obj, 0, NULL, attrs, RGW_OBJ_CATEGORY_MULTIMETA, PUT_OBJ_CREATE_EXCL);
} while (ret == -EEXIST);
}
struct req_state *s;
bool is_complete;
- virtual int do_complete(string& etag, map<string, bufferlist>& attrs) = 0;
+ virtual int do_complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs) = 0;
list<rgw_obj> objs;
};
virtual int handle_data(bufferlist& bl, off_t ofs, void **phandle) = 0;
virtual int throttle_data(void *handle) = 0;
- virtual int complete(string& etag, map<string, bufferlist>& attrs);
+ virtual int complete(string& etag, time_t *mtime, map<string, bufferlist>& attrs);
};
class RGWPutObj : public RGWOp {
bool chunked_upload;
RGWAccessControlPolicy policy;
const char *obj_manifest;
+ time_t mtime;
public:
RGWPutObj() {
supplied_etag = NULL;
chunked_upload = false;
obj_manifest = NULL;
+ mtime = 0;
}
virtual void init(RGWRados *store, struct req_state *s, RGWHandler *h) {
map<string, bufferlist> src_attrs;
- RGWRESTStreamRequest *out_stream_req;
+ RGWRESTStreamWriteRequest *out_stream_req;
int ret = rest_conn->put_obj_init(user_id, dest_obj, astate->size, attrset, &out_stream_req);
if (ret < 0)
if (ret < 0)
return ret;
- ret = rest_conn->complete_request(out_stream_req);
+ string etag;
+
+ ret = rest_conn->complete_request(out_stream_req, etag, mtime);
if (ret < 0)
return ret;
RGWObjManifest *manifest, const string *ptag, list<string> *remove_objs,
bool modify_version, RGWObjVersionTracker *objv_tracker);
- virtual int put_obj_meta(void *ctx, rgw_obj& obj, uint64_t size,
+ virtual int put_obj_meta(void *ctx, rgw_obj& obj, uint64_t size, time_t *mtime,
map<std::string, bufferlist>& attrs, RGWObjCategory category, int flags,
const bufferlist *data = NULL) {
- return put_obj_meta_impl(ctx, obj, size, NULL, attrs, category, flags,
+ return put_obj_meta_impl(ctx, obj, size, mtime, attrs, category, flags,
NULL, data, NULL, NULL, NULL,
false, NULL);
}
s->cio->print("Location: %s\n", redirect.c_str());
}
-void dump_last_modified(struct req_state *s, time_t t)
+static void dump_time_header(struct req_state *s, const char *name, time_t t)
{
char timestr[TIME_BUF_SIZE];
if (strftime(timestr, sizeof(timestr), "%a, %d %b %Y %H:%M:%S %Z", tmp) == 0)
return;
- int r = s->cio->print("Last-Modified: %s\n", timestr);
+ int r = s->cio->print("%s: %s\n", name, timestr);
+ if (r < 0) {
+ ldout(s->cct, 0) << "ERROR: s->cio->print() returned err=" << r << dendl;
+ }
+}
+
+void dump_last_modified(struct req_state *s, time_t t)
+{
+ dump_time_header(s, "Last-Modified", t);
+}
+
+void dump_epoch_header(struct req_state *s, const char *name, time_t t)
+{
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%lld", (long long)t);
+
+ int r = s->cio->print("%s: %s\n", name, buf);
if (r < 0) {
ldout(s->cct, 0) << "ERROR: s->cio->print() returned err=" << r << dendl;
}
extern void dump_owner(struct req_state *s, string& id, string& name, const char *section = NULL);
extern void dump_content_length(struct req_state *s, uint64_t len);
extern void dump_etag(struct req_state *s, const char *etag);
+extern void dump_epoch_header(struct req_state *s, const char *name, time_t t);
extern void dump_last_modified(struct req_state *s, time_t t);
extern void abort_early(struct req_state *s, int err);
extern void dump_range(struct req_state *s, uint64_t ofs, uint64_t end, uint64_t total_size);
#include "common/ceph_crypto_cms.h"
#include "common/armor.h"
+#include "common/strtol.h"
#define dout_subsys ceph_subsys_rgw
char buf[len + 1];
size_t i;
for (i = 0; i < len && *src; ++i, ++src) {
- buf[i] = toupper(*src);
+ switch (*src) {
+ case '-':
+ buf[i] = '_';
+ break;
+ default:
+ buf[i] = toupper(*src);
+ }
}
buf[i] = '\0';
out_headers[buf] = l;
}
class RGWRESTStreamOutCB : public RGWGetDataCB {
- RGWRESTStreamRequest *req;
+ RGWRESTStreamWriteRequest *req;
public:
- RGWRESTStreamOutCB(RGWRESTStreamRequest *_req) : req(_req) {}
+ RGWRESTStreamOutCB(RGWRESTStreamWriteRequest *_req) : req(_req) {}
int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len); /* callback for object iteration when sending data */
};
return req->add_output_data(new_bl);
}
-RGWRESTStreamRequest::~RGWRESTStreamRequest()
+RGWRESTStreamWriteRequest::~RGWRESTStreamWriteRequest()
{
delete cb;
}
-int RGWRESTStreamRequest::add_output_data(bufferlist& bl)
+int RGWRESTStreamWriteRequest::add_output_data(bufferlist& bl)
{
lock.Lock();
if (status < 0) {
}
}
-int RGWRESTStreamRequest::put_obj_init(RGWAccessKey& key, rgw_obj& obj, uint64_t obj_size, map<string, bufferlist>& attrs)
+int RGWRESTStreamWriteRequest::put_obj_init(RGWAccessKey& key, rgw_obj& obj, uint64_t obj_size, map<string, bufferlist>& attrs)
{
string resource = obj.bucket.name + "/" + obj.object;
string new_url = url;
return 0;
}
-int RGWRESTStreamRequest::send_data(void *ptr, size_t len)
+int RGWRESTStreamWriteRequest::send_data(void *ptr, size_t len)
{
uint64_t sent = 0;
- dout(20) << "RGWRESTStreamRequest::send_data()" << dendl;
+ dout(20) << "RGWRESTStreamWriteRequest::send_data()" << dendl;
lock.Lock();
if (pending_send.empty() || status < 0) {
lock.Unlock();
}
-int RGWRESTStreamRequest::complete()
+void set_str_from_headers(map<string, string>& out_headers, const string& header_name, string& str)
+{
+ map<string, string>::iterator iter = out_headers.find(header_name);
+ if (iter != out_headers.end()) {
+ str = iter->second;
+ } else {
+ str.clear();
+ }
+}
+
+
+int RGWRESTStreamWriteRequest::complete(string& etag, time_t *mtime)
{
int ret = complete_request(handle);
if (ret < 0)
return ret;
+ set_str_from_headers(out_headers, "ETAG", etag);
+ if (mtime) {
+ string mtime_str;
+ set_str_from_headers(out_headers, "RGWX_MTIME", mtime_str);
+ string err;
+ long t = strict_strtol(mtime_str.c_str(), 10, &err);
+ if (!err.empty()) {
+ ldout(cct, 0) << "ERROR: failed converting mtime (" << mtime_str << ") to int " << dendl;
+ return -EINVAL;
+ }
+ *mtime = (time_t)t;
+ }
+
return status;
}
};
-class RGWRESTStreamRequest : public RGWRESTSimpleRequest {
+class RGWRESTStreamWriteRequest : public RGWRESTSimpleRequest {
Mutex lock;
list<bufferlist> pending_send;
void *handle;
int add_output_data(bufferlist& bl);
int send_data(void *ptr, size_t len);
- RGWRESTStreamRequest(CephContext *_cct, string& _url, list<pair<string, string> > *_headers,
+ RGWRESTStreamWriteRequest(CephContext *_cct, string& _url, list<pair<string, string> > *_headers,
list<pair<string, string> > *_params) : RGWRESTSimpleRequest(_cct, _url, _headers, _params),
- lock("RGWRESTStreamRequest"), handle(NULL), cb(NULL) {}
- ~RGWRESTStreamRequest();
+ lock("RGWRESTStreamWriteRequest"), handle(NULL), cb(NULL) {}
+ ~RGWRESTStreamWriteRequest();
int put_obj_init(RGWAccessKey& key, rgw_obj& obj, uint64_t obj_size, map<string, bufferlist>& attrs);
- int complete();
+ int complete(string& etag, time_t *mtime);
RGWGetDataCB *get_out_cb() { return cb; }
};
};
int RGWRegionConnection::put_obj_init(const string& uid, rgw_obj& obj, uint64_t obj_size,
- map<string, bufferlist>& attrs, RGWRESTStreamRequest **req)
+ map<string, bufferlist>& attrs, RGWRESTStreamWriteRequest **req)
{
string url;
int ret = get_url(url);
list<pair<string, string> > params;
params.push_back(make_pair<string, string>(RGW_SYS_PARAM_PREFIX "uid", uid));
params.push_back(make_pair<string, string>(RGW_SYS_PARAM_PREFIX "region", region));
- *req = new RGWRESTStreamRequest(cct, url, NULL, ¶ms);
+ *req = new RGWRESTStreamWriteRequest(cct, url, NULL, ¶ms);
return (*req)->put_obj_init(key, obj, obj_size, attrs);
}
-int RGWRegionConnection::complete_request(RGWRESTStreamRequest *req)
+int RGWRegionConnection::complete_request(RGWRESTStreamWriteRequest *req, string& etag, time_t *mtime)
{
- int ret = req->complete();
+ int ret = req->complete(etag, mtime);
delete req;
return ret;
/* async request */
int put_obj_init(const string& uid, rgw_obj& obj, uint64_t obj_size,
- map<string, bufferlist>& attrs, RGWRESTStreamRequest **req);
- int complete_request(RGWRESTStreamRequest *req);
+ map<string, bufferlist>& attrs, RGWRESTStreamWriteRequest **req);
+ int complete_request(RGWRESTStreamWriteRequest *req, string& etag, time_t *mtime);
};
#endif
dump_etag(s, etag.c_str());
dump_content_length(s, 0);
}
+ if (s->system_request && mtime) {
+ dump_epoch_header(s, "Rgwx-Mtime", mtime);
+ }
dump_errno(s);
end_header(s);
}