sysfs: flatten cleanup paths in sysfs_add_link() and create_dir()
[powerpc.git] / fs / sysfs / symlink.c
1 /*
2  * symlink.c - operations for sysfs symlinks.
3  */
4
5 #include <linux/fs.h>
6 #include <linux/mount.h>
7 #include <linux/module.h>
8 #include <linux/kobject.h>
9 #include <linux/namei.h>
10 #include <asm/semaphore.h>
11
12 #include "sysfs.h"
13
14 static int object_depth(struct kobject * kobj)
15 {
16         struct kobject * p = kobj;
17         int depth = 0;
18         do { depth++; } while ((p = p->parent));
19         return depth;
20 }
21
22 static int object_path_length(struct kobject * kobj)
23 {
24         struct kobject * p = kobj;
25         int length = 1;
26         do {
27                 length += strlen(kobject_name(p)) + 1;
28                 p = p->parent;
29         } while (p);
30         return length;
31 }
32
33 static void fill_object_path(struct kobject * kobj, char * buffer, int length)
34 {
35         struct kobject * p;
36
37         --length;
38         for (p = kobj; p; p = p->parent) {
39                 int cur = strlen(kobject_name(p));
40
41                 /* back up enough to print this bus id with '/' */
42                 length -= cur;
43                 strncpy(buffer + length,kobject_name(p),cur);
44                 *(buffer + --length) = '/';
45         }
46 }
47
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;
53
54         error = -ENOMEM;
55         sl = kzalloc(sizeof(*sl), GFP_KERNEL);
56         if (!sl)
57                 goto err_out;
58
59         sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
60         if (!sl->link_name)
61                 goto err_out;
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                 goto err_out;
70
71         return 0;
72
73  err_out:
74         if (sl) {
75                 kobject_put(sl->target_kobj);
76                 kfree(sl->link_name);
77                 kfree(sl);
78         }
79         return error;
80 }
81
82 /**
83  *      sysfs_create_link - create symlink between two objects.
84  *      @kobj:  object whose directory we're creating the link in.
85  *      @target:        object we're pointing to.
86  *      @name:          name of the symlink.
87  */
88 int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
89 {
90         struct dentry *dentry = NULL;
91         int error = -EEXIST;
92
93         BUG_ON(!name);
94
95         if (!kobj) {
96                 if (sysfs_mount && sysfs_mount->mnt_sb)
97                         dentry = sysfs_mount->mnt_sb->s_root;
98         } else
99                 dentry = kobj->dentry;
100
101         if (!dentry)
102                 return -EFAULT;
103
104         mutex_lock(&dentry->d_inode->i_mutex);
105         if (!sysfs_dirent_exist(dentry->d_fsdata, name))
106                 error = sysfs_add_link(dentry, name, target);
107         mutex_unlock(&dentry->d_inode->i_mutex);
108         return error;
109 }
110
111
112 /**
113  *      sysfs_remove_link - remove symlink in object's directory.
114  *      @kobj:  object we're acting for.
115  *      @name:  name of the symlink to remove.
116  */
117
118 void sysfs_remove_link(struct kobject * kobj, const char * name)
119 {
120         sysfs_hash_and_remove(kobj->dentry,name);
121 }
122
123 static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target,
124                                  char *path)
125 {
126         char * s;
127         int depth, size;
128
129         depth = object_depth(kobj);
130         size = object_path_length(target) + depth * 3 - 1;
131         if (size > PATH_MAX)
132                 return -ENAMETOOLONG;
133
134         pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);
135
136         for (s = path; depth--; s += 3)
137                 strcpy(s,"../");
138
139         fill_object_path(target, path, size);
140         pr_debug("%s: path = '%s'\n", __FUNCTION__, path);
141
142         return 0;
143 }
144
145 static int sysfs_getlink(struct dentry *dentry, char * path)
146 {
147         struct kobject *kobj, *target_kobj;
148         int error = 0;
149
150         kobj = sysfs_get_kobject(dentry->d_parent);
151         if (!kobj)
152                 return -EINVAL;
153
154         target_kobj = sysfs_get_kobject(dentry);
155         if (!target_kobj) {
156                 kobject_put(kobj);
157                 return -EINVAL;
158         }
159
160         down_read(&sysfs_rename_sem);
161         error = sysfs_get_target_path(kobj, target_kobj, path);
162         up_read(&sysfs_rename_sem);
163         
164         kobject_put(kobj);
165         kobject_put(target_kobj);
166         return error;
167
168 }
169
170 static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
171 {
172         int error = -ENOMEM;
173         unsigned long page = get_zeroed_page(GFP_KERNEL);
174         if (page)
175                 error = sysfs_getlink(dentry, (char *) page); 
176         nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
177         return NULL;
178 }
179
180 static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
181 {
182         char *page = nd_get_link(nd);
183         if (!IS_ERR(page))
184                 free_page((unsigned long)page);
185 }
186
187 const struct inode_operations sysfs_symlink_inode_operations = {
188         .readlink = generic_readlink,
189         .follow_link = sysfs_follow_link,
190         .put_link = sysfs_put_link,
191 };
192
193
194 EXPORT_SYMBOL_GPL(sysfs_create_link);
195 EXPORT_SYMBOL_GPL(sysfs_remove_link);