Blame view
fs/sysfs/symlink.c
7.11 KB
1da177e4c Linux-2.6.12-rc2 |
1 |
/* |
6d66f5cd2 sysfs: add copyri... |
2 3 4 5 6 7 8 9 10 |
* fs/sysfs/symlink.c - sysfs symlink implementation * * Copyright (c) 2001-3 Patrick Mochel * Copyright (c) 2007 SUSE Linux Products GmbH * Copyright (c) 2007 Tejun Heo <teheo@suse.de> * * This file is released under the GPLv2. * * Please see Documentation/filesystems/sysfs.txt for more information. |
1da177e4c Linux-2.6.12-rc2 |
11 12 13 |
*/ #include <linux/fs.h> |
5a0e3ad6a include cleanup: ... |
14 |
#include <linux/gfp.h> |
ceeee1fb2 SYSFS: allow sysf... |
15 |
#include <linux/mount.h> |
1da177e4c Linux-2.6.12-rc2 |
16 17 18 |
#include <linux/module.h> #include <linux/kobject.h> #include <linux/namei.h> |
869512ab5 sysfs: cleanup se... |
19 |
#include <linux/mutex.h> |
ddd29ec65 sysfs: Add labeli... |
20 |
#include <linux/security.h> |
1da177e4c Linux-2.6.12-rc2 |
21 22 |
#include "sysfs.h" |
36ce6dad6 driver core: Supp... |
23 24 |
static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, const char *name, int warn) |
1da177e4c Linux-2.6.12-rc2 |
25 |
{ |
2b29ac252 sysfs: reimplemen... |
26 27 |
struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; |
3007e997d sysfs: use sysfs_... |
28 |
struct sysfs_dirent *sd = NULL; |
fb6896da3 sysfs: restructur... |
29 |
struct sysfs_addrm_cxt acxt; |
96d6523ad sysfs: Don't allo... |
30 |
enum kobj_ns_type ns_type; |
3007e997d sysfs: use sysfs_... |
31 |
int error; |
1da177e4c Linux-2.6.12-rc2 |
32 |
|
ceeee1fb2 SYSFS: allow sysf... |
33 |
BUG_ON(!name); |
7d0c7d676 sysfs: Make sysfs... |
34 35 36 |
if (!kobj) parent_sd = &sysfs_root; else |
608e266a2 sysfs: make kobj ... |
37 |
parent_sd = kobj->sd; |
ceeee1fb2 SYSFS: allow sysf... |
38 |
|
3007e997d sysfs: use sysfs_... |
39 |
error = -EFAULT; |
608e266a2 sysfs: make kobj ... |
40 |
if (!parent_sd) |
3007e997d sysfs: use sysfs_... |
41 |
goto out_put; |
2b29ac252 sysfs: reimplemen... |
42 |
|
608e266a2 sysfs: make kobj ... |
43 |
/* target->sd can go away beneath us but is protected with |
5f9953237 sysfs: consolidat... |
44 |
* sysfs_assoc_lock. Fetch target_sd from it. |
2b29ac252 sysfs: reimplemen... |
45 |
*/ |
5f9953237 sysfs: consolidat... |
46 |
spin_lock(&sysfs_assoc_lock); |
608e266a2 sysfs: make kobj ... |
47 48 |
if (target->sd) target_sd = sysfs_get(target->sd); |
5f9953237 sysfs: consolidat... |
49 |
spin_unlock(&sysfs_assoc_lock); |
2b29ac252 sysfs: reimplemen... |
50 |
|
3007e997d sysfs: use sysfs_... |
51 |
error = -ENOENT; |
2b29ac252 sysfs: reimplemen... |
52 |
if (!target_sd) |
3007e997d sysfs: use sysfs_... |
53 54 55 56 57 58 |
goto out_put; error = -ENOMEM; sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); if (!sd) goto out_put; |
a1da4dfe3 sysfs: kill an ex... |
59 |
|
96d6523ad sysfs: Don't allo... |
60 61 |
ns_type = sysfs_ns_type(parent_sd); if (ns_type) |
3ff195b01 sysfs: Implement ... |
62 |
sd->s_ns = target->ktype->namespace(target); |
b1fc3d614 sysfs: make s_ele... |
63 |
sd->s_symlink.target_sd = target_sd; |
a1da4dfe3 sysfs: kill an ex... |
64 |
target_sd = NULL; /* reference is now owned by the symlink */ |
1da177e4c Linux-2.6.12-rc2 |
65 |
|
fb6896da3 sysfs: restructur... |
66 |
sysfs_addrm_start(&acxt, parent_sd); |
96d6523ad sysfs: Don't allo... |
67 |
/* Symlinks must be between directories with the same ns_type */ |
d33002129 sysfs: allow crea... |
68 69 |
if (!ns_type || (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) { |
96d6523ad sysfs: Don't allo... |
70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
if (warn) error = sysfs_add_one(&acxt, sd); else error = __sysfs_add_one(&acxt, sd); } else { error = -EINVAL; WARN(1, KERN_WARNING "sysfs: symlink across ns_types %s/%s -> %s/%s ", parent_sd->s_name, sd->s_name, sd->s_symlink.target_sd->s_parent->s_name, sd->s_symlink.target_sd->s_name); } |
23dc27995 sysfs: make sysfs... |
84 |
sysfs_addrm_finish(&acxt); |
2b29ac252 sysfs: reimplemen... |
85 |
|
23dc27995 sysfs: make sysfs... |
86 |
if (error) |
967e35dcc sysfs: cosmetic c... |
87 |
goto out_put; |
967e35dcc sysfs: cosmetic c... |
88 89 |
return 0; |
fb6896da3 sysfs: restructur... |
90 |
|
3007e997d sysfs: use sysfs_... |
91 92 93 |
out_put: sysfs_put(target_sd); sysfs_put(sd); |
1da177e4c Linux-2.6.12-rc2 |
94 95 |
return error; } |
1da177e4c Linux-2.6.12-rc2 |
96 |
/** |
36ce6dad6 driver core: Supp... |
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
* sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. * @target: object we're pointing to. * @name: name of the symlink. */ int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name) { return sysfs_do_create_link(kobj, target, name, 1); } /** * sysfs_create_link_nowarn - create symlink between two objects. * @kobj: object whose directory we're creating the link in. * @target: object we're pointing to. * @name: name of the symlink. * * This function does the same as sysf_create_link(), but it * doesn't warn if the link already exists. */ int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, const char *name) { return sysfs_do_create_link(kobj, target, name, 0); } /** |
746edb7ae sysfs: Implement ... |
124 125 126 127 128 129 130 131 132 133 134 135 136 |
* sysfs_delete_link - remove symlink in object's directory. * @kobj: object we're acting for. * @targ: object we're pointing to. * @name: name of the symlink to remove. * * Unlike sysfs_remove_link sysfs_delete_link has enough information * to successfully delete symlinks in tagged directories. */ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, const char *name) { const void *ns = NULL; spin_lock(&sysfs_assoc_lock); |
521d04535 sysfs: sysfs_dele... |
137 |
if (targ->sd && sysfs_ns_type(kobj->sd)) |
746edb7ae sysfs: Implement ... |
138 139 140 141 142 143 |
ns = targ->sd->s_ns; spin_unlock(&sysfs_assoc_lock); sysfs_hash_and_remove(kobj->sd, ns, name); } /** |
1da177e4c Linux-2.6.12-rc2 |
144 145 146 147 |
* sysfs_remove_link - remove symlink in object's directory. * @kobj: object we're acting for. * @name: name of the symlink to remove. */ |
e3a15db24 [PATCH] sysfs_{cr... |
148 |
void sysfs_remove_link(struct kobject * kobj, const char * name) |
1da177e4c Linux-2.6.12-rc2 |
149 |
{ |
a839c5afc sysfs: Allow remo... |
150 151 152 153 154 155 |
struct sysfs_dirent *parent_sd = NULL; if (!kobj) parent_sd = &sysfs_root; else parent_sd = kobj->sd; |
3ff195b01 sysfs: Implement ... |
156 |
sysfs_hash_and_remove(parent_sd, NULL, name); |
1da177e4c Linux-2.6.12-rc2 |
157 |
} |
7cb32942d sysfs: Implement ... |
158 159 160 161 162 163 164 165 166 167 168 169 170 |
/** * sysfs_rename_link - rename symlink in object's directory. * @kobj: object we're acting for. * @targ: object we're pointing to. * @old: previous name of the symlink. * @new: new name of the symlink. * * A helper function for the common rename symlink idiom. */ int sysfs_rename_link(struct kobject *kobj, struct kobject *targ, const char *old, const char *new) { struct sysfs_dirent *parent_sd, *sd = NULL; |
3ff195b01 sysfs: Implement ... |
171 |
const void *old_ns = NULL, *new_ns = NULL; |
7cb32942d sysfs: Implement ... |
172 173 174 175 176 177 |
int result; if (!kobj) parent_sd = &sysfs_root; else parent_sd = kobj->sd; |
3ff195b01 sysfs: Implement ... |
178 179 |
if (targ->sd) old_ns = targ->sd->s_ns; |
7cb32942d sysfs: Implement ... |
180 |
result = -ENOENT; |
3ff195b01 sysfs: Implement ... |
181 |
sd = sysfs_get_dirent(parent_sd, old_ns, old); |
7cb32942d sysfs: Implement ... |
182 183 184 185 186 187 188 189 |
if (!sd) goto out; result = -EINVAL; if (sysfs_type(sd) != SYSFS_KOBJ_LINK) goto out; if (sd->s_symlink.target_sd->s_dir.kobj != targ) goto out; |
3ff195b01 sysfs: Implement ... |
190 191 192 193 |
if (sysfs_ns_type(parent_sd)) new_ns = targ->ktype->namespace(targ); result = sysfs_rename(sd, parent_sd, new_ns, new); |
7cb32942d sysfs: Implement ... |
194 195 196 197 198 |
out: sysfs_put(sd); return result; } |
2f90a8518 sysfs: create opt... |
199 200 |
static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, struct sysfs_dirent *target_sd, char *path) |
1da177e4c Linux-2.6.12-rc2 |
201 |
{ |
2f90a8518 sysfs: create opt... |
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
struct sysfs_dirent *base, *sd; char *s = path; int len = 0; /* go up to the root, stop at the base */ base = parent_sd; 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; } |
1da177e4c Linux-2.6.12-rc2 |
227 |
|
2f90a8518 sysfs: create opt... |
228 229 230 231 232 |
/* check limits */ if (len < 2) return -EINVAL; len--; if ((s - path) + len > PATH_MAX) |
1da177e4c Linux-2.6.12-rc2 |
233 |
return -ENAMETOOLONG; |
2f90a8518 sysfs: create opt... |
234 235 236 237 |
/* reverse fillup of target string from target to base */ sd = target_sd; while (sd->s_parent && sd != base) { int slen = strlen(sd->s_name); |
1da177e4c Linux-2.6.12-rc2 |
238 |
|
2f90a8518 sysfs: create opt... |
239 240 241 242 |
len -= slen; strncpy(s + len, sd->s_name, slen); if (len) s[--len] = '/'; |
1da177e4c Linux-2.6.12-rc2 |
243 |
|
2f90a8518 sysfs: create opt... |
244 245 |
sd = sd->s_parent; } |
1da177e4c Linux-2.6.12-rc2 |
246 247 248 249 250 251 |
return 0; } static int sysfs_getlink(struct dentry *dentry, char * path) { |
2b29ac252 sysfs: reimplemen... |
252 253 |
struct sysfs_dirent *sd = dentry->d_fsdata; struct sysfs_dirent *parent_sd = sd->s_parent; |
b1fc3d614 sysfs: make s_ele... |
254 |
struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; |
2b29ac252 sysfs: reimplemen... |
255 |
int error; |
1da177e4c Linux-2.6.12-rc2 |
256 |
|
3007e997d sysfs: use sysfs_... |
257 |
mutex_lock(&sysfs_mutex); |
2b29ac252 sysfs: reimplemen... |
258 |
error = sysfs_get_target_path(parent_sd, target_sd, path); |
3007e997d sysfs: use sysfs_... |
259 |
mutex_unlock(&sysfs_mutex); |
1da177e4c Linux-2.6.12-rc2 |
260 |
|
2b29ac252 sysfs: reimplemen... |
261 |
return error; |
1da177e4c Linux-2.6.12-rc2 |
262 |
} |
cc314eef0 Fix nasty ncpfs s... |
263 |
static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) |
1da177e4c Linux-2.6.12-rc2 |
264 265 266 |
{ int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); |
557411eb2 Sysfs: fix possib... |
267 |
if (page) { |
1da177e4c Linux-2.6.12-rc2 |
268 |
error = sysfs_getlink(dentry, (char *) page); |
557411eb2 Sysfs: fix possib... |
269 270 271 |
if (error < 0) free_page((unsigned long)page); } |
1da177e4c Linux-2.6.12-rc2 |
272 |
nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); |
cc314eef0 Fix nasty ncpfs s... |
273 |
return NULL; |
1da177e4c Linux-2.6.12-rc2 |
274 |
} |
cc314eef0 Fix nasty ncpfs s... |
275 |
static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) |
1da177e4c Linux-2.6.12-rc2 |
276 277 278 279 280 |
{ char *page = nd_get_link(nd); if (!IS_ERR(page)) free_page((unsigned long)page); } |
c5ef1c42c [PATCH] mark stru... |
281 |
const struct inode_operations sysfs_symlink_inode_operations = { |
c099aacd4 sysfs: Nicely ind... |
282 283 284 285 |
.setxattr = sysfs_setxattr, .readlink = generic_readlink, .follow_link = sysfs_follow_link, .put_link = sysfs_put_link, |
e61ab4ae4 sysfs: Implement ... |
286 287 288 |
.setattr = sysfs_setattr, .getattr = sysfs_getattr, .permission = sysfs_permission, |
1da177e4c Linux-2.6.12-rc2 |
289 290 291 292 293 |
}; EXPORT_SYMBOL_GPL(sysfs_create_link); EXPORT_SYMBOL_GPL(sysfs_remove_link); |
e0f43752a bridge: update sy... |
294 |
EXPORT_SYMBOL_GPL(sysfs_rename_link); |