diff options
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__ */ |