]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
Implement multi-object delete.
authorcaleb miles <caleb.miles@inktank.com>
Tue, 28 Aug 2012 00:08:44 +0000 (17:08 -0700)
committercaleb miles <caleb.miles@inktank.com>
Tue, 28 Aug 2012 00:08:44 +0000 (17:08 -0700)
An implimentation of multi-object delete described in
the latest Amazon S3 API provied at

http://docs.amazonwebservices.com/AmazonS3/latest/API

This commit is in response to tracker issue 2797

http://tracker.newdream.net/issues/2797

Signed-off-by: caleb miles <caleb.miles@inktank.com>
12 files changed:
src/Makefile.am
src/rgw/rgw_common.cc
src/rgw/rgw_html_errors.h [new file with mode: 0644]
src/rgw/rgw_multi_del.cc [new file with mode: 0644]
src/rgw/rgw_multi_del.h [new file with mode: 0644]
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rest.cc
src/rgw/rgw_rest.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h
src/rgw/rgw_rest_swift.cc

index 0cffd064bdb9f611b399096ca5527e92d3b83d80..cd3c948fb7c07b4165f0eea61b2af1321ddaa265 100644 (file)
@@ -315,6 +315,7 @@ librgw_a_SOURCES =  \
        rgw/rgw_formats.cc \
        rgw/rgw_log.cc \
        rgw/rgw_multi.cc \
+       rgw/rgw_multi_del.cc \
        rgw/rgw_env.cc
 librgw_a_CFLAGS = ${CRYPTO_CFLAGS} ${AM_CFLAGS}
 librgw_a_CXXFLAGS = ${CRYPTO_CXXFLAGS} ${AM_CXXFLAGS}
@@ -1647,6 +1648,7 @@ noinst_HEADERS = \
        rgw/rgw_formats.h\
        rgw/rgw_log.h\
        rgw/rgw_multi.h\
+       rgw/rgw_multi_del.h\
        rgw/rgw_op.h\
        rgw/rgw_swift.h\
        rgw/rgw_swift_auth.h\
