diff options
Diffstat (limited to 'xlators/features/bit-rot/src/stub/bit-rot-stub-helpers.c')
| -rw-r--r-- | xlators/features/bit-rot/src/stub/bit-rot-stub-helpers.c | 218 | 
1 files changed, 218 insertions, 0 deletions
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; +}  | 
