summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaghavendra Bhat <raghavendra@redhat.com>2018-04-20 14:54:33 -0400
committerAmar Tumballi <amarts@redhat.com>2018-05-04 11:13:57 +0000
commitb42a048d3a76c7f377399f18d30f0a8a930f9d05 (patch)
treeb400225db569fdd1b3251105024bec73690da175
parentbef654f48c14bfd7ce20702edff41052f6f54bdc (diff)
features/bitrot: print the path of the corrupted objects
Currently "gluster volume bitrot <volume name> scrub status" gives the list of the corrupted objects (files as of now). But only the gfids of those corrupted objects are seen and one has to do getfattr, find etc operations to get the actual path of those objects for removal etc. This change makes an attempt to print the path of those files as much as possible. * Try to get the path using the on disk gfid2path xattr. * If the above operation fails, then go for in memory path (provided that the object has its dentry properly created and linked in the inode table of the brick where the corrupted object is present) So the gfid to path resolution is a soft resolution, i.e. based on the inode and dentry cache in the brick's memory. If the path cannot be obtained via inode table also, then only gfid is printed. Change-Id: Ie9a30307f43a49a2a9225821803c7d40d231de68 fixes: bz#1570962 Signed-off-by: Raghavendra Bhat <raghavendra@redhat.com>
-rw-r--r--libglusterfs/src/libglusterfs.sym1
-rw-r--r--libglusterfs/src/syncop-utils.c42
-rw-r--r--libglusterfs/src/syncop-utils.h5
-rw-r--r--xlators/features/bit-rot/src/bitd/bit-rot-scrub.c20
-rw-r--r--xlators/features/bit-rot/src/stub/bit-rot-stub-helpers.c218
-rw-r--r--xlators/features/bit-rot/src/stub/bit-rot-stub-messages.h4
-rw-r--r--xlators/features/bit-rot/src/stub/bit-rot-stub.h14
7 files changed, 292 insertions, 12 deletions
diff --git a/libglusterfs/src/libglusterfs.sym b/libglusterfs/src/libglusterfs.sym
index d2d25838647..8b2567a4c62 100644
--- a/libglusterfs/src/libglusterfs.sym
+++ b/libglusterfs/src/libglusterfs.sym
@@ -944,6 +944,7 @@ syncop_fxattrop
syncop_getactivelk
syncop_getxattr
syncop_gfid_to_path
+syncop_gfid_to_path_hard
syncop_inode_find
syncop_inodelk
syncop_entrylk
diff --git a/libglusterfs/src/syncop-utils.c b/libglusterfs/src/syncop-utils.c
index b743bdfae88..40ced03cb45 100644
--- a/libglusterfs/src/syncop-utils.c
+++ b/libglusterfs/src/syncop-utils.c
@@ -554,9 +554,20 @@ out:
return ret;
}
+/**
+ * For hard resove, it it telling posix to make use of the
+ * gfid2path extended attribute stored on disk. Otherwise
+ * posix xlator (with GFID_TO_PATH_KEY as the key) will just
+ * do a in memory inode_path to get the path. Depending upon
+ * the consumer of this function, they can choose how they want
+ * to proceed. If doing a xattr operation sounds costly, then
+ * use GFID_TO_PATH_KEY as the key for getxattr.
+ **/
+
int
-syncop_gfid_to_path (inode_table_t *itable, xlator_t *subvol, uuid_t gfid,
- char **path_p)
+syncop_gfid_to_path_hard (inode_table_t *itable, xlator_t *subvol, uuid_t gfid,
+ inode_t *inode, char **path_p,
+ gf_boolean_t hard_resolve)
{
int ret = 0;
char *path = NULL;
@@ -564,14 +575,25 @@ syncop_gfid_to_path (inode_table_t *itable, xlator_t *subvol, uuid_t gfid,
dict_t *xattr = NULL;
gf_uuid_copy (loc.gfid, gfid);
- loc.inode = inode_new (itable);
- ret = syncop_getxattr (subvol, &loc, &xattr, GFID_TO_PATH_KEY, NULL,
- NULL);
+ if (!inode)
+ loc.inode = inode_new (itable);
+ else
+ loc.inode = inode_ref (inode);
+
+ if (!hard_resolve)
+ ret = syncop_getxattr (subvol, &loc, &xattr, GFID_TO_PATH_KEY,
+ NULL, NULL);
+ else
+ ret = syncop_getxattr (subvol, &loc, &xattr,
+ GFID2PATH_VIRT_XATTR_KEY, NULL, NULL);
+
if (ret < 0)
goto out;
- ret = dict_get_str (xattr, GFID_TO_PATH_KEY, &path);
+ ret = dict_get_str (xattr, hard_resolve ?
+ GFID2PATH_VIRT_XATTR_KEY : GFID_TO_PATH_KEY,
+ &path);
if (ret || !path) {
ret = -EINVAL;
goto out;
@@ -596,6 +618,14 @@ out:
}
int
+syncop_gfid_to_path (inode_table_t *itable, xlator_t *subvol, uuid_t gfid,
+ char **path_p)
+{
+ return syncop_gfid_to_path_hard (itable, subvol, gfid, NULL, path_p,
+ _gf_false);
+}
+
+int
syncop_inode_find (xlator_t *this, xlator_t *subvol,
uuid_t gfid, inode_t **inode,
dict_t *xdata, dict_t **rsp_dict)
diff --git a/libglusterfs/src/syncop-utils.h b/libglusterfs/src/syncop-utils.h
index 4761371c120..97b35046780 100644
--- a/libglusterfs/src/syncop-utils.h
+++ b/libglusterfs/src/syncop-utils.h
@@ -47,4 +47,9 @@ int
syncop_inode_find (xlator_t *this, xlator_t *subvol,
uuid_t gfid, inode_t **inode,
dict_t *xdata, dict_t **rsp_dict);
+
+int
+syncop_gfid_to_path_hard (inode_table_t *itable, xlator_t *subvol, uuid_t gfid,
+ inode_t *inode, char **path_p,
+ gf_boolean_t hard_resolve);
#endif /* _SYNCOP_H */
diff --git a/xlators/features/bit-rot/src/bitd/bit-rot-scrub.c b/xlators/features/bit-rot/src/bitd/bit-rot-scrub.c
index 7012f2f0a4a..ee996525f76 100644
--- a/xlators/features/bit-rot/src/bitd/bit-rot-scrub.c
+++ b/xlators/features/bit-rot/src/bitd/bit-rot-scrub.c
@@ -1625,11 +1625,12 @@ br_read_bad_object_dir (xlator_t *this, br_child_t *child, fd_t *fd,
off_t offset = 0;
int32_t count = 0;
char key[PATH_MAX] = {0, };
+ dict_t *out_dict = NULL;
INIT_LIST_HEAD (&entries.list);
while ((ret = syncop_readdir (child->xl, fd, 131072, offset, &entries,
- NULL, NULL))) {
+ NULL, &out_dict))) {
if (ret < 0)
goto out;
@@ -1647,6 +1648,12 @@ br_read_bad_object_dir (xlator_t *this, br_child_t *child, fd_t *fd,
entry->d_name);
if (!ret)
count++;
+
+ if (out_dict) {
+ dict_copy (out_dict, dict);
+ dict_unref (out_dict);
+ out_dict = NULL;
+ }
}
gf_dirent_free (&entries);
@@ -1742,6 +1749,7 @@ br_collect_bad_objects_of_child (xlator_t *this, br_child_t *child,
int32_t tmp_count = 0;
char *entry = NULL;
char tmp[PATH_MAX] = {0, };
+ char *path = NULL;
ret = dict_get_int32 (child_dict, "count", &count);
if (ret)
@@ -1755,14 +1763,16 @@ br_collect_bad_objects_of_child (xlator_t *this, br_child_t *child,
if (ret)
continue;
- snprintf (tmp, PATH_MAX, "%s ==> BRICK: %s",
- entry, child->brick_path);
+ ret = dict_get_str (child_dict, entry, &path);
+ snprintf (tmp, PATH_MAX, "%s ==> BRICK: %s\n path: %s",
+ entry, child->brick_path, path);
snprintf (main_key, PATH_MAX, "quarantine-%d",
tmp_count);
ret = dict_set_dynstr_with_alloc (dict, main_key, tmp);
- if (!ret)
- tmp_count++;
+ if (!ret)
+ tmp_count++;
+ path = NULL;
}
ret = tmp_count;
diff --git a/xlators/features/bit-rot/src/stub/bit-rot-stub-helpers.c b/xlators/features/bit-rot/src/stub/bit-rot-stub-helpers.c
index 8a88617a487..24aa9aa5ff4 100644
--- a/xlators/features/bit-rot/src/stub/bit-rot-stub-helpers.c
+++ b/xlators/features/bit-rot/src/stub/bit-rot-stub-helpers.c
@@ -560,6 +560,8 @@ br_stub_readdir_wrapper (call_frame_t *frame, xlator_t *this,
int32_t op_errno = 0;
int count = 0;
gf_dirent_t entries;
+ gf_boolean_t xdata_unref = _gf_false;
+ dict_t *dict = NULL;
INIT_LIST_HEAD (&entries.list);
@@ -587,9 +589,225 @@ br_stub_readdir_wrapper (call_frame_t *frame, xlator_t *this,
/* pick ENOENT to indicate EOF */
op_errno = errno;
op_ret = count;
+
+ dict = xdata;
+ (void) br_stub_bad_objects_path (this, fd, &entries, &dict);
+ if (!xdata && dict) {
+ xdata = dict;
+ xdata_unref = _gf_true;
+ }
+
done:
STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, &entries, xdata);
gf_dirent_free (&entries);
+ if (xdata_unref)
+ dict_unref (xdata);
return 0;
}
+/**
+ * This function is called to mainly obtain the paths of the corrupt
+ * objects (files as of now). Currently scrub status prints only the
+ * gfid of the corrupted files. Reason is, bitrot-stub maintains the
+ * list of the corrupted objects as entries inside the quarantine
+ * directory (<brick export>/.glusterfs/quarantine)
+ *
+ * And the name of each entry in the qurantine directory is the gfid
+ * of the corrupted object. So scrub status will just show that info.
+ * But it helps the users a lot if the actual path to the object is
+ * also reported. Hence the below function to get that information.
+ * The function allocates a new dict to be returned (if it does not
+ * get one from the caller of readdir i.e. scrubber as of now), and
+ * stores the paths of each corrupted gfid there. The gfid is used as
+ * the key and path is used as the value.
+ *
+ * NOTE: The path will be there in following situations
+ * 1) gfid2path option has been enabled (posix xlator option)
+ * and the corrupted file contains the path as an extended
+ * attribute.
+ * 2) If the gfid2path option is not enabled, OR if the xattr
+ * is absent, then the inode table should have it.
+ * The path will be there if a name based lookup has happened
+ * on the file which has been corrupted. With lookup a inode and
+ * dentry would be created in the inode table. And the path is
+ * constructed using the in memory inode and dentry. If a lookup
+ * has not happened OR the inode corresponding to the corrupted
+ * file does not exist in the inode table (because it got purged
+ * as lru limit of the inodes exceeded) OR a nameless lookup had
+ * happened to populate the inode in the inode table, then the
+ * path will not be printed in scrub and only the gfid will be there.
+ **/
+int
+br_stub_bad_objects_path (xlator_t *this, fd_t *fd, gf_dirent_t *entries,
+ dict_t **dict)
+{
+ gf_dirent_t *entry = NULL;
+ inode_t *inode = NULL;
+ char *hpath = NULL;
+ uuid_t gfid = {0};
+ int ret = -1;
+ dict_t *tmp_dict = NULL;
+ char str_gfid[64] = {0};
+
+ if (list_empty(&entries->list))
+ return 0;
+
+ tmp_dict = *dict;
+
+ if (!tmp_dict) {
+ tmp_dict = dict_new ();
+ /*
+ * If the allocation of dict fails then no need treat it
+ * it as a error. This path (or function) is executed when
+ * "gluster volume bitrot <volume name> scrub status" is
+ * executed, to get the list of the corrupted objects.
+ * And the motive of this function is to get the paths of
+ * the corrupted objects. If the dict allocation fails, then
+ * the scrub status will only show the gfids of those corrupted
+ * objects (which is the behavior as of the time of this patch
+ * being worked upon). So just return and only the gfids will
+ * be shown.
+ */
+ if (!tmp_dict) {
+ gf_msg (this->name, GF_LOG_ERROR, 0, BRS_MSG_NO_MEMORY,
+ "failed to allocate new dict for saving the paths "
+ "of the corrupted objects. Scrub status will only "
+ "display the gfid");
+ goto out;
+ }
+ }
+
+ list_for_each_entry (entry, &entries->list, list) {
+ gf_uuid_clear (gfid);
+ gf_uuid_parse (entry->d_name, gfid);
+
+ inode = inode_find (fd->inode->table, gfid);
+
+ /* No need to check the return value here.
+ * Because @hpath is examined.
+ */
+ (void) br_stub_get_path_of_gfid (this, fd->inode, inode,
+ gfid, &hpath);
+
+ if (hpath) {
+ gf_msg_debug (this->name, 0, "path of the corrupted "
+ "object (gfid: %s) is %s",
+ uuid_utoa (gfid), hpath);
+ br_stub_entry_xattr_fill (this, hpath, entry, tmp_dict);
+ } else
+ gf_msg (this->name, GF_LOG_WARNING, 0,
+ BRS_MSG_PATH_GET_FAILED,
+ "failed to get the path for the inode %s",
+ uuid_utoa_r (gfid, str_gfid));
+
+ inode = NULL;
+ hpath = NULL;
+ }
+
+ ret = 0;
+ *dict = tmp_dict;
+
+out:
+ return ret;
+ }
+
+int
+br_stub_get_path_of_gfid (xlator_t *this, inode_t *parent, inode_t *inode,
+ uuid_t gfid, char **path)
+{
+ int32_t ret = -1;
+ char gfid_str[64] = {0};
+
+ GF_VALIDATE_OR_GOTO ("bitrot-stub", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, parent, out);
+ GF_VALIDATE_OR_GOTO (this->name, path, out);
+
+ /* No need to validate the @inode for hard resolution. Because inode
+ * can be NULL and if it is NULL, then syncop_gfid_to_path_hard will
+ * allocate a new inode and proceed. So no need to bother about
+ * @inode. Because we need it only to send a syncop_getxattr call
+ * from inside syncop_gfid_to_path_hard. And getxattr fetches the
+ * path from the backend.
+ */
+ ret = syncop_gfid_to_path_hard (parent->table, FIRST_CHILD (this), gfid,
+ inode, path, _gf_true);
+
+ /*
+ * This is to handle those corrupted files which does not contain
+ * the gfid2path xattr in the backend (because they were created
+ * when the option was OFF OR it was upgraded from a version before
+ * gfid2path was brought in.
+ * Ideally posix should be returning ret < 0 i.e. error if the
+ * gfid2path xattr is not present. But for some reason it is
+ * returning success and path as "". THis is causing problems.
+ * For now handling it by adding extra checks. But the better way
+ * is to make posix return error if gfid2path xattr is absent.
+ * When that is done remove below if block and also this entire
+ * comment.
+ */
+ if (ret >= 0 && !strlen (*path)) {
+ gf_msg (this->name, GF_LOG_WARNING, 0, BRS_MSG_PATH_GET_FAILED,
+ "path for the object %s is %s. Going for in memory path",
+ uuid_utoa_r (gfid, gfid_str), *path);
+ ret = -1;
+ }
+
+ /*
+ * Try with soft resolution of path if hard resolve fails. Because
+ * checking the xattr on disk to get the path of a inode (or gfid)
+ * is dependent on whether that option is enabled in the posix
+ * xlator or not. If it is not enabled, then hard resolution by
+ * checking the on disk xattr fails.
+ *
+ * Thus in such situations fall back to the soft resolution which
+ * mainly depends on the inode_path() function. And for using
+ * inode_path, @inode has to be linked i.e. a successful lookup should
+ * have happened on the gfid (or the path) to link the inode to the
+ * inode table. And if @inode is NULL, means, the inode has not been
+ * found in the inode table and better not to do inode_path() on the
+ * inode which has not been linked.
+ */
+ if (ret < 0 && inode)
+ ret = syncop_gfid_to_path_hard (parent->table,
+ FIRST_CHILD (this), gfid, inode,
+ path, _gf_false);
+
+out:
+ return ret;
+}
+
+
+/**
+* NOTE: If the file has multiple hardlinks (in gluster volume
+* namespace), the path would be one of the hardlinks. Its upto
+* the user to find the remaining hardlinks (using find -samefile)
+* and remove them.
+**/
+void
+br_stub_entry_xattr_fill (xlator_t *this, char *hpath, gf_dirent_t *entry,
+ dict_t *dict)
+{
+ int32_t ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("bit-rot-stub", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, hpath, out);
+
+ /*
+ * Use the entry->d_name (which is nothing but the gfid of the
+ * corrupted object) as the key. And the value will be the actual
+ * path of that object (or file).
+ *
+ * ALso ignore the dict_set errors. scrubber will get the gfid of
+ * the corrupted object for sure. So, for now lets just log the
+ * dict_set_dynstr failure and move on.
+ */
+
+ ret = dict_set_dynstr (dict, entry->d_name, hpath);
+ if (ret)
+ gf_msg (this->name, GF_LOG_WARNING, 0, BRS_MSG_DICT_SET_FAILED,
+ "failed to set the actual path %s as the value in the "
+ "dict for the corrupted object %s", hpath,
+ entry->d_name);
+out:
+ return;
+}
diff --git a/xlators/features/bit-rot/src/stub/bit-rot-stub-messages.h b/xlators/features/bit-rot/src/stub/bit-rot-stub-messages.h
index 42022adb116..2ade4f03997 100644
--- a/xlators/features/bit-rot/src/stub/bit-rot-stub-messages.h
+++ b/xlators/features/bit-rot/src/stub/bit-rot-stub-messages.h
@@ -54,7 +54,9 @@ GLFS_MSGID(BITROT_STUB,
BRS_MSG_BAD_OBJ_THREAD_FAIL,
BRS_MSG_BAD_OBJ_DIR_CLOSE_FAIL,
BRS_MSG_LINK_FAIL,
- BRS_MSG_BAD_OBJ_UNLINK_FAIL
+ BRS_MSG_BAD_OBJ_UNLINK_FAIL,
+ BRS_MSG_DICT_SET_FAILED,
+ BRS_MSG_PATH_GET_FAILED
);
#endif /* !_BITROT_STUB_MESSAGES_H_ */
diff --git a/xlators/features/bit-rot/src/stub/bit-rot-stub.h b/xlators/features/bit-rot/src/stub/bit-rot-stub.h
index 41b6cb9d2ba..ae4db0fd4f1 100644
--- a/xlators/features/bit-rot/src/stub/bit-rot-stub.h
+++ b/xlators/features/bit-rot/src/stub/bit-rot-stub.h
@@ -22,6 +22,8 @@
#include "bit-rot-common.h"
#include "bit-rot-stub-messages.h"
#include "glusterfs3-xdr.h"
+#include "syncop.h"
+#include "syncop-utils.h"
#define BAD_OBJECT_THREAD_STACK_SIZE ((size_t)(1024*1024))
#define BR_STUB_DUMP_STR_SIZE 65536
@@ -499,4 +501,16 @@ br_stub_readdir_wrapper (call_frame_t *frame, xlator_t *this,
int
br_stub_del (xlator_t *this, uuid_t gfid);
+int
+br_stub_bad_objects_path (xlator_t *this, fd_t *fd, gf_dirent_t *entries,
+ dict_t **dict);
+
+void
+br_stub_entry_xattr_fill (xlator_t *this, char *hpath, gf_dirent_t *entry,
+ dict_t *dict);
+
+int
+br_stub_get_path_of_gfid (xlator_t *this, inode_t *parent, inode_t *inode,
+ uuid_t gfid, char **path);
+
#endif /* __BIT_ROT_STUB_H__ */