index 675444b4b84b34c82f9b5e8c46ae9043a80d7aee..5fa0416b5f2a6f1ddc5d380e7d55c57747976bc0 100644 (file)
@@ -326,6 +326,7 @@ int XMLArgs::parse()
 
       if ((name.compare("acl") == 0) ||
           (name.compare("location") == 0) ||
+          (name.compare("delete") == 0) ||
           (name.compare("uploads") == 0) ||
           (name.compare("partNumber") == 0) ||
           (name.compare("uploadId") == 0) ||
diff --git a/src/rgw/rgw_html_errors.h b/src/rgw/rgw_html_errors.h
new file mode 100644 (file)
index 0000000..4202216
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef RGW_HTML_ERRORS_H_
+#define RGW_HTML_ERRORS_H_
+
+#include "rgw_common.h"
+
+struct rgw_html_errors {
+  int err_no;
+  int http_ret;
+  const char *s3_code;
+};
+
+const static struct rgw_html_errors RGW_HTML_ERRORS[] = {
+    { 0, 200, "" },
+    { STATUS_CREATED, 201, "Created" },
+    { STATUS_ACCEPTED, 202, "Accepted" },
+    { STATUS_NO_CONTENT, 204, "NoContent" },
+    { STATUS_PARTIAL_CONTENT, 206, "" },
+    { ERR_NOT_MODIFIED, 304, "NotModified" },
+    { EINVAL, 400, "InvalidArgument" },
+    { ERR_INVALID_REQUEST, 400, "InvalidRequest" },
+    { ERR_INVALID_DIGEST, 400, "InvalidDigest" },
+    { ERR_BAD_DIGEST, 400, "BadDigest" },
+    { ERR_INVALID_BUCKET_NAME, 400, "InvalidBucketName" },
+    { ERR_INVALID_OBJECT_NAME, 400, "InvalidObjectName" },
+    { ERR_UNRESOLVABLE_EMAIL, 400, "UnresolvableGrantByEmailAddress" },
+    { ERR_INVALID_PART, 400, "InvalidPart" },
+    { ERR_INVALID_PART_ORDER, 400, "InvalidPartOrder" },
+    { ERR_REQUEST_TIMEOUT, 400, "RequestTimeout" },
+    { ERR_TOO_LARGE, 400, "EntityTooLarge" },
+    { ERR_TOO_MANY_BUCKETS, 400, "TooManyBuckets" },
+    { ERR_LENGTH_REQUIRED, 411, "MissingContentLength" },
+    { EACCES, 403, "AccessDenied" },
+    { EPERM, 403, "AccessDenied" },
+    { ERR_USER_SUSPENDED, 403, "UserSuspended" },
+    { ERR_REQUEST_TIME_SKEWED, 403, "RequestTimeTooSkewed" },
+    { ENOENT, 404, "NoSuchKey" },
+    { ERR_NO_SUCH_BUCKET, 404, "NoSuchBucket" },
+    { ERR_NO_SUCH_UPLOAD, 404, "NoSuchUpload" },
+    { ERR_METHOD_NOT_ALLOWED, 405, "MethodNotAllowed" },
+    { ETIMEDOUT, 408, "RequestTimeout" },
+    { EEXIST, 409, "BucketAlreadyExists" },
+    { ENOTEMPTY, 409, "BucketNotEmpty" },
+    { ERR_PRECONDITION_FAILED, 412, "PreconditionFailed" },
+    { ERANGE, 416, "InvalidRange" },
+    { ERR_UNPROCESSABLE_ENTITY, 422, "UnprocessableEntity" },
+    { ERR_INTERNAL_ERROR, 500, "InternalError" },
+};
+
+const static struct rgw_html_errors RGW_HTML_SWIFT_ERRORS[] = {
+    { EACCES, 401, "AccessDenied" },
+    { EPERM, 401, "AccessDenied" },
+    { ERR_USER_SUSPENDED, 401, "UserSuspended" },
+    { ERR_INVALID_UTF8, 412, "Invalid UTF8" },
+    { ERR_BAD_URL, 412, "Bad URL" },
+};
+
+#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
+
+static const struct rgw_html_errors *search_err(int err_no, const struct rgw_html_errors *errs, int len)
+{
+  for (int i = 0; i < len; ++i, ++errs) {
+    if (err_no == errs->err_no)
+      return errs;
+  }
+  return NULL;
+}
+
+
+
+#endif
diff --git a/src/rgw/rgw_multi_del.cc b/src/rgw/rgw_multi_del.cc
new file mode 100644 (file)
index 0000000..fb7a4a8
--- /dev/null
@@ -0,0 +1,64 @@
+#include <string.h>
+
+#include <iostream>
+
+#include "include/types.h"
+
+#include "rgw_xml.h"
+#include "rgw_multi_del.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+
+bool RGWMultiDelObject::xml_end(const char *el)
+{
+  RGWMultiDelKey *key_obj = (RGWMultiDelKey *)find_first("Key");
+
+  if (!key_obj)
+    return false;
+
+  string s = key_obj->get_data();
+  if (s.empty())
+    return false;
+
+  key = s;
+
+  return true;
+}
+
+bool RGWMultiDelDelete::xml_end(const char *el) {
+  RGWMultiDelQuiet *quiet_set = (RGWMultiDelQuiet *)find_first("Quiet");
+  if (quiet_set) {
+    string quiet_val = quiet_set->get_data();
+    quiet = (strcasecmp(quiet_val.c_str(), "true") == 0);
+  }
+
+  XMLObjIter iter = find("Object");
+  RGWMultiDelObject *object = (RGWMultiDelObject *)iter.get_next();
+  while (object) {
+    string key = object->get_key();
+    objects.push_back(key);
+    object = (RGWMultiDelObject *)iter.get_next();
+  }
+  return true;
+}
+
+XMLObj *RGWMultiDelXMLParser::alloc_obj(const char *el) {
+  XMLObj *obj = NULL;
+  if (strcmp(el, "Delete") == 0) {
+    obj = new RGWMultiDelDelete();
+  } else if (strcmp(el, "Quiet") == 0) {
+    obj = new RGWMultiDelQuiet();
+  } else if (strcmp(el, "Object") == 0) {
+    obj = new RGWMultiDelObject ();
+  } else if (strcmp(el, "Key") == 0) {
+    obj = new RGWMultiDelKey();
+  } else if (strcmp(el, "VersionID") == 0) {
+    /*do nothing*/
+  }
+
+  return obj;
+}
+
diff --git a/src/rgw/rgw_multi_del.h b/src/rgw/rgw_multi_del.h
new file mode 100644 (file)
index 0000000..c590a1e
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef RGW_MULTI_DELETE_H_
+#define RGW_MULTI_DELETE_H_
+
+#include <vector>
+#include "rgw_xml.h"
+
+class RGWMultiDelDelete : public XMLObj
+{
+public:
+  RGWMultiDelDelete() :quiet(false) {}
+  ~RGWMultiDelDelete() {}
+  bool xml_end(const char *el);
+
+  std::vector<string> objects;
+  bool quiet;
+  bool is_quiet() { return quiet; };
+};
+
+class RGWMultiDelQuiet : public XMLObj
+{
+public:
+  RGWMultiDelQuiet() {}
+  ~RGWMultiDelQuiet() {}
+};
+
+class RGWMultiDelObject : public XMLObj
+{
+  string key;
+  string versionID;
+public:
+  RGWMultiDelObject() {}
+  ~RGWMultiDelObject() {}
+  bool xml_end(const char *el);
+
+  string get_key() { return key; }
+};
+
+class RGWMultiDelKey : public XMLObj
+{
+public:
+  RGWMultiDelKey() {}
+  ~RGWMultiDelKey() {}
+};
+
+class RGWMultiDelXMLParser : public RGWXMLParser
+{
+  XMLObj *alloc_obj(const char *el);
+public:
+  RGWMultiDelXMLParser() {}
+  ~RGWMultiDelXMLParser() {}
+};
+
+
+#endif
index 35b5f5d79f6958c61c59ca5402c1ec51b522ba28..345343f6cf22a39f8081d65907fbcfec6a82928d 100644 (file)
@@ -17,6 +17,7 @@
 #include "rgw_user.h"
 #include "rgw_log.h"
 #include "rgw_multi.h"
+#include "rgw_multi_del.h"
 
 #ifdef FASTCGI_INCLUDE_DIR
 # include "fastcgi/fcgiapp.h"
@@ -1742,6 +1743,85 @@ done:
   send_response();
 }
 
