Blame view

fs/sysfs/symlink.c 4.06 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
  /*
   * symlink.c - operations for sysfs symlinks.
   */
  
  #include <linux/fs.h>
ceeee1fb2   Greg Kroah-Hartman   SYSFS: allow sysf...
6
  #include <linux/mount.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
9
  #include <linux/module.h>
  #include <linux/kobject.h>
  #include <linux/namei.h>
94bebf4d1   Oliver Neukum   Driver core: fix ...
10
  #include <asm/semaphore.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
  
  #include "sysfs.h"
2b29ac252   Tejun Heo   sysfs: reimplemen...
13
  static int object_depth(struct sysfs_dirent *sd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  	int depth = 0;
2b29ac252   Tejun Heo   sysfs: reimplemen...
16
17
18
  
  	for (; sd->s_parent; sd = sd->s_parent)
  		depth++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  	return depth;
  }
2b29ac252   Tejun Heo   sysfs: reimplemen...
21
  static int object_path_length(struct sysfs_dirent * sd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  	int length = 1;
2b29ac252   Tejun Heo   sysfs: reimplemen...
24
25
26
  
  	for (; sd->s_parent; sd = sd->s_parent)
  		length += strlen(sd->s_name) + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
  	return length;
  }
2b29ac252   Tejun Heo   sysfs: reimplemen...
29
  static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  	--length;
