aboutsummaryrefslogtreecommitdiffstats
path: root/fs/kernfs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-11-28 14:54:35 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-11-29 21:08:39 -0500
commit2072f1afddfe9fa00c1c0c79f8986707324ec65b (patch)
tree636f5f2e0c44082afcded77328bcbcdb62b58f51 /fs/kernfs
parent414985ae23c031efbd6d16d484dea8b5de28b8f7 (diff)
sysfs, kernfs: move symlink core code to fs/kernfs/symlink.c
Move core symlink code to fs/kernfs/symlink.c. fs/sysfs/symlink.c now only contains sysfs wrappers around kernfs interfaces. The respective declarations in fs/sysfs/sysfs.h are moved to fs/kernfs/kernfs-internal.h. This is pure relocation. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/kernfs')
-rw-r--r--fs/kernfs/kernfs-internal.h5
-rw-r--r--fs/kernfs/symlink.c139
2 files changed, 144 insertions, 0 deletions
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index 38e3a163e5ad..62ae35f997f7 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -149,4 +149,9 @@ extern const struct file_operations kernfs_file_operations;
149 149
150void sysfs_unmap_bin_file(struct sysfs_dirent *sd); 150void sysfs_unmap_bin_file(struct sysfs_dirent *sd);
151 151
152/*
153 * symlink.c
154 */
155extern const struct inode_operations sysfs_symlink_inode_operations;
156
152#endif /* __KERNFS_INTERNAL_H */ 157#endif /* __KERNFS_INTERNAL_H */
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index 2578715baf0e..af3570bb4303 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -7,3 +7,142 @@
7 * 7 *
8 * This file is released under the GPLv2. 8 * This file is released under the GPLv2.
9 */ 9 */
10
11#include <linux/fs.h>
12#include <linux/gfp.h>
13#include <linux/namei.h>
14
15#include "kernfs-internal.h"
16
17/**
18 * kernfs_create_link - create a symlink
19 * @parent: directory to create the symlink in
20 * @name: name of the symlink
21 * @target: target node for the symlink to point to
22 *
23 * Returns the created node on success, ERR_PTR() value on error.
24 */
25struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent,
26 const char *name,
27 struct sysfs_dirent *target)
28{
29 struct sysfs_dirent *sd;
30 struct sysfs_addrm_cxt acxt;
31 int error;
32
33 sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
34 if (!sd)
35 return ERR_PTR(-ENOMEM);
36
37 if (parent->s_flags & SYSFS_FLAG_NS)
38 sd->s_ns = target->s_ns;
39 sd->s_symlink.target_sd = target;
40 kernfs_get(target); /* ref owned by symlink */
41
42 sysfs_addrm_start(&acxt);
43 error = sysfs_add_one(&acxt, sd, parent);
44 sysfs_addrm_finish(&acxt);
45
46 if (!error)
47 return sd;
48
49 kernfs_put(sd);
50 return ERR_PTR(error);
51}
52
53static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,
54 struct sysfs_dirent *target_sd, char *path)
55{
56 struct sysfs_dirent *base, *sd;
57 char *s = path;
58 int len = 0;
59
60 /* go up to the root, stop at the base */
61 base = parent_sd;
62 while (base->s_parent) {
63 sd = target_sd->s_parent;
64 while (sd->s_parent && base != sd)
65 sd = sd->s_parent;
66
67 if (base == sd)
68 break;
69
70 strcpy(s, "../");
71 s += 3;
72 base = base->s_parent;
73 }
74
75 /* determine end of target string for reverse fillup */
76 sd = target_sd;
77 while (sd->s_parent && sd != base) {
78 len += strlen(sd->s_name) + 1;
79 sd = sd->s_parent;
80 }
81
82 /* check limits */
83 if (len < 2)
84 return -EINVAL;
85 len--;
86 if ((s - path) + len > PATH_MAX)
87 return -ENAMETOOLONG;
88
89 /* reverse fillup of target string from target to base */
90 sd = target_sd;
91 while (sd->s_parent && sd != base) {
92 int slen = strlen(sd->s_name);
93
94 len -= slen;
95 strncpy(s + len, sd->s_name, slen);
96 if (len)
97 s[--len] = '/';
98
99 sd = sd->s_parent;
100 }
101
102 return 0;
103}
104
105static int sysfs_getlink(struct dentry *dentry, char *path)
106{
107 struct sysfs_dirent *sd = dentry->d_fsdata;
108 struct sysfs_dirent *parent_sd = sd->s_parent;
109 struct sysfs_dirent *target_sd = sd->s_symlink.target_sd;
110 int error;
111
112 mutex_lock(&sysfs_mutex);
113 error = sysfs_get_target_path(parent_sd, target_sd, path);
114 mutex_unlock(&sysfs_mutex);
115
116 return error;
117}
118
119static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
120{
121 int error = -ENOMEM;
122 unsigned long page = get_zeroed_page(GFP_KERNEL);
123 if (page) {
124 error = sysfs_getlink(dentry, (char *) page);
125 if (error < 0)
126 free_page((unsigned long)page);
127 }
128 nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
129 return NULL;
130}
131
132static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd,
133 void *cookie)
134{
135 char *page = nd_get_link(nd);
136 if (!IS_ERR(page))
137 free_page((unsigned long)page);
138}
139
140const struct inode_operations sysfs_symlink_inode_operations = {
141 .setxattr = sysfs_setxattr,
142 .readlink = generic_readlink,
143 .follow_link = sysfs_follow_link,
144 .put_link = sysfs_put_link,
145 .setattr = sysfs_setattr,
146 .getattr = sysfs_getattr,
147 .permission = sysfs_permission,
148};