+int RGWDeleteMultiObj::verify_permission()
+{
+  if (!verify_bucket_permission(s, RGW_PERM_WRITE))
+    return -EACCES;
+
+  return 0;
+}
+
+void RGWDeleteMultiObj::execute()
+{
+  RGWMultiDelDelete *multi_delete;
+  vector<string>::iterator iter;
+  RGWMultiDelXMLParser parser;
+  pair<string,int> result;
+  int num_processed = 0;
+
+  ret = get_params();
+  if (ret < 0) {
+    goto error;
+  }
+
+  if (!data) {
+    ret = -EINVAL;
+    goto error;
+  }
+
+  if (!parser.init()) {
+    ret = -EINVAL;
+    goto error;
+  }
+
+  if (!parser.parse(data, len, 1)) {
+    ret = -EINVAL;
+    goto error;
+  }
+
+  multi_delete = (RGWMultiDelDelete *)parser.find_first("Delete");
+  if (!multi_delete) {
+    ret = -EINVAL;
+    goto error;
+  }
+
+  if (multi_delete->is_quiet())
+    quiet = true;
+
+  begin_response();
+  if (multi_delete->objects.size() == 0) {
+    goto done;
+  }
+
+  for (iter = multi_delete->objects.begin();
+        iter != multi_delete->objects.end() && num_processed < max_to_delete;
+        ++iter, num_processed++) {
+
+    rgw_obj obj(bucket,(*iter));
+    rgwstore->set_atomic(s->obj_ctx, obj);
+    ret = rgwstore->delete_obj(s->obj_ctx, obj);
+    result = make_pair(*iter, ret);
+
+    send_partial_response(result);
+  }
+
+  /*  set the return code to zero, errors at this point will be
+  dumped to the response */
+  ret = 0;
+
+done:
+  // will likely segfault if begin_response() has not been called
+  end_response();
+  free(data);
+  return;
+
+error:
+  send_status();
+  free(data);
+  return;
+
+}
+
 int RGWHandler::init(struct req_state *_s, FCGX_Request *fcgx)
 {
   s = _s;
index be4087fa9410ec66d60172dfb7abc98374456035..724f0e69608679feb215d90692abb9ca98977f33 100644 (file)
@@ -665,6 +665,43 @@ public:
   virtual const char *name() { return "list_bucket_multiparts"; }
 };
 
+class RGWDeleteMultiObj : public RGWOp {
+protected:
+  int ret;
+  int max_to_delete;
+  size_t len;
+  char *data;
+  string bucket_name;
+  rgw_bucket bucket;
+  bool quiet;
+  bool status_dumped;
+
+
+public:
+  RGWDeleteMultiObj() {}
+
+  virtual void init(struct req_state *s, RGWHandler *h) {
+    RGWOp::init(s, h);
+    ret = 0;
+    max_to_delete = 1000;
+    len = 0;
+    data = NULL;
+    bucket_name = "";
+    quiet = false;
+    status_dumped = false;
+  }
+  int verify_permission();
+  void execute();
+
+  virtual int get_params() = 0;
+  virtual void send_status() = 0;
+  virtual void begin_response() = 0;
+  virtual void send_partial_response(pair<string,int>& result) = 0;
+  virtual void end_response() = 0;
+  virtual const char *name() { return "multi_object_delete"; }
+};
+
+
 class RGWHandler {
 protected:
   struct req_state *s;
index 2e5e78fefbacf7bc8ee8d56d94a057433a4d4219..164ea4d2325cb80f3c1de7cf892d823b5048c16b 100644 (file)
@@ -26,69 +26,19 @@ static void dump_status(struct req_state *s, const char *status)
   CGI_PRINTF(s,"Status: %s\n", status);
 }
 
-struct rgw_html_errors {
-  int err_no;
-  int http_ret;
-  const char *s3_code;
-};
-
-const static struct rgw_html_errors RGW_HTML_ERRORS[] = {
-    { 0, 200, "" },
-    { STATUS_CREATED, 201, "Created" },
-    { STATUS_ACCEPTED, 202, "Accepted" },
-    { STATUS_NO_CONTENT, 204, "NoContent" },
-    { STATUS_PARTIAL_CONTENT, 206, "" },
-    { ERR_NOT_MODIFIED, 304, "NotModified" },
-    { EINVAL, 400, "InvalidArgument" },
-    { ERR_INVALID_REQUEST, 400, "InvalidRequest" },
-    { ERR_INVALID_DIGEST, 400, "InvalidDigest" },
-    { ERR_BAD_DIGEST, 400, "BadDigest" },
-    { ERR_INVALID_BUCKET_NAME, 400, "InvalidBucketName" },
-    { ERR_INVALID_OBJECT_NAME, 400, "InvalidObjectName" },
-    { ERR_UNRESOLVABLE_EMAIL, 400, "UnresolvableGrantByEmailAddress" },
-    { ERR_INVALID_PART, 400, "InvalidPart" },
-    { ERR_INVALID_PART_ORDER, 400, "InvalidPartOrder" },
-    { ERR_REQUEST_TIMEOUT, 400, "RequestTimeout" },
-    { ERR_TOO_LARGE, 400, "EntityTooLarge" },
-    { ERR_TOO_MANY_BUCKETS, 400, "TooManyBuckets" },
-    { ERR_LENGTH_REQUIRED, 411, "MissingContentLength" },
-    { EACCES, 403, "AccessDenied" },
-    { EPERM, 403, "AccessDenied" },
-    { ERR_USER_SUSPENDED, 403, "UserSuspended" },
-    { ERR_REQUEST_TIME_SKEWED, 403, "RequestTimeTooSkewed" },
-    { ENOENT, 404, "NoSuchKey" },
-    { ERR_NO_SUCH_BUCKET, 404, "NoSuchBucket" },
-    { ERR_NO_SUCH_UPLOAD, 404, "NoSuchUpload" },
-    { ERR_METHOD_NOT_ALLOWED, 405, "MethodNotAllowed" },
-    { ETIMEDOUT, 408, "RequestTimeout" },
-    { EEXIST, 409, "BucketAlreadyExists" },
-    { ENOTEMPTY, 409, "BucketNotEmpty" },
-    { ERR_PRECONDITION_FAILED, 412, "PreconditionFailed" },
-    { ERANGE, 416, "InvalidRange" },
-    { ERR_UNPROCESSABLE_ENTITY, 422, "UnprocessableEntity" },
-    { ERR_INTERNAL_ERROR, 500, "InternalError" },
-};
-
-const static struct rgw_html_errors RGW_HTML_SWIFT_ERRORS[] = {
-    { EACCES, 401, "AccessDenied" },
-    { EPERM, 401, "AccessDenied" },
-    { ERR_USER_SUSPENDED, 401, "UserSuspended" },
-    { ERR_INVALID_UTF8, 412, "Invalid UTF8" },
-    { ERR_BAD_URL, 412, "Bad URL" },
-};
-
-#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
-
-static const struct rgw_html_errors *search_err(int err_no, const struct rgw_html_errors *errs, int len)
+void rgw_flush_formatter_and_reset(struct req_state *s, Formatter *formatter)
 {
-  for (int i = 0; i < len; ++i, ++errs) {
-    if (err_no == errs->err_no)
-      return errs;
+  std::ostringstream oss;
+  formatter->flush(oss);
+  std::string outs(oss.str());
+  if (!outs.empty()) {
+    CGI_PutStr(s, outs.c_str(), outs.size());
   }
-  return NULL;
+
+  s->formatter->reset();
 }
 
-void flush_formatter_to_req_state(struct req_state *s, Formatter *formatter)
+void rgw_flush_formatter(struct req_state *s, Formatter *formatter)
 {
   std::ostringstream oss;
   formatter->flush(oss);
@@ -96,7 +46,6 @@ void flush_formatter_to_req_state(struct req_state *s, Formatter *formatter)
   if (!outs.empty()) {
     CGI_PutStr(s, outs.c_str(), outs.size());
   }
-  s->formatter->reset();
 }
 
 void set_req_state_err(struct req_state *s, int err_no)
@@ -235,7 +184,7 @@ void end_header(struct req_state *s, const char *content_type)
   }
   CGI_PRINTF(s,"Content-type: %s\r\n\r\n", content_type);
   s->header_ended = true;
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 void abort_early(struct req_state *s, int err_no)
@@ -243,7 +192,7 @@ void abort_early(struct req_state *s, int err_no)
   set_req_state_err(s, err_no);
   dump_errno(s);
   end_header(s);
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
   perfcounter->inc(l_rgw_failed_req);
 }
 
