sysfs: create optimal relative symlink targets
Instead of walking from the source down to the root of sysfs, and back to the target, we stop at the first directory the source and the target share. This link: /devices/pci0000:00/0000:00:1d.7/usb1/1-0:1.0/ep_81 pointed to: ../../../../../devices/pci0000:00/0000:00:1d.0/usb2/2-0:1.0/usb_endpoint/usbdev2.1_ep81 now it just points to: usb_endpoint/usbdev1.1_ep81 Thanks to Denis Cheng for bringing this up, and sending the initial patch. CC: Denis Cheng <crquan@gmail.com> Signed-off-by: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
7b8712e563
commit
2f90a85180
|
@ -19,39 +19,6 @@
|
||||||
|
|
||||||
#include "sysfs.h"
|
#include "sysfs.h"
|
||||||
|
|
||||||
static int object_depth(struct sysfs_dirent *sd)
|
|
||||||
{
|
|
||||||
int depth = 0;
|
|
||||||
|
|
||||||
for (; sd->s_parent; sd = sd->s_parent)
|
|
||||||
depth++;
|
|
||||||
|
|
||||||
return depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int object_path_length(struct sysfs_dirent * sd)
|
|
||||||
{
|
|
||||||
int length = 1;
|
|
||||||
|
|
||||||
for (; sd->s_parent; sd = sd->s_parent)
|
|
||||||
length += strlen(sd->s_name) + 1;
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length)
|
|
||||||
{
|
|
||||||
--length;
|
|
||||||
for (; sd->s_parent; sd = sd->s_parent) {
|
|
||||||
int cur = strlen(sd->s_name);
|
|
||||||
|
|
||||||
/* back up enough to print this bus id with '/' */
|
|
||||||
length -= cur;
|
|
||||||
strncpy(buffer + length, sd->s_name, cur);
|
|
||||||
*(buffer + --length) = '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sysfs_create_link - create symlink between two objects.
|
* sysfs_create_link - create symlink between two objects.
|
||||||
* @kobj: object whose directory we're creating the link in.
|
* @kobj: object whose directory we're creating the link in.
|
||||||
|
@ -112,7 +79,6 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sysfs_remove_link - remove symlink in object's directory.
|
* sysfs_remove_link - remove symlink in object's directory.
|
||||||
* @kobj: object we're acting for.
|
* @kobj: object we're acting for.
|
||||||
|
@ -124,24 +90,54 @@ void sysfs_remove_link(struct kobject * kobj, const char * name)
|
||||||
sysfs_hash_and_remove(kobj->sd, name);
|
sysfs_hash_and_remove(kobj->sd, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sysfs_get_target_path(struct sysfs_dirent * parent_sd,
|
static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,
|
||||||
struct sysfs_dirent * target_sd, char *path)
|
struct sysfs_dirent *target_sd, char *path)
|
||||||
{
|
{
|
||||||
char * s;
|
struct sysfs_dirent *base, *sd;
|
||||||
int depth, size;
|
char *s = path;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
depth = object_depth(parent_sd);
|
/* go up to the root, stop at the base */
|
||||||
size = object_path_length(target_sd) + depth * 3 - 1;
|
base = parent_sd;
|
||||||
if (size > PATH_MAX)
|
while (base->s_parent) {
|
||||||
|
sd = target_sd->s_parent;
|
||||||
|
while (sd->s_parent && base != sd)
|
||||||
|
sd = sd->s_parent;
|
||||||
|
|
||||||
|
if (base == sd)
|
||||||
|
break;
|
||||||
|
|
||||||
|
strcpy(s, "../");
|
||||||
|
s += 3;
|
||||||
|
base = base->s_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* determine end of target string for reverse fillup */
|
||||||
|
sd = target_sd;
|
||||||
|
while (sd->s_parent && sd != base) {
|
||||||
|
len += strlen(sd->s_name) + 1;
|
||||||
|
sd = sd->s_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check limits */
|
||||||
|
if (len < 2)
|
||||||
|
return -EINVAL;
|
||||||
|
len--;
|
||||||
|
if ((s - path) + len > PATH_MAX)
|
||||||
return -ENAMETOOLONG;
|
return -ENAMETOOLONG;
|
||||||
|
|
||||||
pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);
|
/* reverse fillup of target string from target to base */
|
||||||
|
sd = target_sd;
|
||||||
|
while (sd->s_parent && sd != base) {
|
||||||
|
int slen = strlen(sd->s_name);
|
||||||
|
|
||||||
for (s = path; depth--; s += 3)
|
len -= slen;
|
||||||
strcpy(s,"../");
|
strncpy(s + len, sd->s_name, slen);
|
||||||
|
if (len)
|
||||||
|
s[--len] = '/';
|
||||||
|
|
||||||
fill_object_path(target_sd, path, size);
|
sd = sd->s_parent;
|
||||||
pr_debug("%s: path = '%s'\n", __FUNCTION__, path);
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue