patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / fs / sysfs / symlink.c
1 /*
2  * symlink.c - operations for sysfs symlinks.
3  */
4
5 #include <linux/fs.h>
6 #include <linux/module.h>
7 #include <linux/kobject.h>
8
9 #include "sysfs.h"
10
11 static struct inode_operations sysfs_symlink_inode_operations = {
12         .readlink = sysfs_readlink,
13         .follow_link = sysfs_follow_link,
14 };
15
16 static int init_symlink(struct inode * inode)
17 {
18         inode->i_op = &sysfs_symlink_inode_operations;
19         return 0;
20 }
21
22 static int object_depth(struct kobject * kobj)
23 {
24         struct kobject * p = kobj;
25         int depth = 0;
26         do { depth++; } while ((p = p->parent));
27         return depth;
28 }
29
30 static int object_path_length(struct kobject * kobj)
31 {
32         struct kobject * p = kobj;
33         int length = 1;
34         do {
35                 length += strlen(kobject_name(p)) + 1;
36                 p = p->parent;
37         } while (p);
38         return length;
39 }
40
41 static void fill_object_path(struct kobject * kobj, char * buffer, int length)
42 {
43         struct kobject * p;
44
45         --length;
46         for (p = kobj; p; p = p->parent) {
47                 int cur = strlen(kobject_name(p));
48
49                 /* back up enough to print this bus id with '/' */
50                 length -= cur;
51                 strncpy(buffer + length,kobject_name(p),cur);
52                 *(buffer + --length) = '/';
53         }
54 }
55
56 /**
57  *      sysfs_create_link - create symlink between two objects.
58  *      @kobj:  object whose directory we're creating the link in.
59  *      @target:        object we're pointing to.
60  *      @name:          name of the symlink.
61  */
62 int sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name)
63 {
64         struct dentry * dentry = kobj->dentry;
65         struct dentry * d;
66         int error = 0;
67
68         down(&dentry->d_inode->i_sem);
69         d = sysfs_get_dentry(dentry,name);
70         if (!IS_ERR(d)) {
71                 error = sysfs_create(d, S_IFLNK|S_IRWXUGO, init_symlink);
72                 if (!error)
73                         /* 
74                          * associate the link dentry with the target kobject 
75                          */
76                         d->d_fsdata = kobject_get(target);
77                 dput(d);
78         } else 
79                 error = PTR_ERR(d);
80         up(&dentry->d_inode->i_sem);
81         return error;
82 }
83
84
85 /**
86  *      sysfs_remove_link - remove symlink in object's directory.
87  *      @kobj:  object we're acting for.
88  *      @name:  name of the symlink to remove.
89  */
90
91 void sysfs_remove_link(struct kobject * kobj, char * name)
92 {
93         sysfs_hash_and_remove(kobj->dentry,name);
94 }
95
96 static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target,
97                                    char *path)
98 {
99         char * s;
100         int depth, size;
101
102         depth = object_depth(kobj);
103         size = object_path_length(target) + depth * 3 - 1;
104         if (size > PATH_MAX)
105                 return -ENAMETOOLONG;
106
107         pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size);
108
109         for (s = path; depth--; s += 3)
110                 strcpy(s,"../");
111
112         fill_object_path(target, path, size);
113         pr_debug("%s: path = '%s'\n", __FUNCTION__, path);
114
115         return 0;
116 }
117
118 static int sysfs_getlink(struct dentry *dentry, char * path)
119 {
120         struct kobject *kobj, *target_kobj;
121         int error = 0;
122
123         kobj = sysfs_get_kobject(dentry->d_parent);
124         if (!kobj)
125                 return -EINVAL;
126
127         target_kobj = sysfs_get_kobject(dentry);
128         if (!target_kobj) {
129                 kobject_put(kobj);
130                 return -EINVAL;
131         }
132
133         down_read(&sysfs_rename_sem);
134         error = sysfs_get_target_path(kobj, target_kobj, path);
135         up_read(&sysfs_rename_sem);
136         
137         kobject_put(kobj);
138         kobject_put(target_kobj);
139         return error;
140
141 }
142
143 int sysfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
144 {
145         int error = 0;
146         unsigned long page = get_zeroed_page(GFP_KERNEL);
147
148         if (!page)
149                 return -ENOMEM;
150
151         error = sysfs_getlink(dentry, (char *) page);
152         if (!error)
153                 error = vfs_readlink(dentry, buffer, buflen, (char *) page);
154
155         free_page(page);
156
157         return error;
158 }
159
160 int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
161 {
162         int error = 0;
163         unsigned long page = get_zeroed_page(GFP_KERNEL);
164
165         if (!page)
166                 return -ENOMEM;
167
168         error = sysfs_getlink(dentry, (char *) page); 
169         if (!error)
170                 error = vfs_follow_link(nd, (char *) page);
171
172         free_page(page);
173
174         return error;
175 }
176
177 EXPORT_SYMBOL(sysfs_create_link);
178 EXPORT_SYMBOL(sysfs_remove_link);
179