virtual int create_bucket(std::string& id, std::string& bucket, map<std::string, bufferlist>& attrs, uint64_t auid=0) = 0;
/** write an object to the storage device in the appropriate pool
with the given stats */
- virtual int put_obj(std::string& id, std::string& bucket, std::string& obj, const char *data, size_t size,
- time_t *mtime,
+ virtual int put_obj_meta(std::string& id, std::string& bucket, std::string& obj, time_t *mtime,
map<std::string, bufferlist>& attrs) = 0;
+ virtual int put_obj_data(std::string& id, std::string& bucket, std::string& obj, const char *data,
+ off_t ofs, size_t len, time_t *mtime) = 0;
+
+ int put_obj(std::string& id, std::string& bucket, std::string& obj, const char *data, size_t len,
+ time_t *mtime, map<std::string, bufferlist>& attrs) {
+ int ret = put_obj_meta(id, bucket, obj, NULL, attrs);
+ if (ret >= 0)
+ ret = put_obj_data(id, bucket, obj, data, 0, len, mtime);
+ return ret;
+ }
+
/**
* Copy an object.
* id: unused (well, it's passed to put_obj)
* (if get_data==true) length of read data,
* (if get_data==false) length of the object
*/
- virtual int get_obj(std::string& bucket, std::string& obj,
- char **data, off_t ofs, off_t end,
- map<std::string, bufferlist> *attrs,
- const time_t *mod_ptr,
- const time_t *unmod_ptr,
- const char *if_match,
- const char *if_nomatch,
- bool get_data,
- struct rgw_err *err) = 0;
+ virtual int prepare_get_obj(std::string& bucket, std::string& obj,
+ off_t ofs, off_t *end,
+ map<string, bufferlist> *attrs,
+ const time_t *mod_ptr,
+ const time_t *unmod_ptr,
+ const char *if_match,
+ const char *if_nomatch,
+ bool get_data,
+ void **handle,
+ struct rgw_err *err) = 0;
+
+ virtual int get_obj(void *handle, std::string& bucket, std::string& oid,
+ char **data, off_t ofs, off_t end) = 0;
/**
* Get the attributes for an object.
return 0;
}
-int RGWFS::put_obj(std::string& id, std::string& bucket, std::string& obj, const char *data, size_t size,
- time_t *mtime,
- map<string, bufferlist>& attrs)
+int RGWFS::put_obj_meta(std::string& id, std::string& bucket, std::string& obj,
+ time_t *mtime, map<string, bufferlist>& attrs)
{
int len = strlen(DIR_NAME) + 1 + bucket.size() + 1 + obj.size() + 1;
char buf[len];
if (bl.length()) {
r = fsetxattr(fd, name.c_str(), bl.c_str(), bl.length(), 0);
- if (r < 0) {
- r = -errno;
- close(fd);
- return r;
- }
+ if (r < 0)
+ goto done_err;
}
}
- r = write(fd, data, size);
- if (r < 0) {
- r = -errno;
- close(fd);
- unlink(buf);
- return r;
+ if (mtime) {
+ struct stat st;
+ r = fstat(fd, &st);
+ if (r < 0)
+ goto done_err;
+ *mtime = st.st_mtime;
}
+ r = close(fd);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+done_err:
+ r = -errno;
+ close(fd);
+ unlink(buf);
+ return -errno;
+}
+
+int RGWFS::put_obj_data(std::string& id, std::string& bucket, std::string& obj, const char *data,
+ off_t ofs, size_t size, time_t *mtime)
+{
+ int len = strlen(DIR_NAME) + 1 + bucket.size() + 1 + obj.size() + 1;
+ char buf[len];
+ snprintf(buf, len, "%s/%s/%s", DIR_NAME, bucket.c_str(), obj.c_str());
+ int fd;
+
+ fd = open(buf, O_CREAT | O_WRONLY, 0755);
+ if (fd < 0)
+ return -errno;
+
+ int r;
+
+ r = lseek(fd, ofs, SEEK_SET);
+ if (r < 0)
+ goto done_err;
+
+ r = write(fd, data, size);
+ if (r < 0)
+ goto done_err;
+
if (mtime) {
struct stat st;
r = fstat(fd, &st);
- if (r < 0) {
- r = -errno;
- close(fd);
- unlink(buf);
- return -errno;
- }
+ if (r < 0)
+ goto done_err;
*mtime = st.st_mtime;
}
return -errno;
return 0;
+done_err:
+ r = -errno;
+ close(fd);
+ unlink(buf);
+ return r;
}
int RGWFS::copy_obj(std::string& id, std::string& dest_bucket, std::string& dest_obj,
{
int ret;
char *data;
+ void *handle;
+ off_t ofs = 0, end = -1;
map<string, bufferlist> attrset;
- ret = get_obj(src_bucket, src_obj, &data, 0, -1, &attrset,
- mod_ptr, unmod_ptr, if_match, if_nomatch, true, err);
+ ret = prepare_get_obj(src_bucket, src_obj, 0, &end, &attrset, mod_ptr, unmod_ptr,
+ if_match, if_nomatch, true, &handle, err);
if (ret < 0)
return ret;
+
+ do {
+ ret = get_obj(handle, src_bucket, src_obj, &data, ofs, end);
+ if (ret < 0)
+ return ret;
+ ofs += ret;
+ } while (ofs <= end);
map<string, bufferlist>::iterator iter;
for (iter = attrs.begin(); iter != attrs.end(); ++iter) {
return ret;
}
-int RGWFS::get_obj(std::string& bucket, std::string& obj,
- char **data, off_t ofs, off_t end,
+int RGWFS::prepare_get_obj(std::string& bucket, std::string& obj,
+ off_t ofs, off_t *end,
map<string, bufferlist> *attrs,
const time_t *mod_ptr,
const time_t *unmod_ptr,
const char *if_match,
const char *if_nomatch,
bool get_data,
+ void **handle,
struct rgw_err *err)
{
int len = strlen(DIR_NAME) + 1 + bucket.size() + 1 + obj.size() + 1;
int fd;
struct stat st;
int r = -EINVAL;
- size_t max_len, pos;
+ size_t max_len;
char *etag = NULL;
+ off_t size;
+
+
+ GetObjState *state = new GetObjState;
+ if (!state)
+ return -ENOMEM;
snprintf(buf, len, "%s/%s/%s", DIR_NAME, bucket.c_str(), obj.c_str());
fd = open(buf, O_RDONLY, 0755);
- if (fd < 0)
- return -errno;
+ if (fd < 0) {
+ r = -errno;
+ goto done_err;
+ }
+
+ state->fd = fd;
r = fstat(fd, &st);
if (r < 0)
return -errno;
- if (end < 0)
- end = st.st_size - 1;
+ size = st.st_size;
+
+ if (*end < 0)
+ *end = size - 1;
- max_len = end - ofs + 1;
+ max_len = *end + 1 - ofs;
r = -ECANCELED;
if (mod_ptr) {
if (st.st_mtime < *mod_ptr) {
err->num = "304";
err->code = "PreconditionFailed";
- goto done;
+ goto done_err;
}
}
if (st.st_mtime >= *mod_ptr) {
err->num = "412";
err->code = "PreconditionFailed";
- goto done;
+ goto done_err;
}
}
if (if_match || if_nomatch) {
r = get_attr(RGW_ATTR_ETAG, fd, &etag);
if (r < 0)
- goto done;
+ goto done_err;
r = -ECANCELED;
if (if_match) {
if (strcmp(if_match, etag)) {
err->num = "412";
err->code = "PreconditionFailed";
- goto done;
+ goto done_err;
}
}
if (strcmp(if_nomatch, etag) == 0) {
err->num = "412";
err->code = "PreconditionFailed";
- goto done;
+ goto done_err;
}
}
}
+ free(etag);
+
if (!get_data) {
r = max_len;
goto done;
}
- *data = (char *)malloc(max_len);
- if (!*data) {
- r = -ENOMEM;
- goto done;
- }
- pos = 0;
- while (pos < max_len) {
- r = read(fd, (*data) + pos, max_len);
+ r = 0;
+done:
+ return r;
+
+done_err:
+ delete state;
+ if (fd >= 0)
+ close(fd);
+ return r;
+}
+
+int RGWFS::get_obj(void *handle, std::string& bucket, std::string& obj,
+ char **data, off_t ofs, off_t end)
+{
+ uint64_t len;
+ bufferlist bl;
+ int r = 0;
+
+ GetObjState *state = (GetObjState *)handle;
+
+ if (end <= 0)
+ len = 0;
+ else
+ len = end - ofs + 1;
+ off_t pos = 0;
+
+ while (pos < (off_t)len) {
+ r = read(state->fd, (*data) + pos, len - pos);
if (r > 0) {
pos += r;
} else {
if (!r) {
- cerr << "pos=" << pos << " r=" << r << " max_len=" << max_len << endl;
+ cerr << "pos=" << pos << " r=" << r << " len=" << len << endl;
r = -EIO; /* should not happen as we validated file size earlier */
- goto done;
+ break;
}
switch (errno) {
case EINTR:
break;
default:
r = -errno;
- goto done;
+ break;
}
}
- }
+ }
- r = max_len;
-done:
- free(etag);
- close(fd);
+ if (r >= 0)
+ r = len;
+
+ if (r < 0 || !len || (off_t)(ofs + len - 1) == end) {
+ close(state->fd);
+ delete state;
+ }
return r;
}
-
class RGWFS : public RGWAccess
{
+ struct GetObjState {
+ int fd;
+ };
public:
int list_buckets_init(std::string& id, RGWAccessHandle *handle);
int list_buckets_next(std::string& id, RGWObjEnt& obj, RGWAccessHandle *handle);
std::string& marker, std::vector<RGWObjEnt>& result, map<string, bool>& common_prefixes);
int create_bucket(std::string& id, std::string& bucket, map<std::string, bufferlist>& attrs, uint64_t auid=0);
- int put_obj(std::string& id, std::string& bucket, std::string& obj, const char *data, size_t size,
- time_t *mtime,
+ int put_obj_meta(std::string& id, std::string& bucket, std::string& obj, time_t *mtime,
map<std::string, bufferlist>& attrs);
+ int put_obj_data(std::string& id, std::string& bucket, std::string& obj, const char *data,
+ off_t ofs, size_t size, time_t *mtime);
int copy_obj(std::string& id, std::string& dest_bucket, std::string& dest_obj,
std::string& src_bucket, std::string& src_obj,
time_t *mtime,
int set_attr(std::string& bucket, std::string& obj,
const char *name, bufferlist& bl);
- int get_obj(std::string& bucket, std::string& obj,
- char **data, off_t ofs, off_t end,
- map<std::string, bufferlist> *attrs,
+ int prepare_get_obj(std::string& bucket, std::string& obj,
+ off_t ofs, off_t *end,
+ map<std::string, bufferlist> *attrs,
const time_t *mod_ptr,
const time_t *unmod_ptr,
const char *if_match,
const char *if_nomatch,
bool get_data,
+ void **handle,
struct rgw_err *err);
+
+ int get_obj(void *handle, std::string& bucket, std::string& obj,
+ char **data, off_t ofs, off_t end);
};
#endif
void RGWGetObj::execute()
{
+ void *handle;
+
if (!verify_permission(s, RGW_PERM_READ)) {
ret = -EACCES;
goto done;
init_common();
- len = rgwstore->get_obj(s->bucket_str, s->object_str, &data, ofs, end, &attrs,
- mod_ptr, unmod_ptr, if_match, if_nomatch, get_data, &err);
- if (len < 0)
+ len = rgwstore->prepare_get_obj(s->bucket_str, s->object_str, ofs, &end, &attrs, mod_ptr,
+ unmod_ptr, if_match, if_nomatch, get_data, &handle, &err);
+
+ if (len < 0) {
ret = len;
+ goto done;
+ }
+
+ if (!get_data)
+ goto done;
+
+ while (ofs <= end) {
+ len = rgwstore->get_obj(handle, s->bucket_str, s->object_str, &data, ofs, end);
+ if (len < 0) {
+ ret = len;
+ goto done;
+ }
+
+ ofs += len;
+ send_response(handle);
+ free(data);
+ }
+
+ return;
done:
- send_response();
+ send_response(handle);
+ free(data);
}
int RGWGetObj::init_common()
void execute();
virtual int get_params() = 0;
- virtual int send_response() = 0;
+ virtual int send_response(void *handle) = 0;
};
class RGWListBuckets : public RGWOp {
{
return rados->open_pool(bucket.c_str(), pool);
}
+
+static int close_pool(rados_pool_t pool)
+{
+ return rados->close_pool(pool);
+}
/**
* get listing of the objects in a bucket.
* id: ignored.
* attrs: all the given attrs are written to bucket storage for the given object
* Returns: 0 on success, -ERR# otherwise.
*/
-int RGWRados::put_obj(std::string& id, std::string& bucket, std::string& oid, const char *data, size_t size,
- time_t *mtime,
- map<string, bufferlist>& attrs)
+int RGWRados::put_obj_meta(std::string& id, std::string& bucket, std::string& oid,
+ time_t *mtime, map<string, bufferlist>& attrs)
{
rados_pool_t pool;
}
}
+ if (mtime) {
+ r = rados->stat(pool, oid, NULL, mtime);
+ if (r < 0)
+ return r;
+ }
+
+ close_pool(pool);
+
+ return 0;
+}
+
+/**
+ * Write/overwrite an object to the bucket storage.
+ * id: ignored
+ * bucket: the bucket to store the object in
+ * obj: the object name/key
+ * data: the object contents/value
+ * size: the amount of data to write (data must be this long)
+ * mtime: if non-NULL, writes the given mtime to the bucket storage
+ * attrs: all the given attrs are written to bucket storage for the given object
+ * Returns: 0 on success, -ERR# otherwise.
+ */
+int RGWRados::put_obj_data(std::string& id, std::string& bucket, std::string& oid, const char *data, off_t ofs, size_t len,
+ time_t *mtime)
+{
+ rados_pool_t pool;
+
+ int r = open_pool(bucket, &pool);
+ if (r < 0)
+ return r;
+
bufferlist bl;
- bl.append(data, size);
- r = rados->write_full(pool, oid, bl);
+ bl.append(data, len);
+ r = rados->write(pool, oid, ofs, bl, len);
if (r < 0)
return r;
return r;
}
+ close_pool(pool);
+
return 0;
}
/**
map<string, bufferlist>& attrs, /* in/out */
struct rgw_err *err)
{
- int ret;
+ int ret, r;
char *data;
+ off_t ofs = 0, end = -1;
+ map<string, bufferlist>::iterator iter;
cerr << "copy " << src_bucket << ":" << src_obj << " => " << dest_bucket << ":" << dest_obj << std::endl;
+ void *handle;
+
map<string, bufferlist> attrset;
- ret = get_obj(src_bucket, src_obj, &data, 0, -1, &attrset,
- mod_ptr, unmod_ptr, if_match, if_nomatch, true, err);
+ ret = prepare_get_obj(src_bucket, src_obj, ofs, &end, &attrset,
+ mod_ptr, unmod_ptr, if_match, if_nomatch, true, &handle, err);
if (ret < 0)
return ret;
- map<string, bufferlist>::iterator iter;
+ do {
+ ret = get_obj(&handle, src_bucket, src_obj, &data, ofs, end);
+ if (ret < 0)
+ return ret;
+
+ r = put_obj_data(id, dest_bucket, dest_obj, data, ofs, ret, NULL);
+ free(data);
+ if (r < 0)
+ goto done_err;
+
+ ofs += ret;
+ } while (ofs <= end);
+
for (iter = attrs.begin(); iter != attrs.end(); ++iter) {
attrset[iter->first] = iter->second;
}
attrs = attrset;
- ret = put_obj(id, dest_bucket, dest_obj, data, ret, mtime, attrs);
+ ret = put_obj_meta(id, dest_bucket, dest_obj, mtime, attrs);
return ret;
+done_err:
+ /* FIXME: need to free handle */
+ return r;
}
/**
* (if get_data==true) length of read data,
* (if get_data==false) length of the object
*/
-int RGWRados::get_obj(std::string& bucket, std::string& oid,
- char **data, off_t ofs, off_t end,
+int RGWRados::prepare_get_obj(std::string& bucket, std::string& oid,
+ off_t ofs, off_t *end,
map<string, bufferlist> *attrs,
const time_t *mod_ptr,
const time_t *unmod_ptr,
const char *if_match,
const char *if_nomatch,
bool get_data,
+ void **handle,
struct rgw_err *err)
{
int r = -EINVAL;
- uint64_t size, len;
+ uint64_t size;
bufferlist etag;
time_t mtime;
- bufferlist bl;
- rados_pool_t pool;
map<string, bufferlist>::iterator iter;
- r = open_pool(bucket, &pool);
+ GetObjState *state = new GetObjState;
+ if (!state)
+ return -ENOMEM;
+
+ *handle = state;
+
+ r = open_pool(bucket, &state->pool);
if (r < 0)
- return r;
+ goto done_err;
- r = rados->stat(pool, oid, &size, &mtime);
+ r = rados->stat(state->pool, oid, &size, &mtime);
if (r < 0)
- return r;
+ goto done_err;
if (attrs) {
- r = rados->getxattrs(pool, oid, *attrs);
+ r = rados->getxattrs(state->pool, oid, *attrs);
for (iter = attrs->begin(); iter != attrs->end(); ++iter) {
cerr << "xattr: " << iter->first << std::endl;
}
if (r < 0)
- return r;
+ goto done_err;
}
if (mtime < *mod_ptr) {
err->num = "304";
err->code = "PreconditionFailed";
- goto done;
+ goto done_err;
}
}
if (mtime >= *mod_ptr) {
err->num = "412";
err->code = "PreconditionFailed";
- goto done;
+ goto done_err;
}
}
if (if_match || if_nomatch) {
r = get_attr(bucket, oid, RGW_ATTR_ETAG, etag);
if (r < 0)
- goto done;
+ goto done_err;
r = -ECANCELED;
if (if_match) {
if (strcmp(if_match, etag.c_str())) {
err->num = "412";
err->code = "PreconditionFailed";
- goto done;
+ goto done_err;
}
}
if (strcmp(if_nomatch, etag.c_str()) == 0) {
err->num = "412";
err->code = "PreconditionFailed";
- goto done;
+ goto done_err;
}
}
}
- if (!get_data) {
- r = size;
- goto done;
- }
+ if (*end < 0)
+ *end = size - 1;
+
+ if (!get_data)
+ return size;
+
+ return 0;
+
+done_err:
+ delete state;
+ *handle = NULL;
+ return r;
+}
+
+int RGWRados::get_obj(void *handle,
+ std::string& bucket, std::string& oid,
+ char **data, off_t ofs, off_t end)
+{
+ uint64_t len;
+ bufferlist bl;
+
+ GetObjState *state = (GetObjState *)handle;
if (end <= 0)
len = 0;
len = end - ofs + 1;
cout << "rados->read ofs=" << ofs << " len=" << len << std::endl;
- r = rados->read(pool, oid, ofs, bl, len);
+ int r = rados->read(state->pool, oid, ofs, bl, len);
cout << "rados->read r=" << r << std::endl;
- if (r < 0)
- return r;
if (r > 0) {
*data = (char *)malloc(r);
memcpy(*data, bl.c_str(), bl.length());
}
-done:
+ if (r < 0 || !len || (ofs + len - 1 == end)) {
+ rados->close_pool(state->pool);
+ delete state;
+ }
return r;
+
}
{
/** Open the pool used as root for this gateway */
int open_root_pool(rados_pool_t *pool);
+
+ struct GetObjState {
+ rados_pool_t pool;
+ bool sent_data;
+
+ GetObjState() : pool(0), sent_data(false) {}
+ };
+
public:
/** Initialize the RADOS instance and prepare to do other ops */
int initialize(int argc, char *argv[]);
int create_bucket(std::string& id, std::string& bucket, map<std::string,bufferlist>& attrs, uint64_t auid=0);
/** Write/overwrite an object to the bucket storage. */
- int put_obj(std::string& id, std::string& bucket, std::string& obj, const char *data, size_t size,
- time_t *mtime,
+ int put_obj_meta(std::string& id, std::string& bucket, std::string& obj, time_t *mtime,
map<std::string, bufferlist>& attrs);
+ int put_obj_data(std::string& id, std::string& bucket, std::string& obj, const char *data,
+ off_t ofs, size_t len, time_t *mtime);
/** Copy an object, with many extra options */
int copy_obj(std::string& id, std::string& dest_bucket, std::string& dest_obj,
std::string& src_bucket, std::string& src_obj,
const char *name, bufferlist& bl);
/** Get data about an object out of RADOS and into memory. */
- int get_obj(std::string& bucket, std::string& obj,
- char **data, off_t ofs, off_t end,
+ int prepare_get_obj(std::string& bucket, std::string& obj,
+ off_t ofs, off_t *end,
map<string, bufferlist> *attrs,
const time_t *mod_ptr,
const time_t *unmod_ptr,
const char *if_match,
const char *if_nomatch,
bool get_data,
+ void **handle,
struct rgw_err *err);
+
+ int get_obj(void *handle, std::string& bucket, std::string& oid,
+ char **data, off_t ofs, off_t end);
};
#endif
return 0;
}
-int RGWGetObj_REST::send_response()
+int RGWGetObj_REST::send_response(void *handle)
{
const char *content_type = NULL;
+ if (sent_header)
+ goto send_data;
+
if (get_data && !ret) {
dump_content_length(s, len);
}
}
dump_errno(s, ret, &err);
end_header(s, content_type);
+
+ sent_header = true;
+
+send_data:
if (get_data && !ret) {
FCGX_PutStr(data, len, s->fcgx->out);
}
class RGWGetObj_REST : public RGWGetObj
{
+ bool sent_header;
public:
RGWGetObj_REST() {}
~RGWGetObj_REST() {}
+
+ virtual void init(struct req_state *s) {
+ RGWGetObj::init(s);
+ sent_header = false;
+ }
+
int get_params();
- int send_response();
+ int send_response(void *handle);
};
class RGWListBuckets_REST : public RGWListBuckets {
int ret;
char *data;
struct rgw_err err;
+ void *handle;
+ off_t ofs = 0, end = -1;
- ret = rgwstore->get_obj(ui_bucket, user_id, &data, 0, -1, NULL, NULL, NULL, NULL, NULL, true, &err);
- if (ret < 0) {
+ ret = rgwstore->prepare_get_obj(ui_bucket, user_id, ofs, &end, NULL, NULL, NULL, NULL, NULL, true, &handle, &err);
+ if (ret < 0)
return ret;
- }
- bl.append(data, ret);
+ do {
+ ret = rgwstore->get_obj(handle, ui_bucket, user_id, &data, ofs, end);
+ if (ret < 0) {
+ return ret;
+ }
+ bl.append(data, ret);
+ free(data);
+ ofs += ret;
+ } while (ofs <= end);
+
bufferlist::iterator iter = bl.begin();
info.decode(iter);
- free(data);
return 0;
}
char *data;
struct rgw_err err;
RGWUID uid;
+ void *handle;
+ off_t ofs = 0, end = -1;
- ret = rgwstore->get_obj(ui_email_bucket, email, &data, 0, -1, NULL, NULL, NULL, NULL, NULL, true, &err);
- if (ret < 0) {
+ ret = rgwstore->prepare_get_obj(ui_email_bucket, email, ofs, &end, NULL, NULL,
+ NULL, NULL, NULL, true, &handle, &err);
+ if (ret < 0)
return ret;
- }
- bl.append(data, ret);
+ do {
+ ret = rgwstore->get_obj(handle, ui_email_bucket, email, &data, ofs, end);
+ if (ret < 0)
+ return ret;
+ ofs += ret;
+ bl.append(data, ret);
+ free(data);
+ } while (ofs <= end);
+
bufferlist::iterator iter = bl.begin();
uid.decode(iter);
user_id = uid.user_id;
- free(data);
return 0;
}