@@ -457,6 +406,53 @@ int RGWListBucketMultiparts_REST::get_params()
   return 0;
 }
 
+int RGWDeleteMultiObj_REST::get_params()
+{
+  static rgw_bucket pi_buckets_rados = RGW_ROOT_BUCKET;
+  bufferlist bl;
+  RGWBucketInfo info;
+
+  bucket_name = s->bucket_name;
+
+  if (bucket_name.empty()) {
+    ret = -EINVAL;
+    return ret;
+  }
+
+  ret = rgw_get_obj(NULL, pi_buckets_rados, bucket_name, bl, NULL);
+  if (ret < 0)
+    return ret;
+
+  bufferlist::iterator iter = bl.begin();
+  try {
+    ::decode(info, iter);
+  } catch (buffer::error& err) {
+    cerr << "ERROR: could not decode buffer info, caught buffer::error" << std::endl;
+    return -EIO;
+  }
+
+  // everything is probably fine, set the bucket
+  bucket = s->bucket;
+
+  size_t cl = 0;
+
+  if (s->length)
+    cl = atoll(s->length);
+  if (cl) {
+    data = (char *)malloc(cl + 1);
+    if (!data) {
+      ret = -ENOMEM;
+      return ret;
+    }
+    CGI_GetStr(s, data, cl, len);
+    data[len] = '\0';
+  } else {
+    return -EINVAL;
+  }
+
+  return ret;
+}
+
 static void next_tok(string& str, string& tok, char delim)
 {
   if (str.size() == 0) {
@@ -912,6 +908,11 @@ int RGWHandler_REST::read_permissions(RGWOp *op_obj)
     break;
   case OP_PUT:
   case OP_POST:
+    /* is it a 'multi-object delete' request? */
+    if (s->request_params == "delete") {
+      only_bucket = true;
+      break;
+    }
     if (is_obj_update_op()) {
       only_bucket = false;
       break;
index b0ba54965fac0ca896f164ca81bceb35e152e135..fd4b8748ae5b25520eaecc2e3309f1661fa774bf 100644 (file)
@@ -4,9 +4,11 @@
 
 #include "rgw_op.h"
 
-extern void flush_formatter_to_req_state(struct req_state *s,
+extern void rgw_flush_formatter_and_reset(struct req_state *s,
                                         ceph::Formatter *formatter);
 
+extern void rgw_flush_formatter(struct req_state *s,
+                                         ceph::Formatter *formatter);
 
 class RGWGetObj_REST : public RGWGetObj
 {
@@ -141,6 +143,14 @@ public:
   int get_params();
 };
 
+class RGWDeleteMultiObj_REST : public RGWDeleteMultiObj {
+public:
+  RGWDeleteMultiObj_REST() {}
+  ~RGWDeleteMultiObj_REST() {}
+
+  int get_params();
+};
+
 class RGWHandler_REST : public RGWHandler {
 protected:
   virtual bool is_acl_op() = 0;
index 968b329d2ef27eee8e3385256bb96ae9652d86f4..0fb3de99457e5d8cda4c7b2353fad28796ad21cd 100644 (file)
@@ -39,6 +39,20 @@ void dump_bucket(struct req_state *s, RGWBucketEnt& obj)
   s->formatter->close_section();
 }
 
+void rgw_get_errno_s3(rgw_html_errors *e , int err_no)
+{
+  const struct rgw_html_errors *r;
+  r = search_err(err_no, RGW_HTML_ERRORS, ARRAY_LEN(RGW_HTML_ERRORS));
+
+  if (r) {
+    e->http_ret = r->http_ret;
+    e->s3_code = r->s3_code;
+  } else {
+    e->http_ret = 500;
+    e->s3_code = "UnknownError";
+  }
+}
+
 int RGWGetObj_REST_S3::send_response(bufferlist& bl)
 {
   string content_type_str;
@@ -141,7 +155,7 @@ void RGWListBuckets_REST_S3::send_response()
   list_all_buckets_end(s);
   dump_content_length(s, s->formatter->get_len());
   end_header(s, "application/xml");
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 int RGWListBucket_REST_S3::get_params()
@@ -202,7 +216,7 @@ void RGWListBucket_REST_S3::send_response()
     }
   }
   s->formatter->close_section();
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 static void dump_bucket_metadata(struct req_state *s, RGWBucketEnt& bucket)
@@ -371,7 +385,7 @@ void RGWCopyObj_REST_S3::send_response()
       }
     }
     s->formatter->close_section();
-    flush_formatter_to_req_state(s, s->formatter);
+    rgw_flush_formatter_and_reset(s, s->formatter);
   }
 }
 
