diff options
author | Tejun Heo <htejun@gmail.com> | 2007-06-13 14:45:15 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-11 19:09:04 -0400 |
commit | 2b29ac252afff87b8465b064ca2d9740cf1f6e52 (patch) | |
tree | 9f4930db68ace50adc7c11feba12aafe34e2cdbe /fs/sysfs/symlink.c | |
parent | aecdcedaab49ca40620dc7dd70f67ee7269a66c9 (diff) |
sysfs: reimplement symlink using sysfs_dirent tree
sysfs symlink is implemented by referencing dentry and kobject from
sysfs_dirent - symlink entry references kobject, dentry is used to
walk the tree. This complicates object lifetimes rules and is
dangerous - for example, there is no way to tell to which module the
target of a symlink belongs and referencing that kobject can make it
linger after the module is gone.
This patch reimplements symlink using only sysfs_dirent tree. sd for
a symlink points and holds reference to the target sysfs_dirent and
all walking is done using sysfs_dirent tree. Simpler and safer.
Please read the following message for more info.
http://article.gmane.org/gmane.linux.kernel/510293
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/sysfs/symlink.c')
-rw-r--r-- | fs/sysfs/symlink.c | 88 |
1 files changed, 47 insertions, 41 deletions
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 27df635b786a..ff605d3f4d33 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c | |||
@@ -11,50 +11,49 @@ | |||
11 | 11 | ||
12 | #include "sysfs.h" | 12 | #include "sysfs.h" |
13 | 13 | ||
14 | static int object_depth(struct kobject * kobj) | 14 | static int object_depth(struct sysfs_dirent *sd) |
15 | { | 15 | { |
16 | struct kobject * p = kobj; | ||
17 | int depth = 0; | 16 | int depth = 0; |
18 | do { depth++; } while ((p = p->parent)); | 17 | |
18 | for (; sd->s_parent; sd = sd->s_parent) | ||
19 | depth++; | ||
20 | |||
19 | return depth; | 21 | return depth; |
20 | } | 22 | } |
21 | 23 | ||
22 | static int object_path_length(struct kobject * kobj) | 24 | static int object_path_length(struct sysfs_dirent * sd) |
23 | { | 25 | { |
24 | struct kobject * p = kobj; | ||
25 | int length = 1; | 26 | int length = 1; |
26 | do { | 27 | |
27 | length += strlen(kobject_name(p)) + 1; | 28 | for (; sd->s_parent; sd = sd->s_parent) |
28 | p = p->parent; | 29 | length += strlen(sd->s_name) + 1; |
29 | } while (p); | 30 | |
30 | return length; | 31 | return length; |
31 | } | 32 | } |
32 | 33 | ||
33 | static void fill_object_path(struct kobject * kobj, char * buffer, int length) | 34 | static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length) |
34 | { | 35 | { |
35 | struct kobject * p; | ||
36 | |||
37 | --length; | 36 | --length; |
38 | for (p = kobj; p; p = p->parent) { | 37 | for (; sd->s_parent; sd = sd->s_parent) { |
39 | int cur = strlen(kobject_name(p)); | 38 | int cur = strlen(sd->s_name); |
40 | 39 | ||
41 | /* back up enough to print this bus id with '/' */ | 40 | /* back up enough to print this bus id with '/' */ |
42 | length -= cur; | 41 | length -= cur; |
43 | strncpy(buffer + length,kobject_name(p),cur); | 42 | strncpy(buffer + length, sd->s_name, cur); |
44 | *(buffer + --length) = '/'; | 43 | *(buffer + --length) = '/'; |
45 | } | 44 | } |
46 | } | 45 | } |
47 | 46 | ||
48 | static int sysfs_add_link(struct dentry * parent, const char * name, struct kobject * target) | 47 | static int sysfs_add_link(struct sysfs_dirent * parent_sd, const char * name, |
48 | struct sysfs_dirent * target_sd) | ||
49 | { | 49 | { |
50 | struct sysfs_dirent * parent_sd = parent->d_fsdata; | ||
51 | struct sysfs_dirent * sd; | 50 | struct sysfs_dirent * sd; |
52 | 51 | ||
53 | sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); | 52 | sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); |
54 | if (!sd) | 53 | if (!sd) |
55 | return -ENOMEM; | 54 | return -ENOMEM; |
56 | 55 | ||
57 | sd->s_elem.symlink.target_kobj = kobject_get(target); | 56 | sd->s_elem.symlink.target_sd = target_sd; |
58 | sysfs_attach_dirent(sd, parent_sd, NULL); | 57 | sysfs_attach_dirent(sd, parent_sd, NULL); |
59 | return 0; | 58 | return 0; |
60 | } | 59 | } |
@@ -68,6 +67,8 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj | |||
68 | int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) | 67 | int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) |
69 | { | 68 | { |
70 | struct dentry *dentry = NULL; | 69 | struct dentry *dentry = NULL; |
70 | struct sysfs_dirent *parent_sd = NULL; | ||
71 | struct sysfs_dirent *target_sd = NULL; | ||
71 | int error = -EEXIST; | 72 | int error = -EEXIST; |
72 | 73 | ||
73 | BUG_ON(!name); | 74 | BUG_ON(!name); |
@@ -80,11 +81,27 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char | |||
80 | 81 | ||
81 | if (!dentry) | 82 | if (!dentry) |
82 | return -EFAULT; | 83 | return -EFAULT; |
84 | parent_sd = dentry->d_fsdata; | ||
85 | |||
86 | /* target->dentry can go away beneath us but is protected with | ||
87 | * kobj_sysfs_assoc_lock. Fetch target_sd from it. | ||
88 | */ | ||
89 | spin_lock(&kobj_sysfs_assoc_lock); | ||
90 | if (target->dentry) | ||
91 | target_sd = sysfs_get(target->dentry->d_fsdata); | ||
92 | spin_unlock(&kobj_sysfs_assoc_lock); | ||
93 | |||
94 | if (!target_sd) | ||
95 | return -ENOENT; | ||
83 | 96 | ||
84 | mutex_lock(&dentry->d_inode->i_mutex); | 97 | mutex_lock(&dentry->d_inode->i_mutex); |
85 | if (!sysfs_dirent_exist(dentry->d_fsdata, name)) | 98 | if (!sysfs_dirent_exist(dentry->d_fsdata, name)) |
86 | error = sysfs_add_link(dentry, name, target); | 99 | error = sysfs_add_link(parent_sd, name, target_sd); |
87 | mutex_unlock(&dentry->d_inode->i_mutex); | 100 | mutex_unlock(&dentry->d_inode->i_mutex); |
101 | |||
102 | if (error) | ||
103 | sysfs_put(target_sd); | ||
104 | |||
88 | return error; | 105 | return error; |
89 | } | 106 | } |
90 | 107 | ||
@@ -100,14 +117,14 @@ void sysfs_remove_link(struct kobject * kobj, const char * name) | |||
100 | sysfs_hash_and_remove(kobj->dentry,name); | 117 | sysfs_hash_and_remove(kobj->dentry,name); |
101 | } | 118 | } |
102 | 119 | ||
103 | static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | 120 | static int sysfs_get_target_path(struct sysfs_dirent * parent_sd, |
104 | char *path) | 121 | struct sysfs_dirent * target_sd, char *path) |
105 | { | 122 | { |
106 | char * s; | 123 | char * s; |
107 | int depth, size; | 124 | int depth, size; |
108 | 125 | ||
109 | depth = object_depth(kobj); | 126 | depth = object_depth(parent_sd); |
110 | size = object_path_length(target) + depth * 3 - 1; | 127 | size = object_path_length(target_sd) + depth * 3 - 1; |
111 | if (size > PATH_MAX) | 128 | if (size > PATH_MAX) |
112 | return -ENAMETOOLONG; | 129 | return -ENAMETOOLONG; |
113 | 130 | ||
@@ -116,7 +133,7 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | |||
116 | for (s = path; depth--; s += 3) | 133 | for (s = path; depth--; s += 3) |
117 | strcpy(s,"../"); | 134 | strcpy(s,"../"); |
118 | 135 | ||
119 | fill_object_path(target, path, size); | 136 | fill_object_path(target_sd, path, size); |
120 | pr_debug("%s: path = '%s'\n", __FUNCTION__, path); | 137 | pr_debug("%s: path = '%s'\n", __FUNCTION__, path); |
121 | 138 | ||
122 | return 0; | 139 | return 0; |
@@ -124,27 +141,16 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | |||
124 | 141 | ||
125 | static int sysfs_getlink(struct dentry *dentry, char * path) | 142 | static int sysfs_getlink(struct dentry *dentry, char * path) |
126 | { | 143 | { |
127 | struct kobject *kobj, *target_kobj; | 144 | struct sysfs_dirent *sd = dentry->d_fsdata; |
128 | int error = 0; | 145 | struct sysfs_dirent *parent_sd = sd->s_parent; |
129 | 146 | struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd; | |
130 | kobj = sysfs_get_kobject(dentry->d_parent); | 147 | int error; |
131 | if (!kobj) | ||
132 | return -EINVAL; | ||
133 | |||
134 | target_kobj = sysfs_get_kobject(dentry); | ||
135 | if (!target_kobj) { | ||
136 | kobject_put(kobj); | ||
137 | return -EINVAL; | ||
138 | } | ||
139 | 148 | ||
140 | down_read(&sysfs_rename_sem); | 149 | down_read(&sysfs_rename_sem); |
141 | error = sysfs_get_target_path(kobj, target_kobj, path); | 150 | error = sysfs_get_target_path(parent_sd, target_sd, path); |
142 | up_read(&sysfs_rename_sem); | 151 | up_read(&sysfs_rename_sem); |
143 | |||
144 | kobject_put(kobj); | ||
145 | kobject_put(target_kobj); | ||
146 | return error; | ||
147 | 152 | ||
153 | return error; | ||
148 | } | 154 | } |
149 | 155 | ||
150 | static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) | 156 | static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) |