Blame view
fs/sysfs/symlink.c
4.06 KB
1da177e4c
|
1 2 3 4 5 |
/* * symlink.c - operations for sysfs symlinks. */ #include <linux/fs.h> |
ceeee1fb2
|
6 |
#include <linux/mount.h> |
1da177e4c
|
7 8 9 |
#include <linux/module.h> #include <linux/kobject.h> #include <linux/namei.h> |
94bebf4d1
|
10 |
#include <asm/semaphore.h> |
1da177e4c
|
11 12 |
#include "sysfs.h" |
2b29ac252
|
13 |
static int object_depth(struct sysfs_dirent *sd) |
1da177e4c
|
14 |
{ |
1da177e4c
|
15 |
int depth = 0; |
2b29ac252
|
16 17 18 |
for (; sd->s_parent; sd = sd->s_parent) depth++; |
1da177e4c
|
19 20 |
return depth; } |
2b29ac252
|
21 |
static int object_path_length(struct sysfs_dirent * sd) |
1da177e4c
|
22 |
{ |
1da177e4c
|
23 |
int length = 1; |
2b29ac252
|
24 25 26 |
for (; sd->s_parent; sd = sd->s_parent) length += strlen(sd->s_name) + 1; |
1da177e4c
|
27 28 |
return length; } |
2b29ac252
|
29 |
static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length) |
1da177e4c
|
30 |
{ |
1da177e4c
|
31 |
--length; |
2b29ac252
|
32 33 |
for (; sd->s_parent; sd = sd->s_parent) { int cur = strlen(sd->s_name); |
1da177e4c
|
34 35 36 |
/* back up enough to print this bus id with '/' */ length -= cur; |
2b29ac252
|
37 |
strncpy(buffer + length, sd->s_name, cur); |
1da177e4c
|
38 39 40 |
*(buffer + --length) = '/'; } } |
1da177e4c
|
41 42 43 44 45 46 |
/** * 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. */ |
e3a15db24
|
47 |
int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) |
1da177e4c
|
48 |
{ |
2b29ac252
|
49 50 |
struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; |
3007e997d
|
51 |
struct sysfs_dirent *sd = NULL; |
fb6896da3
|
52 |
struct sysfs_addrm_cxt acxt; |
3007e997d
|
53 |
int error; |
1da177e4c
|
54 |
|
ceeee1fb2
|
55 56 57 58 |
BUG_ON(!name); if (!kobj) { if (sysfs_mount && sysfs_mount->mnt_sb) |
608e266a2
|
59 |
parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata; |
ceeee1fb2
|
60 |
} else |
608e266a2
|
61 |
parent_sd = kobj->sd; |
ceeee1fb2
|
62 |
|
3007e997d
|
63 |
error = -EFAULT; |
608e266a2
|
64 |
if (!parent_sd) |
3007e997d
|
65 |
goto out_put; |
2b29ac252
|
66 |
|
608e266a2
|
67 |
/* target->sd can go away beneath us but is protected with |
5f9953237
|
68 |
* sysfs_assoc_lock. Fetch target_sd from it. |
2b29ac252
|
69 |
*/ |
5f9953237
|
70 |
spin_lock(&sysfs_assoc_lock); |
608e266a2
|
71 72 |
if (target->sd) target_sd = sysfs_get(target->sd); |
5f9953237
|
73 |
spin_unlock(&sysfs_assoc_lock); |
2b29ac252
|
74 |
|
3007e997d
|
75 |
error = -ENOENT; |
2b29ac252
|
76 |
if (!target_sd) |
3007e997d
|
77 78 79 80 81 82 |
goto out_put; error = -ENOMEM; sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); if (!sd) goto out_put; |
a1da4dfe3
|
83 |
|
3007e997d
|
84 |
sd->s_elem.symlink.target_sd = target_sd; |
a1da4dfe3
|
85 |
target_sd = NULL; /* reference is now owned by the symlink */ |
1da177e4c
|
86 |
|
fb6896da3
|
87 |
sysfs_addrm_start(&acxt, parent_sd); |
2b29ac252
|
88 |
|
fb6896da3
|
89 90 91 92 |
if (!sysfs_find_dirent(parent_sd, name)) { sysfs_add_one(&acxt, sd); sysfs_link_sibling(sd); } |
2b29ac252
|
93 |
|
967e35dcc
|
94 95 96 97 98 99 |
if (!sysfs_addrm_finish(&acxt)) { error = -EEXIST; goto out_put; } return 0; |
fb6896da3
|
100 |
|
3007e997d
|
101 102 103 |
out_put: sysfs_put(target_sd); sysfs_put(sd); |
1da177e4c
|
104 105 106 107 108 109 110 111 112 |
return error; } /** * sysfs_remove_link - remove symlink in object's directory. * @kobj: object we're acting for. * @name: name of the symlink to remove. */ |
e3a15db24
|
113 |
void sysfs_remove_link(struct kobject * kobj, const char * name) |
1da177e4c
|
114 |
{ |
608e266a2
|
115 |
sysfs_hash_and_remove(kobj->sd, name); |
1da177e4c
|
116 |
} |
2b29ac252
|
117 118 |
static int sysfs_get_target_path(struct sysfs_dirent * parent_sd, struct sysfs_dirent * target_sd, char *path) |
1da177e4c
|
119 120 121 |
{ char * s; int depth, size; |
2b29ac252
|
122 123 |
depth = object_depth(parent_sd); size = object_path_length(target_sd) + depth * 3 - 1; |
1da177e4c
|
124 125 126 127 128 129 130 131 |
if (size > PATH_MAX) return -ENAMETOOLONG; pr_debug("%s: depth = %d, size = %d ", __FUNCTION__, depth, size); for (s = path; depth--; s += 3) strcpy(s,"../"); |
2b29ac252
|
132 |
fill_object_path(target_sd, path, size); |
1da177e4c
|
133 134 135 136 137 138 139 140 |
pr_debug("%s: path = '%s' ", __FUNCTION__, path); return 0; } static int sysfs_getlink(struct dentry *dentry, char * path) { |
2b29ac252
|
141 142 143 144 |
struct sysfs_dirent *sd = dentry->d_fsdata; struct sysfs_dirent *parent_sd = sd->s_parent; struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd; int error; |
1da177e4c
|
145 |
|
3007e997d
|
146 |
mutex_lock(&sysfs_mutex); |
2b29ac252
|
147 |
error = sysfs_get_target_path(parent_sd, target_sd, path); |
3007e997d
|
148 |
mutex_unlock(&sysfs_mutex); |
1da177e4c
|
149 |
|
2b29ac252
|
150 |
return error; |
1da177e4c
|
151 |
} |
cc314eef0
|
152 |
static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) |
1da177e4c
|
153 154 155 156 157 158 |
{ int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); if (page) error = sysfs_getlink(dentry, (char *) page); nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); |
cc314eef0
|
159 |
return NULL; |
1da177e4c
|
160 |
} |
cc314eef0
|
161 |
static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) |
1da177e4c
|
162 163 164 165 166 |
{ char *page = nd_get_link(nd); if (!IS_ERR(page)) free_page((unsigned long)page); } |
c5ef1c42c
|
167 |
const struct inode_operations sysfs_symlink_inode_operations = { |
1da177e4c
|
168 169 170 171 172 173 174 175 |
.readlink = generic_readlink, .follow_link = sysfs_follow_link, .put_link = sysfs_put_link, }; EXPORT_SYMBOL_GPL(sysfs_create_link); EXPORT_SYMBOL_GPL(sysfs_remove_link); |