<div dir="ltr"><br><div class="gmail_extra"><br><br><div class="gmail_quote">2013/12/12 Liu Yuan <span dir="ltr"><<a href="mailto:namei.unix@gmail.com" target="_blank">namei.unix@gmail.com</a>></span><br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="HOEnZb"><div class="h5">On Thu, Dec 12, 2013 at 06:15:55PM +0800, Robin Dong wrote:<br>
> From: Robin Dong <<a href="mailto:sanbai@taobao.com">sanbai@taobao.com</a>><br>
><br>
> Using hyper volume (size up to 16PB) to store large number of accounts<br>
> and containers.<br>
><br>
> Signed-off-by: Robin Dong <<a href="mailto:sanbai@taobao.com">sanbai@taobao.com</a>><br>
> ---<br>
> sheep/http/http.c | 5 +<br>
> sheep/http/http.h | 1 +<br>
> sheep/http/kv.c | 646 +++++++++++++++++++++++++++++++++++++++++++++++------<br>
> sheep/http/kv.h | 22 +-<br>
> sheep/http/s3.c | 6 +-<br>
> sheep/http/swift.c | 108 ++++++---<br>
> 6 files changed, 676 insertions(+), 112 deletions(-)<br>
><br>
> diff --git a/sheep/http/http.c b/sheep/http/http.c<br>
> index 04ef364..0081707 100644<br>
> --- a/sheep/http/http.c<br>
> +++ b/sheep/http/http.c<br>
> @@ -52,6 +52,7 @@ static inline const char *strstatus(enum http_status status)<br>
> [NO_CONTENT] = "204 No Content",<br>
> [PARTIAL_CONTENT] = "206 Partial Content",<br>
> [BAD_REQUEST] = "400 Bad Request",<br>
> + [UNAUTHORIZED] = "401 Unauthorized",<br>
> [NOT_FOUND] = "404 Not Found",<br>
> [METHOD_NOT_ALLOWED] = "405 Method Not Allowed",<br>
> [CONFLICT] = "409 Conflict",<br>
> @@ -192,6 +193,9 @@ void http_response_header(struct http_request *req, enum http_status status)<br>
><br>
> req->status = status;<br>
> http_request_writef(req, "Status: %s\r\n", strstatus(status));<br>
> + if (req->opcode == HTTP_GET && req->data_length > 0)<br>
> + http_request_writef(req, "Content-Length: %lu\r\n",<br>
> + req->data_length);<br>
> http_request_writes(req, "Content-type: text/plain;\r\n\r\n");<br>
> }<br>
><br>
> @@ -233,6 +237,7 @@ static void http_run_request(struct work *work)<br>
><br>
> if (method != NULL) {<br>
> method(req);<br>
> + sd_debug("req->status %d", req->status);<br>
> if (req->status != UNKNOWN)<br>
> goto out;<br>
> }<br>
> diff --git a/sheep/http/http.h b/sheep/http/http.h<br>
> index 046d412..a8527d1 100644<br>
> --- a/sheep/http/http.h<br>
> +++ b/sheep/http/http.h<br>
> @@ -32,6 +32,7 @@ enum http_status {<br>
> NO_CONTENT, /* 204 */<br>
> PARTIAL_CONTENT, /* 206 */<br>
> BAD_REQUEST, /* 400 */<br>
> + UNAUTHORIZED, /* 401 */<br>
> NOT_FOUND, /* 404 */<br>
> METHOD_NOT_ALLOWED, /* 405 */<br>
> CONFLICT, /* 409 */<br>
> diff --git a/sheep/http/kv.c b/sheep/http/kv.c<br>
> index 8113389..8d33e37 100644<br>
> --- a/sheep/http/kv.c<br>
> +++ b/sheep/http/kv.c<br>
> @@ -16,14 +16,30 @@<br>
><br>
> #define FOR_EACH_VDI(nr, vdis) FOR_EACH_BIT(nr, vdis, SD_NR_VDIS)<br>
><br>
> -static int lookup_bucket(struct http_request *req, const char *bucket,<br>
> - uint32_t *vid)<br>
> +struct bucket_inode_hdr {<br>
> + char bucket_name[SD_MAX_BUCKET_NAME];<br>
> + uint64_t obj_count;<br>
> + uint64_t bytes_used;<br>
> + uint32_t onode_vid;<br>
> +};<br>
> +<br>
> +struct bucket_inode {<br>
> + union {<br>
> + struct bucket_inode_hdr hdr;<br>
> + uint8_t data[SD_MAX_BUCKET_NAME << 1];<br>
> + };<br>
> +};<br>
> +<br>
> +#define MAX_BUCKETS (SD_MAX_VDI_SIZE / sizeof(struct bucket_inode))<br>
> +#define BUCKETS_PER_SD_OBJ (SD_DATA_OBJ_SIZE / sizeof(struct bucket_inode))<br>
> +<br>
> +static int lookup_vdi(const char *name, uint32_t *vid)<br>
> {<br>
> int ret;<br>
> struct vdi_info info = {};<br>
> struct vdi_iocb iocb = {<br>
> - .name = bucket,<br>
> - .data_len = strlen(bucket),<br>
> + .name = name,<br>
> + .data_len = strlen(name),<br>
> };<br>
><br>
> ret = vdi_lookup(&iocb, &info);<br>
> @@ -32,27 +48,23 @@ static int lookup_bucket(struct http_request *req, const char *bucket,<br>
> *vid = info.vid;<br>
> break;<br>
> case SD_RES_NO_VDI:<br>
> - sd_info("no such bucket %s", bucket);<br>
> - http_response_header(req, NOT_FOUND);<br>
> - return -1;<br>
> + sd_info("no such vdi %s", name);<br>
> + break;<br>
> default:<br>
> - sd_err("%s: bucket %s", sd_strerror(ret), bucket);<br>
> - http_response_header(req, INTERNAL_SERVER_ERROR);<br>
> - return -1;<br>
> + sd_err("Failed to find vdi %s %s", name, sd_strerror(ret));<br>
> }<br>
><br>
> - return 0;<br>
> + return ret;<br>
> }<br>
><br>
> -/* Bucket operations */<br>
> -<br>
> -int kv_create_bucket(struct http_request *req, const char *bucket)<br>
> +static int kv_create_hyper_volume(const char *name, uint32_t *vdi_id)<br>
> {<br>
> struct sd_req hdr;<br>
> + struct sd_rsp *rsp = (struct sd_rsp *)&hdr;<br>
> int ret;<br>
> char buf[SD_MAX_VDI_LEN] = {0};<br>
><br>
> - pstrcpy(buf, SD_MAX_VDI_LEN, bucket);<br>
> + pstrcpy(buf, SD_MAX_VDI_LEN, name);<br>
><br>
> sd_init_req(&hdr, SD_OP_NEW_VDI);<br>
> hdr.flags = SD_FLAG_CMD_WRITE;<br>
> @@ -64,104 +76,592 @@ int kv_create_bucket(struct http_request *req, const char *bucket)<br>
> hdr.vdi.store_policy = 1;<br>
><br>
> ret = exec_local_req(&hdr, buf);<br>
> + if (rsp->result != SD_RES_SUCCESS)<br>
> + sd_err("Failed to create VDI %s: %s", name,<br>
> + sd_strerror(rsp->result));<br>
> +<br>
> + if (vdi_id)<br>
> + *vdi_id = rsp->vdi.vdi_id;<br>
> +<br>
> + return ret;<br>
> +}<br>
> +<br>
> +static int discard_data_obj(uint64_t oid)<br>
> +{<br>
> + int ret;<br>
> + struct sd_req hdr;<br>
> +<br>
> + sd_init_req(&hdr, SD_OP_DISCARD_OBJ);<br>
> + hdr.obj.oid = oid;<br>
> +<br>
> + ret = exec_local_req(&hdr, NULL);<br>
> + if (ret != SD_RES_SUCCESS)<br>
> + sd_err("Failed to discard data obj %lu %s", oid,<br>
> + sd_strerror(ret));<br>
> +<br>
> + return ret;<br>
> +}<br>
> +<br>
> +static int kv_delete_vdi(const char *name)<br>
> +{<br>
> + int ret;<br>
> + struct sd_req hdr;<br>
> + char data[SD_MAX_VDI_LEN] = {0};<br>
> + uint32_t vid;<br>
> +<br>
> + ret = lookup_vdi(name, &vid);<br>
> + if (ret != SD_RES_SUCCESS)<br>
> + return ret;<br>
> +<br>
> + sd_init_req(&hdr, SD_OP_DEL_VDI);<br>
> + hdr.flags = SD_FLAG_CMD_WRITE;<br>
> + hdr.data_length = sizeof(data);<br>
> + pstrcpy(data, SD_MAX_VDI_LEN, name);<br>
> +<br>
> + ret = exec_local_req(&hdr, data);<br>
> + if (ret != SD_RES_SUCCESS)<br>
> + sd_err("Failed to delete vdi %s %s", name, sd_strerror(ret));<br>
> +<br>
> + return ret;<br>
> +}<br>
> +<br>
> +/*<br>
> + * An account is actually a hyper volume vdi (up to 16PB),<br>
> + * all the buckets (or containers, identified by 'struct bucket_inode') are<br>
> + * stores in this hyper vdi using hashing algorithm.<br>
> + * The bucket also has a hyper vdi named "account/bucket" which stores<br>
> + * 'struct kv_onodes'.<br>
> + *<br>
> + * For example: account "coly" has two buckets "jetta" and "volvo"<br>
> + *<br>
> + *<br>
> + * account vdi<br>
> + * +-----------+---+--------------------------+---+--------------------------+--<br>
> + * |name: coly |...|bucket_inode (name: jetta)|...|bucket_inode (name: volvo)|..<br>
> + * +-----------+---+--------------------------+---+--------------------------+--<br>
> + * | |<br>
> + * / |<br>
> + * bucket vdi / |<br>
> + * +-----------------+-------+ <-- |<br>
> + * |name: coly/jetta |.......| |<br>
> + * +-----------------+-------+ /<br>
> + * bucket vdi /<br>
> + * +-----------------+------+ <----<br>
> + * | name: coly/volvo|......|<br>
> + * +-----------------+------+<br>
> + */<br>
> +<br>
> +/* Account operations */<br>
> +<br>
> +int kv_create_account(const char *account)<br>
> +{<br>
> + uint32_t vdi_id;<br>
> + return kv_create_hyper_volume(account, &vdi_id);<br>
> +}<br>
> +<br>
> +typedef void (*list_cb)(struct http_request *req, const char *bucket,<br>
> + void *opaque);<br>
> +<br>
> +struct list_buckets_arg {<br>
> + struct http_request *req;<br>
> + void *opaque;<br>
> + list_cb cb;<br>
> + uint32_t bucket_counter;<br>
> +};<br>
> +<br>
> +static void list_buckets_cb(void *data, enum btree_node_type type, void *arg)<br>
> +{<br>
> + struct sd_extent *ext;<br>
> + struct list_buckets_arg *lbarg = arg;<br>
> + struct bucket_inode *bnode;<br>
> + uint64_t oid;<br>
> + char *buf = NULL;<br>
> + int ret;<br>
> +<br>
> + if (type == BTREE_EXT) {<br>
> + ext = (struct sd_extent *)data;<br>
> + if (!ext->vdi_id)<br>
> + return;<br>
> +<br>
> + buf = xzalloc(SD_DATA_OBJ_SIZE);<br>
> +<br>
> + oid = vid_to_data_oid(ext->vdi_id, ext->idx);<br>
> + ret = sd_read_object(oid, buf, SD_DATA_OBJ_SIZE, 0);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to read data object %lx", oid);<br>
> + goto out;<br>
> + }<br>
> + /* loop all bucket_inodes in this data-object */<br>
> + for (int i = 0; i < BUCKETS_PER_SD_OBJ; i++) {<br>
> + bnode = (struct bucket_inode *)<br>
> + (buf + i * sizeof(struct bucket_inode));<br>
> + if (bnode->hdr.onode_vid == 0)<br>
> + continue;<br>
> + if (lbarg->cb)<br>
> + lbarg->cb(lbarg->req, bnode->hdr.bucket_name,<br>
> + (void *)lbarg->opaque);<br>
> + lbarg->bucket_counter++;<br>
> + }<br>
> + }<br>
> +out:<br>
> + free(buf);<br>
> +}<br>
> +<br>
> +/* get number of buckets in this account */<br>
> +static int kv_get_account(const char *account, uint32_t *nr_buckets)<br>
> +{<br>
> + struct sd_inode inode;<br>
> + uint64_t oid;<br>
> + uint32_t account_vid;<br>
> + int ret;<br>
> +<br>
> + ret = lookup_vdi(account, &account_vid);<br>
> + if (ret != SD_RES_SUCCESS)<br>
> + return ret;<br>
> +<br>
> + /* read account vdi out */<br>
> + oid = vid_to_vdi_oid(account_vid);<br>
> + ret = sd_read_object(oid, (char *)&inode, sizeof(struct sd_inode), 0);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to read inode header %lx", oid);<br>
> + return ret;<br>
> + }<br>
> +<br>
> + struct list_buckets_arg arg = {NULL, NULL, NULL, 0};<br>
> + traverse_btree(sheep_bnode_reader, &inode, list_buckets_cb, &arg);<br>
> + if (nr_buckets)<br>
> + *nr_buckets = arg.bucket_counter;<br>
> +<br>
> + return SD_RES_SUCCESS;<br>
> +}<br>
> +<br>
> +int kv_read_account(const char *account, uint32_t *nr_buckets)<br>
> +{<br>
> + int ret;<br>
> +<br>
> + ret = kv_get_account(account, nr_buckets);<br>
> + if (ret != SD_RES_SUCCESS)<br>
> + sd_err("Failed to get number of buckets in %s", account);<br>
> + return ret;<br>
> +}<br>
> +<br>
> +int kv_update_account(const char *account)<br>
> +{<br>
> + /* TODO: update metadata of the account */<br>
> + return -1;<br>
> +}<br>
> +<br>
> +int kv_delete_account(const char *account)<br>
> +{<br>
> + int ret;<br>
> +<br>
> + ret = kv_delete_vdi(account);<br>
> + if (ret != SD_RES_SUCCESS)<br>
> + sd_err("Failed to delete vdi %s", account);<br>
> +<br>
> + return ret;<br>
> +}<br>
> +<br>
> +/* Bucket operations */<br>
> +<br>
> +static int lookup_bucket(struct http_request *req, const char *bucket,<br>
> + uint32_t *vid)<br>
> +{<br>
> + int ret;<br>
> + struct vdi_info info = {};<br>
> + struct vdi_iocb iocb = {<br>
> + .name = bucket,<br>
> + .data_len = strlen(bucket),<br>
> + };<br>
> +<br>
> + ret = vdi_lookup(&iocb, &info);<br>
> switch (ret) {<br>
> case SD_RES_SUCCESS:<br>
> - http_response_header(req, CREATED);<br>
> + *vid = info.vid;<br>
> break;<br>
> - case SD_RES_VDI_EXIST:<br>
> - http_response_header(req, ACCEPTED);<br>
> + case SD_RES_NO_VDI:<br>
> + sd_info("no such bucket %s", bucket);<br>
> + http_response_header(req, NOT_FOUND);<br>
> break;<br>
> default:<br>
> - sd_err("%s: bucket %s", sd_strerror(ret), bucket);<br>
> + sd_err("Failed to find bucket %s %s", bucket, sd_strerror(ret));<br>
> http_response_header(req, INTERNAL_SERVER_ERROR);<br>
> - return -1;<br>
> }<br>
><br>
> - return 0;<br>
> + return ret;<br>
> }<br>
><br>
> -int kv_read_bucket(struct http_request *req, const char *bucket)<br>
> +/*<br>
> + * Delete bucket(container) inode in account vdi.<br>
> + * idx: the target hash positon of bucket<br>
> + * Return the position of bucket_inode in sd-data-object if success<br>
> + * Return BUCKETS_PER_SD_OBJ if bucket_inode is not found<br>
> + * Return -1 if some errors happend<br>
> + */<br>
> +static int delete_bucket(struct sd_inode *account_inode, uint64_t idx,<br>
> + const char *bucket)<br>
> {<br>
> - /* TODO: read metadata of the bucket */<br>
> - return -1;<br>
> + struct bucket_inode *bnode;<br>
> + char *buf = NULL;<br>
> + uint32_t vdi_id;<br>
> + uint64_t oid;<br>
> + uint64_t data_index = idx / BUCKETS_PER_SD_OBJ;<br>
> + int offset = idx % BUCKETS_PER_SD_OBJ;<br>
> + int ret, i, empty_buckets = 0, found = 0;<br>
> +<br>
> + vdi_id = INODE_GET_VID(account_inode, data_index);<br>
> + if (!vdi_id) {<br>
> + sd_err("the %lu in vdi %s is not exists", data_index,<br>
> + account_inode->name);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> +<br>
> + oid = vid_to_data_oid(account_inode->vdi_id, data_index);<br>
> + buf = xzalloc(SD_DATA_OBJ_SIZE);<br>
> + ret = sd_read_object(oid, buf, SD_DATA_OBJ_SIZE, 0);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to read inode header %lx", oid);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> +<br>
> + for (i = 0; i < BUCKETS_PER_SD_OBJ; i++) {<br>
> + char vdi_name[SD_MAX_VDI_LEN];<br>
> + bnode = (struct bucket_inode *)<br>
> + (buf + i * sizeof(struct bucket_inode));<br>
> + /* count all empty buckets in this sd-data-obj */<br>
> + if (bnode->hdr.onode_vid == 0) {<br>
> + empty_buckets++;<br>
> + continue;<br>
> + }<br>
> + if (strncmp(bnode->hdr.bucket_name, bucket, SD_MAX_BUCKET_NAME))<br>
> + continue;<br>
> +<br>
> + if (i < offset)<br>
> + panic("postion of bucket inode %d is smaller than %d",<br>
> + i, offset);<br>
> +<br>
> + found = i;<br>
> + /* find the bnode */<br>
> + bnode->hdr.onode_vid = 0;<br>
> + snprintf(vdi_name, SD_MAX_VDI_LEN, "%s/%s",<br>
> + account_inode->name, bucket);<br>
> +<br>
> + ret = kv_delete_vdi(vdi_name);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to delete vdi %s", vdi_name);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> + sd_debug("delete vdi %s success", vdi_name);<br>
> + }<br>
> +<br>
> + if (!found) {<br>
> + ret = BUCKETS_PER_SD_OBJ;<br>
> + goto out;<br>
> + }<br>
> +<br>
> + /*<br>
> + * if only this bucket_inode is in the sd-data-obj,<br>
> + * then delete this sd-data-obj<br>
> + */<br>
> + if (empty_buckets == BUCKETS_PER_SD_OBJ - 1) {<br>
> + ret = discard_data_obj(oid);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> + INODE_SET_VID(account_inode, data_index, 0);<br>
> + ret = sd_inode_write_vid(sheep_bnode_writer, account_inode,<br>
> + data_index, vdi_id, vdi_id, 0, false,<br>
> + false);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to write inode %x", vdi_id);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> + sd_debug("discard obj %lx and update vdi %x success",<br>
> + oid, vdi_id);<br>
> + } else {<br>
> + ret = sd_write_object(oid, buf, sizeof(struct bucket_inode),<br>
> + i * sizeof(struct bucket_inode), false);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to write object %lx", oid);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> + }<br>
> +<br>
> + sd_debug("write object oid %lx success", oid);<br>
> + ret = found;<br>
> +out:<br>
> + free(buf);<br>
> + return ret;<br>
> }<br>
><br>
> -int kv_update_bucket(struct http_request *req, const char *bucket)<br>
> +/*<br>
> + * Add bucket(container) inode into account vdi.<br>
> + * idx: the target hash positon of bucket<br>
> + * Return the position of bucket_inode in sd-data-object if success<br>
> + * Return BUCKETS_PER_SD_OBJ if the data-object is full of bucket_inode<br>
> + * Return -1 if some error happend<br>
> + */<br>
> +static int add_bucket(struct sd_inode *account_inode, uint64_t idx,<br>
> + const char *bucket)<br>
> {<br>
> - /* TODO: update metadata of the bucket */<br>
> - return -1;<br>
> + struct bucket_inode *bnode;<br>
> + char *buf = NULL;<br>
> + uint32_t vdi_id;<br>
> + uint64_t oid;<br>
> + uint64_t data_index = idx / BUCKETS_PER_SD_OBJ;<br>
> + int offset = idx % BUCKETS_PER_SD_OBJ;<br>
> + int ret, i;<br>
> + bool create = false;<br>
> +<br>
> + buf = xzalloc(SD_DATA_OBJ_SIZE);<br>
> +<br>
> + vdi_id = INODE_GET_VID(account_inode, data_index);<br>
> + oid = vid_to_data_oid(account_inode->vdi_id, data_index);<br>
> + sd_debug("oid %x %lx %lx", account_inode->vdi_id, data_index, oid);<br>
> + /* the data object is exists */<br>
> + if (vdi_id) {<br>
> + ret = sd_read_object(oid, buf, SD_DATA_OBJ_SIZE, 0);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to read inode header %lx", oid);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> + } else<br>
> + create = true;<br>
> +<br>
> + sd_debug("bucket_inode offset %d %lu", offset, BUCKETS_PER_SD_OBJ);<br>
> + for (i = offset; i < BUCKETS_PER_SD_OBJ; i++) {<br>
> + char vdi_name[SD_MAX_VDI_LEN];<br>
> + bnode = (struct bucket_inode *)<br>
> + (buf + i * sizeof(struct bucket_inode));<br>
> + if (bnode->hdr.onode_vid != 0)<br>
> + continue;<br>
> +<br>
> + /* the bnode not used */<br>
> + strncpy(bnode->hdr.bucket_name, bucket, SD_MAX_BUCKET_NAME);<br>
> + bnode->hdr.obj_count = 0;<br>
> + bnode->hdr.bytes_used = 0;<br>
> + snprintf(vdi_name, SD_MAX_VDI_LEN, "%s/%s",<br>
> + account_inode->name, bucket);<br>
> + ret = kv_create_hyper_volume(vdi_name, &(bnode->hdr.onode_vid));<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to create hyper volume %d", ret);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> + sd_debug("create hyper volume %s success", vdi_name);<br>
> + break;<br>
> + }<br>
> +<br>
> + if (i >= BUCKETS_PER_SD_OBJ) {<br>
> + ret = BUCKETS_PER_SD_OBJ;<br>
> + goto out;<br>
> + }<br>
> +<br>
> + /* write bnode back to account-vdi */<br>
> + if (create)<br>
> + ret = sd_write_object(oid, buf, SD_DATA_OBJ_SIZE, 0, create);<br>
> + else<br>
> + ret = sd_write_object(oid, buf, sizeof(struct bucket_inode),<br>
> + i * sizeof(struct bucket_inode), create);<br>
> +<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to write object %lx", oid);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> +<br>
> + sd_debug("write object oid %lx success", oid);<br>
> +<br>
> + /* update index of vdi */<br>
> + if (create) {<br>
> + vdi_id = account_inode->vdi_id;<br>
> + INODE_SET_VID(account_inode, data_index, vdi_id);<br>
> + ret = sd_inode_write_vid(sheep_bnode_writer, account_inode,<br>
> + data_index, vdi_id, vdi_id, 0, false,<br>
> + false);<br>
> + if (ret != SD_RES_SUCCESS) {<br>
> + sd_err("Failed to write inode %x", vdi_id);<br>
> + ret = -1;<br>
> + goto out;<br>
> + }<br>
> + sd_debug("write account inode success");<br>
> + }<br>
> +<br>
> + ret = i;<br>
> +out:<br>
> + free(buf);<br>
> + return ret;<br>
> }<br>
><br>
> -/* TODO: return HTTP_CONFLICT when the bucket is not empty */<br>
> -int kv_delete_bucket(struct http_request *req, const char *bucket)<br>
> +static int kv_get_bucket(struct sd_inode *account_inode, uint32_t account_vid,<br>
> + const char *account, const char *bucket)<br>
<br>
</div></div>account_inode has the vid, so we can remove extra account_vid from parameter<br></blockquote><div><br></div><div>No, we can't, the account_inode passed in is empty.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Thanks<br>
<span class="HOEnZb"><font color="#888888">Yuan<br>
</font></span></blockquote></div><br><br clear="all"><div><br></div>-- <br>--<br>Best Regard<br>Robin Dong
</div></div>