/* Copyright (c) 2008-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include "stripe.h" #include #include #include void stripe_local_wipe(stripe_local_t *local) { if (!local) goto out; loc_wipe(&local->loc); loc_wipe(&local->loc2); if (local->fd) fd_unref(local->fd); if (local->inode) inode_unref(local->inode); if (local->xattr) dict_unref(local->xattr); if (local->xdata) dict_unref(local->xdata); out: return; } int stripe_aggregate(dict_t *this, char *key, data_t *value, void *data) { dict_t *dst = NULL; int64_t *ptr = 0, *size = NULL; int32_t ret = -1; dst = data; if (strcmp(key, QUOTA_SIZE_KEY) == 0) { ret = dict_get_bin(dst, key, (void **)&size); if (ret < 0) { size = GF_CALLOC(1, sizeof(int64_t), gf_common_mt_char); if (size == NULL) { gf_log("stripe", GF_LOG_WARNING, "memory allocation failed"); goto out; } ret = dict_set_bin(dst, key, size, sizeof(int64_t)); if (ret < 0) { gf_log("stripe", GF_LOG_WARNING, "stripe aggregate dict set failed"); GF_FREE(size); goto out; } } ptr = data_to_bin(value); if (ptr == NULL) { gf_log("stripe", GF_LOG_WARNING, "data to bin failed"); goto out; } *size = hton64(ntoh64(*size) + ntoh64(*ptr)); } else if (strcmp(key, GF_CONTENT_KEY)) { /* No need to aggregate 'CONTENT' data */ ret = dict_set(dst, key, value); if (ret) gf_log("stripe", GF_LOG_WARNING, "xattr dict set failed"); } out: return 0; } void stripe_aggregate_xattr(dict_t *dst, dict_t *src) { if ((dst == NULL) || (src == NULL)) { goto out; } dict_foreach(src, stripe_aggregate, dst); out: return; } int32_t stripe_xattr_aggregate(char *buffer, stripe_local_t *local, int32_t *total) { int32_t i = 0; int32_t ret = -1; int32_t len = 0; char *sbuf = NULL; stripe_xattr_sort_t *xattr = NULL; if (!buffer || !local || !local->xattr_list) goto out; sbuf = buffer; for (i = 0; i < local->nallocs; i++) { xattr = local->xattr_list + i; len = xattr->xattr_len - 1; /* length includes \0 */ if (len && xattr && xattr->xattr_value) { memcpy(buffer, xattr->xattr_value, len); buffer += len; *buffer++ = ' '; } } *--buffer = '\0'; if (total) *total = buffer - sbuf; ret = 0; out: return ret; } int32_t stripe_free_xattr_str(stripe_local_t *local) { int32_t i = 0; int32_t ret = -1; stripe_xattr_sort_t *xattr = NULL; if (!local || !local->xattr_list) goto out; for (i = 0; i < local->nallocs; i++) { xattr = local->xattr_list + i; if (xattr && xattr->xattr_value) GF_FREE(xattr->xattr_value); } ret = 0; out: return ret; } int32_t stripe_fill_lockinfo_xattr(xlator_t *this, stripe_local_t *local, void **xattr_serz) { int32_t ret = -1, i = 0, len = 0; dict_t *tmp1 = NULL, *tmp2 = NULL; char *buf = NULL; stripe_xattr_sort_t *xattr = NULL; if (xattr_serz == NULL) { goto out; } tmp2 = dict_new(); if (tmp2 == NULL) { goto out; } for (i = 0; i < local->nallocs; i++) { xattr = local->xattr_list + i; len = xattr->xattr_len; if (len && xattr && xattr->xattr_value) { ret = dict_reset(tmp2); if (ret < 0) { gf_log(this->name, GF_LOG_DEBUG, "dict_reset failed (%s)", strerror(-ret)); } ret = dict_unserialize(xattr->xattr_value, xattr->xattr_len, &tmp2); if (ret < 0) { gf_log(this->name, GF_LOG_WARNING, "dict_unserialize failed (%s)", strerror(-ret)); ret = -1; goto out; } tmp1 = dict_copy(tmp2, tmp1); if (tmp1 == NULL) { gf_log(this->name, GF_LOG_WARNING, "dict_copy failed (%s)", strerror(-ret)); ret = -1; goto out; } } } len = dict_serialized_length(tmp1); if (len > 0) { buf = GF_CALLOC(1, len, gf_common_mt_dict_t); if (buf == NULL) { ret = -1; goto out; } ret = dict_serialize(tmp1, buf); if (ret < 0) { gf_log(this->name, GF_LOG_WARNING, "dict_serialize failed (%s)", strerror(-ret)); GF_FREE(buf); ret = -1; goto out; } *xattr_serz = buf; } ret = 0; out: if (tmp1 != NULL) { dict_unref(tmp1); } if (tmp2 != NULL) { dict_unref(tmp2); } return ret; } int32_t stripe_fill_pathinfo_xattr(xlator_t *this, stripe_local_t *local, char **xattr_serz) { int ret = -1; int32_t padding = 0; int32_t tlen = 0; int len = 0; char stripe_size_str[20] = { 0, }; char *pathinfo_serz = NULL; if (!local) { gf_log(this->name, GF_LOG_ERROR, "Possible NULL deref"); goto out; } len = snprintf(stripe_size_str, sizeof(stripe_size_str), "%" PRId64, local->fctx ? local->fctx->stripe_size : 0); if (len < 0 || len >= sizeof(stripe_size_str)) goto out; /* extra bytes for decorations (brackets and <>'s) */ padding = strlen(this->name) + SLEN(STRIPE_PATHINFO_HEADER) + len + 7; local->xattr_total_len += (padding + 2); pathinfo_serz = GF_MALLOC(local->xattr_total_len, gf_common_mt_char); if (!pathinfo_serz) goto out; /* xlator info */ (void)sprintf(pathinfo_serz, "(<" STRIPE_PATHINFO_HEADER "%s:[%s]> ", this->name, stripe_size_str); ret = stripe_xattr_aggregate(pathinfo_serz + padding, local, &tlen); if (ret) { gf_log(this->name, GF_LOG_ERROR, "Cannot aggregate pathinfo list"); GF_FREE(pathinfo_serz); goto out; } *(pathinfo_serz + padding + tlen) = ')'; *(pathinfo_serz + padding + tlen + 1) = '\0'; *xattr_serz = pathinfo_serz; ret = 0; out: return ret; } /** * stripe_get_matching_bs - Get the matching block size for the given path. */ int32_t stripe_get_matching_bs(const char *path, stripe_private_t *priv) { struct stripe_options *trav = NULL; uint64_t block_size = 0; GF_VALIDATE_OR_GOTO("stripe", priv, out); GF_VALIDATE_OR_GOTO("stripe", path, out); LOCK(&priv->lock); { block_size = priv->block_size; trav = priv->pattern; while (trav) { if (!fnmatch(trav->path_pattern, path, FNM_NOESCAPE)) { block_size = trav->block_size; break; } trav = trav->next; } } UNLOCK(&priv->lock); out: return block_size; } int32_t stripe_ctx_handle(xlator_t *this, call_frame_t *prev, stripe_local_t *local, dict_t *dict) { char key[256] = { 0, }; data_t *data = NULL; int32_t index = 0; stripe_private_t *priv = NULL; priv = this->private; if (!local->fctx) { local->fctx = GF_CALLOC(1, sizeof(stripe_fd_ctx_t), gf_stripe_mt_stripe_fd_ctx_t); if (!local->fctx) { local->op_errno = ENOMEM; local->op_ret = -1; goto out; } local->fctx->static_array = 0; } /* Stripe block size */ sprintf(key, "trusted.%s.stripe-size", this->name); data = dict_get(dict, key); if (!data) { local->xattr_self_heal_needed = 1; gf_log(this->name, GF_LOG_ERROR, "Failed to get stripe-size"); goto out; } else { if (!local->fctx->stripe_size) { local->fctx->stripe_size = data_to_int64(data); } if (local->fctx->stripe_size != data_to_int64(data)) { gf_log(this->name, GF_LOG_WARNING, "stripe-size mismatch in blocks"); local->xattr_self_heal_needed = 1; } } /* Stripe count */ sprintf(key, "trusted.%s.stripe-count", this->name); data = dict_get(dict, key); if (!data) { local->xattr_self_heal_needed = 1; gf_log(this->name, GF_LOG_ERROR, "Failed to get stripe-count"); goto out; } if (!local->fctx->xl_array) { local->fctx->stripe_count = data_to_int32(data); if (!local->fctx->stripe_count) { gf_log(this->name, GF_LOG_ERROR, "error with stripe-count xattr"); local->op_ret = -1; local->op_errno = EIO; goto out; } local->fctx->xl_array = GF_CALLOC(local->fctx->stripe_count, sizeof(xlator_t *), gf_stripe_mt_xlator_t); if (!local->fctx->xl_array) { local->op_errno = ENOMEM; local->op_ret = -1; goto out; } } if (local->fctx->stripe_count != data_to_int32(data)) { gf_log(this->name, GF_LOG_ERROR, "error with stripe-count xattr (%d != %d)", local->fctx->stripe_count, data_to_int32(data)); local->op_ret = -1; local->op_errno = EIO; goto out; } /* index */ sprintf(key, "trusted.%s.stripe-index", this->name); data = dict_get(dict, key); if (!data) { local->xattr_self_heal_needed = 1; gf_log(this->name, GF_LOG_ERROR, "Failed to get stripe-index"); goto out; } index = data_to_int32(data); if (index > priv->child_count) { gf_log(this->name, GF_LOG_ERROR, "error with stripe-index xattr (%d)", index); local->op_ret = -1; local->op_errno = EIO; goto out; } if (local->fctx->xl_array) { if (!local->fctx->xl_array[index]) local->fctx->xl_array[index] = prev->this; } sprintf(key, "trusted.%s.stripe-coalesce", this->name); data = dict_get(dict, key); if (!data) { /* * The file was probably created prior to coalesce support. * Assume non-coalesce mode for this file to maintain backwards * compatibility. */ gf_log(this->name, GF_LOG_DEBUG, "missing stripe-coalesce " "attr, assume non-coalesce mode"); local->fctx->stripe_coalesce = 0; } else { local->fctx->stripe_coalesce = data_to_int32(data); } out: return 0; } int32_t stripe_xattr_request_build(xlator_t *this, dict_t *dict, uint64_t stripe_size, uint32_t stripe_count, uint32_t stripe_index, uint32_t stripe_coalesce) { char key[256] = { 0, }; int32_t ret = -1; sprintf(key, "trusted.%s.stripe-size", this->name); ret = dict_set_int64(dict, key, stripe_size); if (ret) { gf_log(this->name, GF_LOG_WARNING, "failed to set %s in xattr_req dict", key); goto out; } sprintf(key, "trusted.%s.stripe-count", this->name); ret = dict_set_int32(dict, key, stripe_count); if (ret) { gf_log(this->name, GF_LOG_WARNING, "failed to set %s in xattr_req dict", key); goto out; } sprintf(key, "trusted.%s.stripe-index", this->name); ret = dict_set_int32(dict, key, stripe_index); if (ret) { gf_log(this->name, GF_LOG_WARNING, "failed to set %s in xattr_req dict", key); goto out; } sprintf(key, "trusted.%s.stripe-coalesce", this->name); ret = dict_set_int32(dict, key, stripe_coalesce); if (ret) { gf_log(this->name, GF_LOG_WARNING, "failed to set %s in xattr_req_dict", key); goto out; } out: return ret; } static int set_default_block_size(stripe_private_t *priv, char *num) { int ret = -1; GF_VALIDATE_OR_GOTO("stripe", THIS, out); GF_VALIDATE_OR_GOTO(THIS->name, priv, out); GF_VALIDATE_OR_GOTO(THIS->name, num, out); if (gf_string2bytesize_uint64(num, &priv->block_size) != 0) { gf_log(THIS->name, GF_LOG_ERROR, "invalid number format \"%s\"", num); goto out; } ret = 0; out: return ret; } int set_stripe_block_size(xlator_t *this, stripe_private_t *priv, char *data) { int ret = -1; char *tmp_str = NULL; char *tmp_str1 = NULL; char *dup_str = NULL; char *stripe_str = NULL; char *pattern = NULL; char *num = NULL; struct stripe_options *temp_stripeopt = NULL; struct stripe_options *stripe_opt = NULL; if (!this || !priv || !data) goto out; /* Get the pattern for striping. "option block-size *avi:10MB" etc */ stripe_str = strtok_r(data, ",", &tmp_str); while (stripe_str) { dup_str = gf_strdup(stripe_str); stripe_opt = GF_CALLOC(1, sizeof(struct stripe_options), gf_stripe_mt_stripe_options); if (!stripe_opt) { goto out; } pattern = strtok_r(dup_str, ":", &tmp_str1); num = strtok_r(NULL, ":", &tmp_str1); if (!num) { num = pattern; pattern = "*"; ret = set_default_block_size(priv, num); if (ret) goto out; } if (gf_string2bytesize_uint64(num, &stripe_opt->block_size) != 0) { gf_log(this->name, GF_LOG_ERROR, "invalid number format \"%s\"", num); goto out; } if (stripe_opt->block_size < STRIPE_MIN_BLOCK_SIZE) { gf_log(this->name, GF_LOG_ERROR, "Invalid Block-size: " "%s. Should be at least %llu bytes", num, STRIPE_MIN_BLOCK_SIZE); goto out; } if (stripe_opt->block_size % 512) { gf_log(this->name, GF_LOG_ERROR, "Block-size: %s should" " be a multiple of 512 bytes", num); goto out; } memcpy(stripe_opt->path_pattern, pattern, strlen(pattern)); gf_log(this->name, GF_LOG_DEBUG, "block-size : pattern %s : size %" PRId64, stripe_opt->path_pattern, stripe_opt->block_size); if (priv->pattern) temp_stripeopt = NULL; else temp_stripeopt = priv->pattern; stripe_opt->next = temp_stripeopt; priv->pattern = stripe_opt; stripe_opt = NULL; GF_FREE(dup_str); dup_str = NULL; stripe_str = strtok_r(NULL, ",", &tmp_str); } ret = 0; out: GF_FREE(dup_str); GF_FREE(stripe_opt); return ret; } int32_t stripe_iatt_merge(struct iatt *from, struct iatt *to) { if (to->ia_size < from->ia_size) to->ia_size = from->ia_size; if (to->ia_mtime < from->ia_mtime) to->ia_mtime = from->ia_mtime; if (to->ia_ctime < from->ia_ctime) to->ia_ctime = from->ia_ctime; if (to->ia_atime < from->ia_atime) to->ia_atime = from->ia_atime; return 0; } off_t coalesced_offset(off_t offset, uint64_t stripe_size, int stripe_count) { size_t line_size = 0; uint64_t stripe_num = 0; off_t coalesced_offset = 0; line_size = stripe_size * stripe_count; stripe_num = offset / line_size; coalesced_offset = (stripe_num * stripe_size) + (offset % stripe_size); return coalesced_offset; } off_t uncoalesced_size(off_t size, uint64_t stripe_size, int stripe_count, int stripe_index) { uint64_t nr_full_stripe_chunks = 0, mod = 0; if (!size) return size; /* * Estimate the number of fully written stripes from the * local file size. Each stripe_size chunk corresponds to * a stripe. */ nr_full_stripe_chunks = (size / stripe_size) * stripe_count; mod = size % stripe_size; if (!mod) { /* * There is no remainder, thus we could have overestimated * the size of the file in terms of chunks. Trim the number * of chunks by the following stripe members and leave it * up to those nodes to respond with a larger size (if * necessary). */ nr_full_stripe_chunks -= stripe_count - (stripe_index + 1); size = nr_full_stripe_chunks * stripe_size; } else { /* * There is a remainder and thus we own the last chunk of the * file. Add the preceding stripe members of the final stripe * along with the remainder to calculate the exact size. */ nr_full_stripe_chunks += stripe_index; size = nr_full_stripe_chunks * stripe_size + mod; } return size; }