@@ -432,7 +446,7 @@ void RGWInitMultipart_REST_S3::send_response()
     s->formatter->dump_string("Key", s->object);
     s->formatter->dump_string("UploadId", upload_id);
     s->formatter->close_section();
-    flush_formatter_to_req_state(s, s->formatter);
+    rgw_flush_formatter_and_reset(s, s->formatter);
   }
 }
 
@@ -452,7 +466,7 @@ void RGWCompleteMultipart_REST_S3::send_response()
     s->formatter->dump_string("Key", s->object);
     s->formatter->dump_string("ETag", etag);
     s->formatter->close_section();
-    flush_formatter_to_req_state(s, s->formatter);
+    rgw_flush_formatter_and_reset(s, s->formatter);
   }
 }
 
@@ -517,7 +531,7 @@ void RGWListMultipart_REST_S3::send_response()
       s->formatter->close_section();
     }
     s->formatter->close_section();
-    flush_formatter_to_req_state(s, s->formatter);
+    rgw_flush_formatter_and_reset(s, s->formatter);
   }
 }
 
@@ -576,7 +590,65 @@ void RGWListBucketMultiparts_REST_S3::send_response()
     }
   }
   s->formatter->close_section();
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
+}
+
+void RGWDeleteMultiObj_REST_S3::send_status()
+{
+  if (!status_dumped) {
+    if (ret < 0)
+      set_req_state_err(s, ret);
+    dump_errno(s);
+    status_dumped = true;
+  }
+}
+
+void RGWDeleteMultiObj_REST_S3::begin_response()
+{
+
+  if (!status_dumped) {
+    send_status();
+  }
+
+  dump_start(s);
+  end_header(s, "application/xml");
+  s->formatter->open_object_section_in_ns("DeleteResult",
+                                            "http://s3.amazonaws.com/doc/2006-03-01/");
+
+  rgw_flush_formatter(s, s->formatter);
+}
+
+void RGWDeleteMultiObj_REST_S3::send_partial_response(pair<string,int>& result)
+{
+  if (!result.first.empty()) {
+    if (result.second == 0 && !quiet) {
+      s->formatter->open_object_section("Deleted");
+      s->formatter->dump_string("Key", result.first);
+      s->formatter->close_section();
+    } else if (result.first < 0) {
+      struct rgw_html_errors *r = new rgw_html_errors;
+      int err_no;
+
+      s->formatter->open_object_section("Error");
+
+      err_no = -(result.second);
+      rgw_get_errno_s3(r, err_no);
+
+      s->formatter->dump_string("Key", result.first);
+      s->formatter->dump_int("Code", r->http_ret);
+      s->formatter->dump_string("Message", r->s3_code);
+      s->formatter->close_section();
+    }
+
+    rgw_flush_formatter(s, s->formatter);
+  }
+}
+
+void RGWDeleteMultiObj_REST_S3::end_response()
+{
+
+  s->formatter->close_section();
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 RGWOp *RGWHandler_REST_S3::get_retrieve_obj_op(bool get_data)
@@ -654,6 +726,9 @@ RGWOp *RGWHandler_REST_S3::get_post_op()
     else
       return new RGWInitMultipart_REST_S3;
   }
