summaryrefslogtreecommitdiffstats
path: root/xlators/features/bit-rot/src/bitd/bit-rot-scrub.c
diff options
context:
space:
mode:
authorVenky Shankar <vshankar@redhat.com>2015-03-13 21:23:20 +0530
committerVijay Bellur <vbellur@redhat.com>2015-03-24 10:55:51 -0700
commit866c64ba5e29a90b37fa051061a58300ae129a2c (patch)
tree0cf2ea60032108c9b36a98e06406e971ee817286 /xlators/features/bit-rot/src/bitd/bit-rot-scrub.c
parent7927e8747c731dbb105e93ae66c336338f48f0e6 (diff)
features/bit-rot: filesystem scrubber
Scrubber performs signature verification for objects that were signed by signer. This is done by recalculating the signature (using the hash algorithm the object was signed with) and verifying it aginst the objects persisted signature. Since the object could be undergoing IO opretaion at the time of hash calculation, the signature may not match objects persisted signature. Bitrot stub provides additional information about the stalesness of an objects signature (determinted by it's versioning mechanism). This additional bit of information is used by scrubber to determine the staleness of the signature, and in such cases the object is skipped verification (although signature staleness is performed twice: once before initiation of hash calculation and another after it (an object could be modified after staleness checks). The implmentation is a part of the bitrot xlator (signer) which acts as a signer or scrubber based on a translator option. As of now the scrub process is ever running (but has some form of weak throttling mechanism during filesystem scan). Going forward, there needs to be some form of scrub scheduling and IO throttling (during hash calculation) tunables (via CLI). Change-Id: I665ce90208f6074b98c5a1dd841ce776627cc6f9 BUG: 1170075 Original-Author: Raghavendra Bhat <rabhat@redhat.com> Original-Author: Venky Shankar <vshankar@redhat.com> Signed-off-by: Venky Shankar <vshankar@redhat.com> Reviewed-on: http://review.gluster.org/9914 Tested-by: Vijay Bellur <vbellur@redhat.com> Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'xlators/features/bit-rot/src/bitd/bit-rot-scrub.c')
-rw-r--r--xlators/features/bit-rot/src/bitd/bit-rot-scrub.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/xlators/features/bit-rot/src/bitd/bit-rot-scrub.c b/xlators/features/bit-rot/src/bitd/bit-rot-scrub.c
new file mode 100644
index 00000000000..1712aa1529b
--- /dev/null
+++ b/xlators/features/bit-rot/src/bitd/bit-rot-scrub.c
@@ -0,0 +1,291 @@
+/*
+ Copyright (c) 2015 Red Hat, Inc. <http://www.redhat.com>
+ 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.
+*/
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include <ctype.h>
+#include <sys/uio.h>
+
+#include "glusterfs.h"
+#include "xlator.h"
+#include "logging.h"
+
+#include "bit-rot.h"
+#include "bit-rot-scrub.h"
+#include <pthread.h>
+
+static inline int32_t
+bitd_fetch_signature (xlator_t *this,
+ br_child_t *child, fd_t *fd, br_isignature_out_t *sign)
+{
+ int32_t ret = -1;
+ dict_t *xattr = NULL;
+ br_isignature_out_t *sigptr = NULL;
+
+ ret = syncop_fgetxattr (child->xl, fd, &xattr,
+ GLUSTERFS_GET_OBJECT_SIGNATURE, NULL);
+ if (ret < 0) {
+ br_log_object (this, "getxattr", fd->inode->gfid, -ret);
+ goto out;
+ }
+
+ ret = dict_get_ptr (xattr, GLUSTERFS_GET_OBJECT_SIGNATURE,
+ (void **)&sigptr);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "failed to extract signature info [GFID: %s]",
+ uuid_utoa (fd->inode->gfid));
+ goto unref_dict;
+ }
+
+ ret = 0;
+ (void) memcpy (sign, sigptr, sizeof (br_isignature_out_t));
+
+ unref_dict:
+ dict_unref (xattr);
+ out:
+ return ret;
+
+}
+
+static inline int32_t
+bitd_scrub_post_compute_check (xlator_t *this,
+ br_child_t *child,
+ br_isignature_out_t *sign, fd_t *fd)
+{
+ int32_t ret = 0;
+
+ ret = bitd_fetch_signature (this, child, fd, sign);
+ if (ret)
+ goto out;
+ if (sign->stale)
+ ret = -1;
+
+ out:
+ return ret;
+
+}
+
+static inline int32_t
+bitd_scrub_pre_compute_check (xlator_t *this, br_child_t *child, fd_t *fd)
+{
+ int32_t ret = -1;
+ br_isignature_out_t sign = {0,};
+
+ /* if the object is already marked bad, don't bother checking */
+ if (bitd_is_bad_file (this, child, NULL, fd))
+ goto out;
+
+ /* else, check for signature staleness */
+ ret = bitd_fetch_signature (this, child, fd, &sign);
+ if (ret)
+ goto out;
+ if (sign.stale) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = 0;
+
+ out:
+ return ret;
+}
+
+static inline int
+bitd_compare_ckum (xlator_t *this,
+ br_isignature_out_t *sign,
+ unsigned char *md, inode_t *linked_inode,
+ gf_dirent_t *entry, fd_t *fd, br_child_t *child)
+{
+ int ret = -1;
+ dict_t xattr = {0,};
+
+ GF_VALIDATE_OR_GOTO ("bit-rot", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, sign, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, child, out);
+ GF_VALIDATE_OR_GOTO (this->name, linked_inode, out);
+ GF_VALIDATE_OR_GOTO (this->name, md, out);
+ GF_VALIDATE_OR_GOTO (this->name, entry, out);
+
+ if (strncmp (sign->signature, (char *)md, strlen (sign->signature))) {
+ gf_log (this->name, GF_LOG_WARNING, "checksums does not match "
+ "for the entry %s (gfid: %s)", entry->d_name,
+ uuid_utoa (linked_inode->gfid));
+ ret = dict_set_int32 (&xattr, "trusted.glusterfs.bad-file",
+ _gf_true);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "dict-set for "
+ "bad-file (entry: %s, gfid: %s) failed",
+ entry->d_name, uuid_utoa (linked_inode->gfid));
+ goto out;
+ }
+
+ ret = syncop_fsetxattr (child->xl, fd, &xattr, 0);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "setxattr to mark "
+ "the file %s (gfid: %s) as bad failed",
+ entry->d_name, uuid_utoa (linked_inode->gfid));
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * This is the scrubber. As of now there's a mix of fd and inode
+ * operations. Better to move them to fd based to be clean and
+ * avoid code cluttering.
+ */
+int
+bitd_start_scrub (xlator_t *subvol,
+ gf_dirent_t *entry, loc_t *parent, void *data)
+{
+ int32_t ret = -1;
+ fd_t *fd = NULL;
+ loc_t loc = {0, };
+ struct iatt iatt = {0, };
+ struct iatt parent_buf = {0, };
+ pid_t pid = 0;
+ br_child_t *child = NULL;
+ xlator_t *this = NULL;
+ unsigned char *md = NULL;
+ inode_t *linked_inode = NULL;
+ br_isignature_out_t sign = {0,};
+
+ GF_VALIDATE_OR_GOTO ("bit-rot", subvol, out);
+ GF_VALIDATE_OR_GOTO ("bit-rot", data, out);
+
+ child = data;
+ this = child->this;
+
+ pid = GF_CLIENT_PID_SCRUB;
+
+ ret = br_prepare_loc (this, child, parent, entry, &loc);
+ if (!ret)
+ goto out;
+
+ syncopctx_setfspid (&pid);
+
+ ret = syncop_lookup (child->xl, &loc, NULL, &iatt, NULL, &parent_buf);
+ if (ret) {
+ br_log_object_path (this, "lookup", loc.path, -ret);
+ goto out;
+ }
+
+ linked_inode = inode_link (loc.inode, parent->inode, loc.name, &iatt);
+ if (linked_inode)
+ inode_lookup (linked_inode);
+
+ if (iatt.ia_type != IA_IFREG) {
+ gf_log (this->name, GF_LOG_DEBUG, "%s is not a regular "
+ "file", entry->d_name);
+ ret = 0;
+ goto unref_inode;
+ }
+
+ /**
+ * open() an fd for subsequent opertaions
+ */
+ fd = fd_create (linked_inode, 0);
+ if (!fd) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to create fd for "
+ "inode %s", uuid_utoa (linked_inode->gfid));
+ goto unref_inode;
+ }
+
+ ret = syncop_open (child->xl, &loc, O_RDWR, fd);
+ if (ret) {
+ br_log_object (this, "open", linked_inode->gfid, -ret);
+ ret = -1;
+ goto unrefd;
+ }
+
+ fd_bind (fd);
+
+ /**
+ * perform pre compute checks before initiating checksum
+ * computation
+ * - presence of bad object
+ * - signature staleness
+ */
+ ret = bitd_scrub_pre_compute_check (this, child, fd);
+ if (ret)
+ goto unrefd; /* skip this object */
+
+ /* if all's good, proceed to calculate the hash */
+ md = GF_CALLOC (SHA256_DIGEST_LENGTH, sizeof (*md),
+ gf_common_mt_char);
+ if (!md)
+ goto unrefd;
+
+ ret = br_calculate_obj_checksum (md, child, fd, &iatt);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "error calculating hash "
+ "for object [GFID: %s]", uuid_utoa (fd->inode->gfid));
+ ret = -1;
+ goto free_md;
+ }
+
+ /**
+ * perform post compute checks as an object's signature may have
+ * become stale while scrubber calculated checksum.
+ */
+ ret = bitd_scrub_post_compute_check (this, child, &sign, fd);
+ if (ret)
+ goto free_md;
+
+ ret = bitd_compare_ckum (this, &sign, md,
+ linked_inode, entry, fd, child);
+
+ /** fd_unref() takes care of closing fd.. like syncop_close() */
+
+ free_md:
+ GF_FREE (md);
+ unrefd:
+ fd_unref (fd);
+ unref_inode:
+ inode_unref (linked_inode);
+ out:
+ loc_wipe (&loc);
+ return ret;
+}
+
+#define BR_SCRUB_THROTTLE_COUNT 10
+#define BR_SCRUB_THROTTLE_ZZZ 100
+void *
+br_scrubber (void *arg)
+{
+ loc_t loc = {0,};
+ xlator_t *this = NULL;
+ br_child_t *child = NULL;
+
+ child = arg;
+ this = child->this;
+
+ THIS = this;
+
+ loc.inode = child->table->root;
+ while (1) {
+ (void) syncop_ftw_throttle
+ (child->xl, &loc,
+ GF_CLIENT_PID_SCRUB, child, bitd_start_scrub,
+ BR_SCRUB_THROTTLE_COUNT, BR_SCRUB_THROTTLE_ZZZ);
+
+ sleep (BR_SCRUB_THROTTLE_ZZZ * BR_SCRUB_THROTTLE_COUNT);
+ }
+
+ return NULL;
+}