From ab2558a3e7a1b2de2d63a3812ab4ed58d10d8619 Mon Sep 17 00:00:00 2001 From: Kotresh HR Date: Sun, 16 Jul 2017 15:16:56 -0400 Subject: storage/posix: Add virtual xattr to fetch path from gfid The gfid2path infra stores the "pargfid/bname" as on xattr value for each non directory entry. Hardlinks would have a separate xattr. This xattr key is internal and is not exposed to applications. A virtual xattr is exposed for the applications to fetch the path from gfid. Internal xattr: trusted.gfid2path. Virtual xattr: glusterfs.gfidtopath getfattr -h -n glusterfs.gfidtopath //.gfid/ If there are hardlinks, it returns all the paths separated by ':'. A volume set option is introduced to change the delimiter to required string of max length 7. gluster vol set gfid2path-separator ":::" Updates: #139 Change-Id: Ie3b0c3fd8bd5333c4a27410011e608333918c02a Signed-off-by: Kotresh HR Reviewed-on: https://review.gluster.org/17785 Smoke: Gluster Build System CentOS-regression: Gluster Build System Reviewed-by: Krutika Dhananjay --- xlators/storage/posix/src/posix-gfid-path.c | 184 ++++++++++++++++++++++++++++ xlators/storage/posix/src/posix-gfid-path.h | 3 + xlators/storage/posix/src/posix-helpers.c | 96 +++++++++++++++ xlators/storage/posix/src/posix.c | 51 ++++++++ xlators/storage/posix/src/posix.h | 4 + 5 files changed, 338 insertions(+) (limited to 'xlators/storage') diff --git a/xlators/storage/posix/src/posix-gfid-path.c b/xlators/storage/posix/src/posix-gfid-path.c index 7529f559fc7..2834c2fc4fd 100644 --- a/xlators/storage/posix/src/posix-gfid-path.c +++ b/xlators/storage/posix/src/posix-gfid-path.c @@ -13,6 +13,9 @@ #include "syscall.h" #include "logging.h" #include "posix-messages.h" +#include "posix-mem-types.h" +#include "posix-gfid-path.h" +#include "posix.h" int32_t posix_set_gfid2path_xattr (xlator_t *this, const char *path, uuid_t pgfid, @@ -96,3 +99,184 @@ posix_is_gfid2path_xattr (const char *name) return _gf_false; } } + +static int gf_posix_xattr_enotsup_log; + +int32_t +posix_get_gfid2path (xlator_t *this, inode_t *inode, const char *real_path, + int *op_errno, dict_t *dict) +{ + int ret = 0; + char *path = NULL; + ssize_t size = 0; + char *list = NULL; + int32_t list_offset = 0; + int32_t i = 0; + int32_t j = 0; + char *paths[MAX_GFID2PATH_LINK_SUP] = {NULL,}; + char *value = NULL; + size_t remaining_size = 0; + size_t bytes = 0; + char keybuffer[4096] = {0,}; + char value_buf[8192] = {0,}; + uuid_t pargfid = {0,}; + gf_boolean_t have_val = _gf_false; + struct posix_private *priv = NULL; + char pargfid_str[UUID_CANONICAL_FORM_LEN + 1] = {0,}; + + priv = this->private; + + if (IA_ISDIR (inode->ia_type)) { + ret = posix_resolve_dirgfid_to_path (inode->gfid, + priv->base_path, + NULL, &path); + if (ret < 0) { + ret = -1; + goto err; + } + ret = dict_set_dynstr (dict, GFID2PATH_VIRT_XATTR_KEY, path); + if (ret < 0) { + gf_msg (this->name, GF_LOG_WARNING, -ret, + P_MSG_DICT_SET_FAILED, "could not set " + "value for key (%s)", GFID2PATH_VIRT_XATTR_KEY); + goto err; + } + } else { + have_val = _gf_false; + memset (value_buf, '\0', sizeof(value_buf)); + size = sys_llistxattr (real_path, value_buf, + sizeof (value_buf) - 1); + if (size > 0) { + have_val = _gf_true; + } else { + if (errno == ERANGE) { + gf_msg (this->name, GF_LOG_DEBUG, errno, + P_MSG_XATTR_FAILED, + "listxattr failed due to overflow of" + " buffer on %s ", real_path); + size = sys_llistxattr (real_path, NULL, 0); + } + if (size == -1) { + *op_errno = errno; + if ((errno == ENOTSUP) || (errno == ENOSYS)) { + GF_LOG_OCCASIONALLY ( + gf_posix_xattr_enotsup_log, + this->name, GF_LOG_WARNING, + "Extended attributes not " + "supported (try remounting" + " brick with 'user_xattr' " + "flag)"); + } else { + gf_msg (this->name, GF_LOG_ERROR, errno, + P_MSG_XATTR_FAILED, + "listxattr failed on %s", + real_path); + } + goto err; + } + if (size == 0) + goto done; + } + list = alloca (size); + if (!list) { + *op_errno = errno; + goto err; + } + if (have_val) { + memcpy (list, value_buf, size); + } else { + size = sys_llistxattr (real_path, list, size); + if (size < 0) { + ret = -1; + *op_errno = errno; + goto err; + } + } + remaining_size = size; + list_offset = 0; + while (remaining_size > 0) { + strncpy (keybuffer, list + list_offset, + sizeof(keybuffer)); + + if (!posix_is_gfid2path_xattr (keybuffer)) { + goto ignore; + } + + memset (value_buf, '\0', sizeof(value_buf)); + size = sys_lgetxattr (real_path, keybuffer, value_buf, + sizeof (value_buf) - 1); + if (size == -1) { + ret = -1; + *op_errno = errno; + gf_msg (this->name, GF_LOG_ERROR, errno, + P_MSG_XATTR_FAILED, "getxattr failed on" + " %s: key = %s ", real_path, keybuffer); + break; + } + + /* Parse pargfid from xattr value*/ + strncpy (pargfid_str, value_buf, 36); + pargfid_str[36] = '\0'; + gf_uuid_parse (pargfid_str, pargfid); + + /* Convert pargfid to path */ + ret = posix_resolve_dirgfid_to_path (pargfid, + priv->base_path, + &value_buf[37], + &paths[i]); + i++; + +ignore: + remaining_size -= strlen (keybuffer) + 1; + list_offset += strlen (keybuffer) + 1; + } /* while (remaining_size > 0) */ + + /* Calculate memory to be allocated */ + for (j = 0; j < i; j++) { + bytes += strlen(paths[j]); + if (j < i-1) + bytes += strlen(priv->gfid2path_sep); + } + value = GF_CALLOC (bytes + 1, sizeof(char), gf_posix_mt_char); + if (!value) { + ret = -1; + *op_errno = errno; + goto err; + } + + for (j = 0; j < i; j++) { + strcat (value, paths[j]); + if (j != i - 1) + strcat (value, priv->gfid2path_sep); + } + value[bytes] = '\0'; + + ret = dict_set_dynptr (dict, GFID2PATH_VIRT_XATTR_KEY, + value, bytes); + if (ret < 0) { + *op_errno = -ret; + gf_msg (this->name, GF_LOG_ERROR, *op_errno, + P_MSG_DICT_SET_FAILED, "dict set operation " + "on %s for the key %s failed.", + real_path, GFID2PATH_VIRT_XATTR_KEY); + GF_FREE (value); + goto err; + } + } + +done: + for (j = 0; j < i; j++) { + if (paths[j]) + GF_FREE (paths[j]); + } + ret = 0; + return ret; +err: + if (path) + GF_FREE (path); + for (j = 0; j < i; j++) { + if (paths[j]) + GF_FREE (paths[j]); + } + return ret; +} diff --git a/xlators/storage/posix/src/posix-gfid-path.h b/xlators/storage/posix/src/posix-gfid-path.h index b1a23752e8f..ac3d03e14c3 100644 --- a/xlators/storage/posix/src/posix-gfid-path.h +++ b/xlators/storage/posix/src/posix-gfid-path.h @@ -24,4 +24,7 @@ posix_remove_gfid2path_xattr (xlator_t *, const char *, uuid_t, const char *); gf_boolean_t posix_is_gfid2path_xattr (const char *name); +int32_t +posix_get_gfid2path (xlator_t *this, inode_t *inode, const char *real_path, + int *op_errno, dict_t *dict); #endif /* _POSIX_GFID_PATH_H */ diff --git a/xlators/storage/posix/src/posix-helpers.c b/xlators/storage/posix/src/posix-helpers.c index 1530b1192c5..1a05d65161a 100644 --- a/xlators/storage/posix/src/posix-helpers.c +++ b/xlators/storage/posix/src/posix-helpers.c @@ -2379,6 +2379,102 @@ posix_fdget_objectsignature (int fd, dict_t *xattr) return -EINVAL; } +/* + * posix_resolve_dirgfid_to_path: + * It converts given dirgfid to path by doing recursive readlinks at the + * backend. If bname is given, it suffixes bname to dir path to form the + * complete path else it doesn't. It allocates memory for the path and is + * caller's responsibility to free the same. If bname is NULL and pargfid + * is ROOT, then it returns "/" + **/ + +int32_t +posix_resolve_dirgfid_to_path (const uuid_t dirgfid, const char *brick_path, + const char *bname, char **path) +{ + char *linkname = NULL; + char *dir_handle = NULL; + char *pgfidstr = NULL; + char *saveptr = NULL; + ssize_t len = 0; + int ret = 0; + uuid_t tmp_gfid = {0, }; + uuid_t pargfid = {0, }; + char gpath[PATH_MAX] = {0,}; + char result[PATH_MAX] = {0,}; + char result1[PATH_MAX] = {0,}; + char *dir_name = NULL; + char pre_dir_name[PATH_MAX] = {0,}; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT (this); + + gf_uuid_copy (pargfid, dirgfid); + if (!path || gf_uuid_is_null (pargfid)) { + ret = -1; + goto out; + } + + if (__is_root_gfid (pargfid)) { + if (bname) { + snprintf (result, PATH_MAX, "/%s", bname); + *path = gf_strdup (result); + } else { + *path = gf_strdup ("/"); + } + return ret; + } + + dir_handle = alloca (PATH_MAX); + linkname = alloca (PATH_MAX); + (void) snprintf (gpath, PATH_MAX, "%s/.glusterfs/", brick_path); + + while (!(__is_root_gfid (pargfid))) { + snprintf (dir_handle, PATH_MAX, "%s/%02x/%02x/%s", gpath, + pargfid[0], pargfid[1], uuid_utoa (pargfid)); + + len = sys_readlink (dir_handle, linkname, PATH_MAX); + if (len < 0) { + gf_msg (this->name, GF_LOG_ERROR, errno, + P_MSG_READLINK_FAILED, + "could not read the " + "link from the gfid handle %s", dir_handle); + ret = -1; + goto out; + } + + linkname[len] = '\0'; + + pgfidstr = strtok_r (linkname + strlen("../../00/00/"), "/", + &saveptr); + dir_name = strtok_r (NULL, "/", &saveptr); + + if (strlen(pre_dir_name) != 0) { /* Remove '/' at the end */ + snprintf (result, PATH_MAX, "%s/%s", dir_name, + pre_dir_name); + } else { + snprintf (result, PATH_MAX, "%s", dir_name); + } + + strncpy (pre_dir_name, result, sizeof(pre_dir_name)); + + gf_uuid_parse (pgfidstr, tmp_gfid); + gf_uuid_copy (pargfid, tmp_gfid); + } + + if (bname) { + snprintf (result1, PATH_MAX, "/%s/%s", result, bname); + } else { + snprintf (result1, PATH_MAX, "/%s", result); + } + + *path = gf_strdup (result1); + +out: + return ret; +} + posix_inode_ctx_t * __posix_inode_ctx_get (inode_t *inode, xlator_t *this) { diff --git a/xlators/storage/posix/src/posix.c b/xlators/storage/posix/src/posix.c index ceb9d448025..a04d830dd69 100644 --- a/xlators/storage/posix/src/posix.c +++ b/xlators/storage/posix/src/posix.c @@ -4734,6 +4734,23 @@ posix_getxattr (call_frame_t *frame, xlator_t *this, goto done; } + if (loc->inode && name && + (strcmp (name, GFID2PATH_VIRT_XATTR_KEY) == 0)) { + if (!priv->gfid2path) { + op_errno = ENOATTR; + op_ret = -1; + goto out; + } + ret = posix_get_gfid2path (this, loc->inode, real_path, + &op_errno, dict); + if (ret < 0) { + op_ret = -1; + goto out; + } + size = ret; + goto done; + } + if (loc->inode && name && (strcmp (name, GET_ANCESTRY_PATH_KEY) == 0)) { int type = POSIX_ANCESTRY_PATH; @@ -6989,7 +7006,19 @@ posix_set_owner (xlator_t *this, uid_t uid, gid_t gid) return ret; } +static int +set_gfid2path_separator (struct posix_private *priv, const char *str) +{ + int str_len = 0; + str_len = strlen(str); + if (str_len > 0 && str_len < 8) { + strcpy (priv->gfid2path_sep, str); + return 0; + } + + return -1; +} static int set_batch_fsync_mode (struct posix_private *priv, const char *str) @@ -7036,6 +7065,7 @@ reconfigure (xlator_t *this, dict_t *options) int32_t uid = -1; int32_t gid = -1; char *batch_fsync_mode_str = NULL; + char *gfid2path_sep = NULL; priv = this->private; @@ -7056,6 +7086,14 @@ reconfigure (xlator_t *this, dict_t *options) goto out; } + GF_OPTION_RECONF ("gfid2path-separator", gfid2path_sep, options, + str, out); + if (set_gfid2path_separator (priv, gfid2path_sep) != 0) { + gf_msg (this->name, GF_LOG_ERROR, 0, P_MSG_INVALID_ARGUMENT, + "Length of separator exceeds 7: %s", gfid2path_sep); + goto out; + } + #ifdef GF_DARWIN_HOST_OS char *xattr_user_namespace_mode_str = NULL; @@ -7249,6 +7287,7 @@ init (xlator_t *this) int32_t uid = -1; int32_t gid = -1; char *batch_fsync_mode_str; + char *gfid2path_sep = NULL; dir_data = dict_get (this->options, "directory"); @@ -7750,6 +7789,13 @@ init (xlator_t *this) goto out; } + GF_OPTION_INIT ("gfid2path-separator", gfid2path_sep, str, out); + if (set_gfid2path_separator (_private, gfid2path_sep) != 0) { + gf_msg (this->name, GF_LOG_ERROR, 0, P_MSG_INVALID_ARGUMENT, + "Length of separator exceeds 7: %s", gfid2path_sep); + goto out; + } + #ifdef GF_DARWIN_HOST_OS char *xattr_user_namespace_mode_str = NULL; @@ -7950,6 +7996,11 @@ struct volume_options options[] = { .default_value = "off", .description = "Enable logging metadata for gfid to path conversion" }, + { .key = {"gfid2path-separator"}, + .type = GF_OPTION_TYPE_STR, + .default_value = ":", + .description = "Path separator for glusterfs.gfidtopath virt xattr" + }, #if GF_DARWIN_HOST_OS { .key = {"xattr-user-namespace-mode"}, .type = GF_OPTION_TYPE_STR, diff --git a/xlators/storage/posix/src/posix.h b/xlators/storage/posix/src/posix.h index d83e2678572..3893cf3f9bd 100644 --- a/xlators/storage/posix/src/posix.h +++ b/xlators/storage/posix/src/posix.h @@ -173,6 +173,7 @@ struct posix_private { uint32_t batch_fsync_delay_usec; gf_boolean_t update_pgfid_nlinks; gf_boolean_t gfid2path; + char gfid2path_sep[8]; /* seconds to sleep between health checks */ uint32_t health_check_interval; @@ -300,6 +301,9 @@ posix_get_ancestry (xlator_t *this, inode_t *leaf_inode, dict_t *xdata); int posix_handle_georep_xattrs (call_frame_t *, const char *, int *, gf_boolean_t); +int32_t +posix_resolve_dirgfid_to_path (const uuid_t dirgfid, const char *brick_path, + const char *bname, char **path); void posix_gfid_unset (xlator_t *this, dict_t *xdata); -- cgit