Symlinks in configfs are used to be created from near places. Currently the path is artificially inflated by multiple ../ to the configfs root an then a full path of the target. For scsi target subsystem the difference between such a path and a minimal possible path is ~100 characters. This patch makes a minimal relative path of symlink - from the closest common parent. Signed-off-by: Dmitry Bogdanov <d.bogdanov@xxxxxxxxx> --- fs/configfs/symlink.c | 59 ++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index 224c9e4899d4..a61f5a4763e1 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -19,62 +19,79 @@ /* Protects attachments of new symlinks */ DEFINE_MUTEX(configfs_symlink_mutex); -static int item_depth(struct config_item * item) -{ - struct config_item * p = item; - int depth = 0; - do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p)); - return depth; -} - -static int item_path_length(struct config_item * item) +static int item_path_length(struct config_item *item, int depth) { struct config_item * p = item; int length = 1; + + if (!depth) + return length; + do { length += strlen(config_item_name(p)) + 1; p = p->ci_parent; - } while (p && !configfs_is_root(p)); + depth--; + } while (depth && p && !configfs_is_root(p)); return length; } -static void fill_item_path(struct config_item * item, char * buffer, int length) + +static void fill_item_path(struct config_item *item, int depth, char *buffer, int length) { struct config_item * p; --length; - for (p = item; p && !configfs_is_root(p); p = p->ci_parent) { + for (p = item; depth && p && !configfs_is_root(p); p = p->ci_parent, depth--) { int cur = strlen(config_item_name(p)); /* back up enough to print this bus id with '/' */ length -= cur; memcpy(buffer + length, config_item_name(p), cur); - *(buffer + --length) = '/'; + if (depth > 1) + *(buffer + --length) = '/'; } } static int configfs_get_target_path(struct config_item *item, struct config_item *target, char **path) { - int depth, size; + struct config_item *pdest, *ptarget; + int target_depth = 0, item_depth = 0; + int size; char *s; - depth = item_depth(item); - size = item_path_length(target) + depth * 3 - 1; + /* find closest common parent to make a minimal path */ + for (ptarget = target; + ptarget && !configfs_is_root(ptarget); + ptarget = ptarget->ci_parent) { + item_depth = 0; + for (pdest = item; + pdest && !configfs_is_root(pdest); + pdest = pdest->ci_parent) { + if (pdest == ptarget) + goto out; + + item_depth++; + } + + target_depth++; + } +out: + size = 3 * item_depth + item_path_length(target, target_depth) - 1; if (size > PATH_MAX) return -ENAMETOOLONG; - pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size); + pr_debug("%s: item_depth = %d, target_depth = %d, size = %d\n", + __func__, item_depth, target_depth, size); *path = kzalloc(size, GFP_KERNEL); if (!*path) return -ENOMEM; + for (s = *path; item_depth--; s += 3) + strcpy(s, "../"); - for (s = *path; depth--; s += 3) - strcpy(s,"../"); - - fill_item_path(target, *path, size); + fill_item_path(target, target_depth, *path, size); pr_debug("%s: path = '%s'\n", __func__, *path); return 0; } -- 2.25.1