diff options
author | Raghavendra Bhat <raghavendra@redhat.com> | 2018-04-20 14:54:33 -0400 |
---|---|---|
committer | Amar Tumballi <amarts@redhat.com> | 2018-05-04 11:13:57 +0000 |
commit | b42a048d3a76c7f377399f18d30f0a8a930f9d05 (patch) | |
tree | b400225db569fdd1b3251105024bec73690da175 /xlators/features/bit-rot | |
parent | bef654f48c14bfd7ce20702edff41052f6f54bdc (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>
Diffstat (limited to 'xlators/features/bit-rot')
4 files changed, 250 insertions, 6 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 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__ */ |