+  else if ( s->request_params == "delete" ) {
+    return new RGWDeleteMultiObj_REST_S3;
+  }
 
   return NULL;
 }
index 72b20df7d91f091d9531824f2569b46804107f30..cf601bf3ba9beedb6c687a470e8812ab31d736f9 100644 (file)
@@ -3,10 +3,13 @@
 #define TIME_BUF_SIZE 128
 
 #include "rgw_op.h"
+#include "rgw_html_errors.h"
 #include "rgw_acl_s3.h"
 
 #define RGW_AUTH_GRACE_MINS 15
 
+void rgw_get_errno_s3(struct rgw_html_errors *e, int err_no);
+
 class RGWGetObj_REST_S3 : public RGWGetObj_REST
 {
 public:
@@ -149,6 +152,18 @@ public:
   void send_response();
 };
 
+class RGWDeleteMultiObj_REST_S3 : public RGWDeleteMultiObj_REST {
+public:
+  RGWDeleteMultiObj_REST_S3() {}
+  ~RGWDeleteMultiObj_REST_S3() {}
+
+  void send_status();
+  void begin_response();
+  void send_partial_response(pair<string,int>& result);
+  void end_response();
+};
+
+
 class RGWHandler_REST_S3 : public RGWHandler_REST {
 protected:
   bool is_acl_op() {
index d6e59845fabfdae3c56bc41299e5c7d78dc422e0..8fa8f5d92457198fa68eeaa69fe5fd858f80a41d 100644 (file)
@@ -67,7 +67,7 @@ done:
     return;
   }
 
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 int RGWListBucket_REST_SWIFT::get_params()
@@ -186,7 +186,7 @@ next:
     return;
   }
 
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 static void dump_container_metadata(struct req_state *s, RGWBucketEnt& bucket)
@@ -270,7 +270,7 @@ void RGWCreateBucket_REST_SWIFT::send_response()
   set_req_state_err(s, ret);
   dump_errno(s);
   end_header(s);
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 void RGWDeleteBucket_REST_SWIFT::send_response()
@@ -282,7 +282,7 @@ void RGWDeleteBucket_REST_SWIFT::send_response()
   set_req_state_err(s, r);
   dump_errno(s);
   end_header(s);
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 int RGWPutObj_REST_SWIFT::get_params()
@@ -325,7 +325,7 @@ void RGWPutObj_REST_SWIFT::send_response()
   set_req_state_err(s, ret);
   dump_errno(s);
   end_header(s);
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 int RGWPutMetadata_REST_SWIFT::get_params()
@@ -366,7 +366,7 @@ void RGWPutMetadata_REST_SWIFT::send_response()
   set_req_state_err(s, ret);
   dump_errno(s);
   end_header(s);
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 void RGWDeleteObj_REST_SWIFT::send_response()
@@ -378,7 +378,7 @@ void RGWDeleteObj_REST_SWIFT::send_response()
   set_req_state_err(s, r);
   dump_errno(s);
   end_header(s);
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 }
 
 int RGWCopyObj_REST_SWIFT::init_dest_policy()
@@ -481,7 +481,7 @@ send_data:
   if (get_data && !orig_ret) {
     CGI_PutStr(s, bl.c_str(), len);
   }
-  flush_formatter_to_req_state(s, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
 
   return 0;
 }