diff options
Diffstat (limited to 'fs/sysfs/symlink.c')
-rw-r--r-- | fs/sysfs/symlink.c | 150 |
1 files changed, 70 insertions, 80 deletions
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 7b9c5bfde920..2f86e0422290 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c | |||
@@ -11,71 +11,39 @@ | |||
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) | ||
49 | { | ||
50 | struct sysfs_dirent * parent_sd = parent->d_fsdata; | ||
51 | struct sysfs_symlink * sl; | ||
52 | int error = 0; | ||
53 | |||
54 | error = -ENOMEM; | ||
55 | sl = kmalloc(sizeof(*sl), GFP_KERNEL); | ||
56 | if (!sl) | ||
57 | goto exit1; | ||
58 | |||
59 | sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); | ||
60 | if (!sl->link_name) | ||
61 | goto exit2; | ||
62 | |||
63 | strcpy(sl->link_name, name); | ||
64 | sl->target_kobj = kobject_get(target); | ||
65 | |||
66 | error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO, | ||
67 | SYSFS_KOBJ_LINK); | ||
68 | if (!error) | ||
69 | return 0; | ||
70 | |||
71 | kobject_put(target); | ||
72 | kfree(sl->link_name); | ||
73 | exit2: | ||
74 | kfree(sl); | ||
75 | exit1: | ||
76 | return error; | ||
77 | } | ||
78 | |||
79 | /** | 47 | /** |
80 | * sysfs_create_link - create symlink between two objects. | 48 | * sysfs_create_link - create symlink between two objects. |
81 | * @kobj: object whose directory we're creating the link in. | 49 | * @kobj: object whose directory we're creating the link in. |
@@ -84,24 +52,57 @@ exit1: | |||
84 | */ | 52 | */ |
85 | int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) | 53 | int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) |
86 | { | 54 | { |
87 | struct dentry *dentry = NULL; | 55 | struct sysfs_dirent *parent_sd = NULL; |
88 | int error = -EEXIST; | 56 | struct sysfs_dirent *target_sd = NULL; |
57 | struct sysfs_dirent *sd = NULL; | ||
58 | struct sysfs_addrm_cxt acxt; | ||
59 | int error; | ||
89 | 60 | ||
90 | BUG_ON(!name); | 61 | BUG_ON(!name); |
91 | 62 | ||
92 | if (!kobj) { | 63 | if (!kobj) { |
93 | if (sysfs_mount && sysfs_mount->mnt_sb) | 64 | if (sysfs_mount && sysfs_mount->mnt_sb) |
94 | dentry = sysfs_mount->mnt_sb->s_root; | 65 | parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata; |
95 | } else | 66 | } else |
96 | dentry = kobj->dentry; | 67 | parent_sd = kobj->sd; |
68 | |||
69 | error = -EFAULT; | ||
70 | if (!parent_sd) | ||
71 | goto out_put; | ||
72 | |||
73 | /* target->sd can go away beneath us but is protected with | ||
74 | * sysfs_assoc_lock. Fetch target_sd from it. | ||
75 | */ | ||
76 | spin_lock(&sysfs_assoc_lock); | ||
77 | if (target->sd) | ||
78 | target_sd = sysfs_get(target->sd); | ||
79 | spin_unlock(&sysfs_assoc_lock); | ||
80 | |||
81 | error = -ENOENT; | ||
82 | if (!target_sd) | ||
83 | goto out_put; | ||
84 | |||
85 | error = -ENOMEM; | ||
86 | sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); | ||
87 | if (!sd) | ||
88 | goto out_put; | ||
89 | sd->s_elem.symlink.target_sd = target_sd; | ||
97 | 90 | ||
98 | if (!dentry) | 91 | sysfs_addrm_start(&acxt, parent_sd); |
99 | return -EFAULT; | ||
100 | 92 | ||
101 | mutex_lock(&dentry->d_inode->i_mutex); | 93 | if (!sysfs_find_dirent(parent_sd, name)) { |
102 | if (!sysfs_dirent_exist(dentry->d_fsdata, name)) | 94 | sysfs_add_one(&acxt, sd); |
103 | error = sysfs_add_link(dentry, name, target); | 95 | sysfs_link_sibling(sd); |
104 | mutex_unlock(&dentry->d_inode->i_mutex); | 96 | } |
97 | |||
98 | if (sysfs_addrm_finish(&acxt)) | ||
99 | return 0; | ||
100 | |||
101 | error = -EEXIST; | ||
102 | /* fall through */ | ||
103 | out_put: | ||
104 | sysfs_put(target_sd); | ||
105 | sysfs_put(sd); | ||
105 | return error; | 106 | return error; |
106 | } | 107 | } |
107 | 108 | ||
@@ -114,17 +115,17 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char | |||
114 | 115 | ||
115 | void sysfs_remove_link(struct kobject * kobj, const char * name) | 116 | void sysfs_remove_link(struct kobject * kobj, const char * name) |
116 | { | 117 | { |
117 | sysfs_hash_and_remove(kobj->dentry,name); | 118 | sysfs_hash_and_remove(kobj->sd, name); |
118 | } | 119 | } |
119 | 120 | ||
120 | static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | 121 | static int sysfs_get_target_path(struct sysfs_dirent * parent_sd, |
121 | char *path) | 122 | struct sysfs_dirent * target_sd, char *path) |
122 | { | 123 | { |
123 | char * s; | 124 | char * s; |
124 | int depth, size; | 125 | int depth, size; |
125 | 126 | ||
126 | depth = object_depth(kobj); | 127 | depth = object_depth(parent_sd); |
127 | size = object_path_length(target) + depth * 3 - 1; | 128 | size = object_path_length(target_sd) + depth * 3 - 1; |
128 | if (size > PATH_MAX) | 129 | if (size > PATH_MAX) |
129 | return -ENAMETOOLONG; | 130 | return -ENAMETOOLONG; |
130 | 131 | ||
@@ -133,7 +134,7 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | |||
133 | for (s = path; depth--; s += 3) | 134 | for (s = path; depth--; s += 3) |
134 | strcpy(s,"../"); | 135 | strcpy(s,"../"); |
135 | 136 | ||
136 | fill_object_path(target, path, size); | 137 | fill_object_path(target_sd, path, size); |
137 | pr_debug("%s: path = '%s'\n", __FUNCTION__, path); | 138 | pr_debug("%s: path = '%s'\n", __FUNCTION__, path); |
138 | 139 | ||
139 | return 0; | 140 | return 0; |
@@ -141,27 +142,16 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | |||
141 | 142 | ||
142 | static int sysfs_getlink(struct dentry *dentry, char * path) | 143 | static int sysfs_getlink(struct dentry *dentry, char * path) |
143 | { | 144 | { |
144 | struct kobject *kobj, *target_kobj; | 145 | struct sysfs_dirent *sd = dentry->d_fsdata; |
145 | int error = 0; | 146 | struct sysfs_dirent *parent_sd = sd->s_parent; |
147 | struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd; | ||
148 | int error; | ||
146 | 149 | ||
147 | kobj = sysfs_get_kobject(dentry->d_parent); | 150 | mutex_lock(&sysfs_mutex); |
148 | if (!kobj) | 151 | error = sysfs_get_target_path(parent_sd, target_sd, path); |
149 | return -EINVAL; | 152 | mutex_unlock(&sysfs_mutex); |
150 | 153 | ||
151 | target_kobj = sysfs_get_kobject(dentry); | ||
152 | if (!target_kobj) { | ||
153 | kobject_put(kobj); | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | down_read(&sysfs_rename_sem); | ||
158 | error = sysfs_get_target_path(kobj, target_kobj, path); | ||
159 | up_read(&sysfs_rename_sem); | ||
160 | |||
161 | kobject_put(kobj); | ||
162 | kobject_put(target_kobj); | ||
163 | return error; | 154 | return error; |
164 | |||
165 | } | 155 | } |
166 | 156 | ||
167 | static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) | 157 | static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) |