2b29ac252   Tejun Heo   sysfs: reimplemen...
32
33
  	for (; sd->s_parent; sd = sd->s_parent) {
  		int cur = strlen(sd->s_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
  
  		/* back up enough to print this bus id with '/' */
  		length -= cur;
2b29ac252   Tejun Heo   sysfs: reimplemen...
37
  		strncpy(buffer + length, sd->s_name, cur);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
  		*(buffer + --length) = '/';
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dmitry Torokhov   [PATCH] sysfs_{cr...
47
  int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  {
2b29ac252   Tejun Heo   sysfs: reimplemen...
49
50
  	struct sysfs_dirent *parent_sd = NULL;
  	struct sysfs_dirent *target_sd = NULL;
3007e997d   Tejun Heo   sysfs: use sysfs_...
51
  	struct sysfs_dirent *sd = NULL;
fb6896da3   Tejun Heo   sysfs: restructur...
52
  	struct sysfs_addrm_cxt acxt;
3007e997d   Tejun Heo   sysfs: use sysfs_...
53
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54

ceeee1fb2   Greg Kroah-Hartman   SYSFS: allow sysf...
55
56
57
58
  	BUG_ON(!name);
  
  	if (!kobj) {
  		if (sysfs_mount && sysfs_mount->mnt_sb)
608e266a2   Tejun Heo   sysfs: make kobj ...
59
  			parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata;
ceeee1fb2   Greg Kroah-Hartman   SYSFS: allow sysf...
60
  	} else
608e266a2   Tejun Heo   sysfs: make kobj ...
61
  		parent_sd = kobj->sd;
ceeee1fb2   Greg Kroah-Hartman   SYSFS: allow sysf...
62

3007e997d   Tejun Heo   sysfs: use sysfs_...
63
  	error = -EFAULT;
608e266a2   Tejun Heo   sysfs: make kobj ...
64
  	if (!parent_sd)
3007e997d   Tejun Heo   sysfs: use sysfs_...
65
  		goto out_put;
2b29ac252   Tejun Heo   sysfs: reimplemen...
66

608e266a2   Tejun Heo   sysfs: make kobj ...
67
  	/* target->sd can go away beneath us but is protected with
5f9953237   Tejun Heo   sysfs: consolidat...
68
  	 * sysfs_assoc_lock.  Fetch target_sd from it.
2b29ac252   Tejun Heo   sysfs: reimplemen...
69
  	 */
5f9953237   Tejun Heo   sysfs: consolidat...
70
  	spin_lock(&sysfs_assoc_lock);
608e266a2   Tejun Heo   sysfs: make kobj ...
71
72
  	if (target->sd)
  		target_sd = sysfs_get(target->sd);
5f9953237   Tejun Heo   sysfs: consolidat...
73
  	spin_unlock(&sysfs_assoc_lock);
2b29ac252   Tejun Heo   sysfs: reimplemen...
74

3007e997d   Tejun Heo   sysfs: use sysfs_...
75
  	error = -ENOENT;
2b29ac252   Tejun Heo   sysfs: reimplemen...
76
  	if (!target_sd)
3007e997d   Tejun Heo   sysfs: use sysfs_...
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   Tejun Heo   sysfs: kill an ex...
83

3007e997d   Tejun Heo   sysfs: use sysfs_...
84
  	sd->s_elem.symlink.target_sd = target_sd;
a1da4dfe3   Tejun Heo   sysfs: kill an ex...
85
  	target_sd = NULL;	/* reference is now owned by the symlink */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86

fb6896da3   Tejun Heo   sysfs: restructur...
87
  	sysfs_addrm_start(&acxt, parent_sd);
2b29ac252   Tejun Heo   sysfs: reimplemen...
88

fb6896da3   Tejun Heo   sysfs: restructur...
89
90
91
92
  	if (!sysfs_find_dirent(parent_sd, name)) {
  		sysfs_add_one(&acxt, sd);
  		sysfs_link_sibling(sd);
  	}
2b29ac252   Tejun Heo   sysfs: reimplemen...
93

967e35dcc   Tejun Heo   sysfs: cosmetic c...
94
95
96
97
98
99
  	if (!sysfs_addrm_finish(&acxt)) {
  		error = -EEXIST;
  		goto out_put;
  	}
  
  	return 0;
fb6896da3   Tejun Heo   sysfs: restructur...
100

3007e997d   Tejun Heo   sysfs: use sysfs_...
101
102
103
   out_put:
  	sysfs_put(target_sd);
  	sysfs_put(sd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Dmitry Torokhov   [PATCH] sysfs_{cr...
113
  void sysfs_remove_link(struct kobject * kobj, const char * name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  {
608e266a2   Tejun Heo   sysfs: make kobj ...
115
  	sysfs_hash_and_remove(kobj->sd, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  }
2b29ac252   Tejun Heo   sysfs: reimplemen...
117
118
  static int sysfs_get_target_path(struct sysfs_dirent * parent_sd,
  				 struct sysfs_dirent * target_sd, char *path)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
  {
  	char * s;
  	int depth, size;
2b29ac252   Tejun Heo   sysfs: reimplemen...
122
123
  	depth = object_depth(parent_sd);
  	size = object_path_length(target_sd) + depth * 3 - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Tejun Heo   sysfs: reimplemen...
132
  	fill_object_path(target_sd, path, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Tejun Heo   sysfs: reimplemen...
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   Linus Torvalds   Linux-2.6.12-rc2
145

3007e997d   Tejun Heo   sysfs: use sysfs_...
146
  	mutex_lock(&sysfs_mutex);
2b29ac252   Tejun Heo   sysfs: reimplemen...
147
  	error = sysfs_get_target_path(parent_sd, target_sd, path);
3007e997d   Tejun Heo   sysfs: use sysfs_...
148
  	mutex_unlock(&sysfs_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149

2b29ac252   Tejun Heo   sysfs: reimplemen...
150
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
  }
cc314eef0   Linus Torvalds   Fix nasty ncpfs s...
152
  static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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   Linus Torvalds   Fix nasty ncpfs s...
159
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  }
cc314eef0   Linus Torvalds   Fix nasty ncpfs s...
161
  static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
  {
  	char *page = nd_get_link(nd);
  	if (!IS_ERR(page))
  		free_page((unsigned long)page);
  }
c5ef1c42c   Arjan van de Ven   [PATCH] mark stru...
167
  const struct inode_operations sysfs_symlink_inode_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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);