aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/symlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/sysfs/symlink.c')
-rw-r--r--fs/sysfs/symlink.c150
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
14static int object_depth(struct kobject * kobj) 14static 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
22static int object_path_length(struct kobject * kobj) 24static 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
33static void fill_object_path(struct kobject * kobj, char * buffer, int length) 34static 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
48static 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);
73exit2:
74 kfree(sl);
75exit1:
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 */
85int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) 53int 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
115void sysfs_remove_link(struct kobject * kobj, const char * name) 116void 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
120static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, 121static 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
142static int sysfs_getlink(struct dentry *dentry, char * path) 143static 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
167static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) 157static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)