Blame view

fs/sysfs/inode.c 8.35 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
6d66f5cd2   Tejun Heo   sysfs: add copyri...
2
   * fs/sysfs/inode.c - basic sysfs inode and dentry operations
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   *
6d66f5cd2   Tejun Heo   sysfs: add copyri...
4
5
6
7
8
   * Copyright (c) 2001-3 Patrick Mochel
   * Copyright (c) 2007 SUSE Linux Products GmbH
   * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
   *
   * This file is released under the GPLv2.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
14
15
16
17
   *
   * Please see Documentation/filesystems/sysfs.txt for more information.
   */
  
  #undef DEBUG 
  
  #include <linux/pagemap.h>
  #include <linux/namei.h>
  #include <linux/backing-dev.h>
16f7e0fe2   Randy Dunlap   [PATCH] capable/c...
18
  #include <linux/capability.h>
995982ca7   Randy.Dunlap   sysfs_remove_bin_...
19
  #include <linux/errno.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
20
  #include <linux/sched.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
21
  #include <linux/slab.h>
ddd29ec65   David P. Quigley   sysfs: Add labeli...
22
23
  #include <linux/xattr.h>
  #include <linux/security.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
  #include "sysfs.h"
  
  extern struct super_block * sysfs_sb;
f5e54d6e5   Christoph Hellwig   [PATCH] mark addr...
27
  static const struct address_space_operations sysfs_aops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
  	.readpage	= simple_readpage,
800d15a53   Nick Piggin   implement simple ...
29
30
  	.write_begin	= simple_write_begin,
  	.write_end	= simple_write_end,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
  };
  
  static struct backing_dev_info sysfs_backing_dev_info = {
d993831fa   Jens Axboe   writeback: add na...
34
  	.name		= "sysfs",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  	.ra_pages	= 0,	/* No readahead */
e4ad08fe6   Miklos Szeredi   mm: bdi: add sepa...
36
  	.capabilities	= BDI_CAP_NO_ACCT_AND_WRITEBACK,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
  };
c5ef1c42c   Arjan van de Ven   [PATCH] mark stru...
38
  static const struct inode_operations sysfs_inode_operations ={
e61ab4ae4   Eric W. Biederman   sysfs: Implement ...
39
  	.permission	= sysfs_permission,
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
40
  	.setattr	= sysfs_setattr,
e61ab4ae4   Eric W. Biederman   sysfs: Implement ...
41
  	.getattr	= sysfs_getattr,
ddd29ec65   David P. Quigley   sysfs: Add labeli...
42
  	.setxattr	= sysfs_setxattr,
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
43
  };
e0bf68dde   Peter Zijlstra   mm: bdi init hooks
44
45
46
47
  int __init sysfs_inode_init(void)
  {
  	return bdi_init(&sysfs_backing_dev_info);
  }
f38506c49   Stefan Richter   sysfs: mark a loc...
48
  static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd)
ddd29ec65   David P. Quigley   sysfs: Add labeli...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  {
  	struct sysfs_inode_attrs *attrs;
  	struct iattr *iattrs;
  
  	attrs = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL);
  	if (!attrs)
  		return NULL;
  	iattrs = &attrs->ia_iattr;
  
  	/* assign default attributes */
  	iattrs->ia_mode = sd->s_mode;
  	iattrs->ia_uid = 0;
  	iattrs->ia_gid = 0;
  	iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME;
  
  	return attrs;
  }
f38506c49   Stefan Richter   sysfs: mark a loc...
66

35df63c46   Eric W. Biederman   sysfs: Fix lockin...
67
  int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr * iattr)
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
68
  {
ddd29ec65   David P. Quigley   sysfs: Add labeli...
69
70
  	struct sysfs_inode_attrs *sd_attrs;
  	struct iattr *iattrs;
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
71
  	unsigned int ia_valid = iattr->ia_valid;
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
72

ddd29ec65   David P. Quigley   sysfs: Add labeli...
73
  	sd_attrs = sd->s_iattr;
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
74

ddd29ec65   David P. Quigley   sysfs: Add labeli...
75
  	if (!sd_attrs) {
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
76
  		/* setting attributes for the first time, allocate now */
ddd29ec65   David P. Quigley   sysfs: Add labeli...
77
78
  		sd_attrs = sysfs_init_inode_attrs(sd);
  		if (!sd_attrs)
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
79
  			return -ENOMEM;
ddd29ec65   David P. Quigley   sysfs: Add labeli...
80
  		sd->s_iattr = sd_attrs;
7c0ff870d   Eric W. Biederman   sysfs: sysfs_sd_s...
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  	}
  	/* attributes were changed at least once in past */
  	iattrs = &sd_attrs->ia_iattr;
  
  	if (ia_valid & ATTR_UID)
  		iattrs->ia_uid = iattr->ia_uid;
  	if (ia_valid & ATTR_GID)
  		iattrs->ia_gid = iattr->ia_gid;
  	if (ia_valid & ATTR_ATIME)
  		iattrs->ia_atime = iattr->ia_atime;
  	if (ia_valid & ATTR_MTIME)
  		iattrs->ia_mtime = iattr->ia_mtime;
  	if (ia_valid & ATTR_CTIME)
  		iattrs->ia_ctime = iattr->ia_ctime;
  	if (ia_valid & ATTR_MODE) {
  		umode_t mode = iattr->ia_mode;
  		iattrs->ia_mode = sd->s_mode = mode;
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
98
  	}
35df63c46   Eric W. Biederman   sysfs: Fix lockin...
99
100
101
102
103
104
105
106
107
108
109
  	return 0;
  }
  
  int sysfs_setattr(struct dentry *dentry, struct iattr *iattr)
  {
  	struct inode *inode = dentry->d_inode;
  	struct sysfs_dirent *sd = dentry->d_fsdata;
  	int error;
  
  	if (!sd)
  		return -EINVAL;
f8d4f618f   Eric W. Biederman   sysfs: Serialize ...
110
  	mutex_lock(&sysfs_mutex);
35df63c46   Eric W. Biederman   sysfs: Fix lockin...
111
112
  	error = inode_change_ok(inode, iattr);
  	if (error)
f8d4f618f   Eric W. Biederman   sysfs: Serialize ...
113
  		goto out;
35df63c46   Eric W. Biederman   sysfs: Fix lockin...
114

75de46b98   Nick Piggin   fix setattr error...
115
116
117
  	error = sysfs_sd_setattr(sd, iattr);
  	if (error)
  		goto out;
3322e79a3   Nick Piggin   fs: convert simpl...
118
  	/* this ignores size changes */
6a1a90ad1   Christoph Hellwig   rename generic_se...
119
  	setattr_copy(inode, iattr);
35df63c46   Eric W. Biederman   sysfs: Fix lockin...
120

f8d4f618f   Eric W. Biederman   sysfs: Serialize ...
121
  out:
35df63c46   Eric W. Biederman   sysfs: Fix lockin...
122
  	mutex_unlock(&sysfs_mutex);
ddd29ec65   David P. Quigley   sysfs: Add labeli...
123
124
  	return error;
  }
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
125

f44d3e785   Eric W. Biederman   sysfs: Update sys...
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, u32 *secdata_len)
  {
  	struct sysfs_inode_attrs *iattrs;
  	void *old_secdata;
  	size_t old_secdata_len;
  
  	iattrs = sd->s_iattr;
  	if (!iattrs)
  		iattrs = sysfs_init_inode_attrs(sd);
  	if (!iattrs)
  		return -ENOMEM;
  
  	old_secdata = iattrs->ia_secdata;
  	old_secdata_len = iattrs->ia_secdata_len;
  
  	iattrs->ia_secdata = *secdata;
  	iattrs->ia_secdata_len = *secdata_len;
  
  	*secdata = old_secdata;
  	*secdata_len = old_secdata_len;
  	return 0;
  }
ddd29ec65   David P. Quigley   sysfs: Add labeli...
148
149
150
151
  int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
  		size_t size, int flags)
  {
  	struct sysfs_dirent *sd = dentry->d_fsdata;
ddd29ec65   David P. Quigley   sysfs: Add labeli...
152
153
154
155
156
157
  	void *secdata;
  	int error;
  	u32 secdata_len = 0;
  
  	if (!sd)
  		return -EINVAL;
ddd29ec65   David P. Quigley   sysfs: Add labeli...
158
159
160
161
162
163
164
165
166
167
168
  
  	if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) {
  		const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
  		error = security_inode_setsecurity(dentry->d_inode, suffix,
  						value, size, flags);
  		if (error)
  			goto out;
  		error = security_inode_getsecctx(dentry->d_inode,
  						&secdata, &secdata_len);
  		if (error)
  			goto out;
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
169

f44d3e785   Eric W. Biederman   sysfs: Update sys...
170
171
172
173
174
175
  		mutex_lock(&sysfs_mutex);
  		error = sysfs_sd_setsecdata(sd, &secdata, &secdata_len);
  		mutex_unlock(&sysfs_mutex);
  
  		if (secdata)
  			security_release_secctx(secdata, secdata_len);
ddd29ec65   David P. Quigley   sysfs: Add labeli...
176
177
178
  	} else
  		return -EINVAL;
  out:
988d186de   Maneesh Soni   [PATCH] sysfs-iat...
179
180
  	return error;
  }
8215534ce   Maneesh Soni   [PATCH] sysfs-iat...
181
182
183
  static inline void set_default_inode_attr(struct inode * inode, mode_t mode)
  {
  	inode->i_mode = mode;
8215534ce   Maneesh Soni   [PATCH] sysfs-iat...
184
185
186
187
188
  	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
  }
  
  static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
  {
8215534ce   Maneesh Soni   [PATCH] sysfs-iat...
189
190
191
192
193
194
  	inode->i_uid = iattr->ia_uid;
  	inode->i_gid = iattr->ia_gid;
  	inode->i_atime = iattr->ia_atime;
  	inode->i_mtime = iattr->ia_mtime;
  	inode->i_ctime = iattr->ia_ctime;
  }
372e88bd1   Eric W. Biederman   sysfs: Move all o...
195
196
197
198
  static int sysfs_count_nlink(struct sysfs_dirent *sd)
  {
  	struct sysfs_dirent *child;
  	int nr = 0;
bc747f37a   Tejun Heo   sysfs: move sysfs...
199
  	for (child = sd->s_dir.children; child; child = child->s_sibling)
372e88bd1   Eric W. Biederman   sysfs: Move all o...
200
201
202
203
204
  		if (sysfs_type(child) == SYSFS_DIR)
  			nr++;
  
  	return nr + 2;
  }
e61ab4ae4   Eric W. Biederman   sysfs: Implement ...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode)
  {
  	struct sysfs_inode_attrs *iattrs = sd->s_iattr;
  
  	inode->i_mode = sd->s_mode;
  	if (iattrs) {
  		/* sysfs_dirent has non-default attributes
  		 * get them from persistent copy in sysfs_dirent
  		 */
  		set_inode_attr(inode, &iattrs->ia_iattr);
  		security_inode_notifysecctx(inode,
  					    iattrs->ia_secdata,
  					    iattrs->ia_secdata_len);
  	}
  
  	if (sysfs_type(sd) == SYSFS_DIR)
  		inode->i_nlink = sysfs_count_nlink(sd);
  }
  
  int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
  {
  	struct sysfs_dirent *sd = dentry->d_fsdata;
  	struct inode *inode = dentry->d_inode;
  
  	mutex_lock(&sysfs_mutex);
  	sysfs_refresh_inode(sd, inode);
  	mutex_unlock(&sysfs_mutex);
  
  	generic_fillattr(inode, stat);
  	return 0;
  }
bc37e2830   Tejun Heo   sysfs: make sysfs...
236
  static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  {
372e88bd1   Eric W. Biederman   sysfs: Move all o...
238
  	struct bin_attribute *bin_attr;
04256b4a8   Eric W. Biederman   sysfs: reference ...
239
  	inode->i_private = sysfs_get(sd);
fc9f54b99   Tejun Heo   sysfs: reorganize...
240
241
242
  	inode->i_mapping->a_ops = &sysfs_aops;
  	inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
  	inode->i_op = &sysfs_inode_operations;
fc9f54b99   Tejun Heo   sysfs: reorganize...
243

e61ab4ae4   Eric W. Biederman   sysfs: Implement ...
244
245
  	set_default_inode_attr(inode, sd->s_mode);
  	sysfs_refresh_inode(sd, inode);
372e88bd1   Eric W. Biederman   sysfs: Move all o...
246

372e88bd1   Eric W. Biederman   sysfs: Move all o...
247
248
  	/* initialize inode according to type */
  	switch (sysfs_type(sd)) {
372e88bd1   Eric W. Biederman   sysfs: Move all o...
249
250
251
  	case SYSFS_DIR:
  		inode->i_op = &sysfs_dir_inode_operations;
  		inode->i_fop = &sysfs_dir_operations;
372e88bd1   Eric W. Biederman   sysfs: Move all o...
252
253
254
255
256
257
  		break;
  	case SYSFS_KOBJ_ATTR:
  		inode->i_size = PAGE_SIZE;
  		inode->i_fop = &sysfs_file_operations;
  		break;
  	case SYSFS_KOBJ_BIN_ATTR:
b1fc3d614   Tejun Heo   sysfs: make s_ele...
258
  		bin_attr = sd->s_bin_attr.bin_attr;
372e88bd1   Eric W. Biederman   sysfs: Move all o...
259
260
261
262
263
264
265
266
267
268
269
  		inode->i_size = bin_attr->size;
  		inode->i_fop = &bin_fops;
  		break;
  	case SYSFS_KOBJ_LINK:
  		inode->i_op = &sysfs_symlink_inode_operations;
  		break;
  	default:
  		BUG();
  	}
  
  	unlock_new_inode(inode);
fc9f54b99   Tejun Heo   sysfs: reorganize...
270
271
272
  }
  
  /**
8312a8d7c   Tejun Heo   sysfs: use iget_l...
273
   *	sysfs_get_inode - get inode for sysfs_dirent
fac2622bb   Eric W. Biederman   sysfs: Pass super...
274
   *	@sb: super block
fc9f54b99   Tejun Heo   sysfs: reorganize...
275
276
   *	@sd: sysfs_dirent to allocate inode for
   *
8312a8d7c   Tejun Heo   sysfs: use iget_l...
277
278
279
   *	Get inode for @sd.  If such inode doesn't exist, a new inode
   *	is allocated and basics are initialized.  New inode is
   *	returned locked.
fc9f54b99   Tejun Heo   sysfs: reorganize...
280
281
282
283
284
285
286
   *
   *	LOCKING:
   *	Kernel thread context (may sleep).
   *
   *	RETURNS:
   *	Pointer to allocated inode on success, NULL on failure.
   */
fac2622bb   Eric W. Biederman   sysfs: Pass super...
287
  struct inode * sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd)
fc9f54b99   Tejun Heo   sysfs: reorganize...
288
289
  {
  	struct inode *inode;
fac2622bb   Eric W. Biederman   sysfs: Pass super...
290
  	inode = iget_locked(sb, sd->s_ino);
8312a8d7c   Tejun Heo   sysfs: use iget_l...
291
  	if (inode && (inode->i_state & I_NEW))
fc9f54b99   Tejun Heo   sysfs: reorganize...
292
  		sysfs_init_inode(sd, inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
  	return inode;
  }
04256b4a8   Eric W. Biederman   sysfs: reference ...
295
296
297
298
  /*
   * The sysfs_dirent serves as both an inode and a directory entry for sysfs.
   * To prevent the sysfs inode numbers from being freed prematurely we take a
   * reference to sysfs_dirent from the sysfs inode.  A
01cd9fef6   Al Viro   switch sysfs to -...
299
   * super_operations.evict_inode() implementation is needed to drop that
04256b4a8   Eric W. Biederman   sysfs: reference ...
300
301
   * reference upon inode destruction.
   */
01cd9fef6   Al Viro   switch sysfs to -...
302
  void sysfs_evict_inode(struct inode *inode)
04256b4a8   Eric W. Biederman   sysfs: reference ...
303
304
305
306
  {
  	struct sysfs_dirent *sd  = inode->i_private;
  
  	truncate_inode_pages(&inode->i_data, 0);
01cd9fef6   Al Viro   switch sysfs to -...
307
  	end_writeback(inode);
04256b4a8   Eric W. Biederman   sysfs: reference ...
308
309
  	sysfs_put(sd);
  }
3ff195b01   Eric W. Biederman   sysfs: Implement ...
310
  int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const char *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
  {
fb6896da3   Tejun Heo   sysfs: restructur...
312
  	struct sysfs_addrm_cxt acxt;
41fc1c274   Tejun Heo   sysfs: make sysfs...
313
  	struct sysfs_dirent *sd;
641e6f30a   Greg Kroah-Hartman   [PATCH] sysfs: sy...
314

608e266a2   Tejun Heo   sysfs: make kobj ...
315
  	if (!dir_sd)
995982ca7   Randy.Dunlap   sysfs_remove_bin_...
316
  		return -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317

fb6896da3   Tejun Heo   sysfs: restructur...
318
  	sysfs_addrm_start(&acxt, dir_sd);
608e266a2   Tejun Heo   sysfs: make kobj ...
319

3ff195b01   Eric W. Biederman   sysfs: Implement ...
320
  	sd = sysfs_find_dirent(dir_sd, ns, name);
af10ec77b   Eric W. Biederman   sysfs: Add suppor...
321
322
  	if (sd && (sd->s_ns != ns))
  		sd = NULL;
41fc1c274   Tejun Heo   sysfs: make sysfs...
323
324
  	if (sd)
  		sysfs_remove_one(&acxt, sd);
3007e997d   Tejun Heo   sysfs: use sysfs_...
325

990e53f88   Tejun Heo   sysfs: make sysfs...
326
327
328
  	sysfs_addrm_finish(&acxt);
  
  	if (sd)
fb6896da3   Tejun Heo   sysfs: restructur...
329
  		return 0;
990e53f88   Tejun Heo   sysfs: make sysfs...
330
331
  	else
  		return -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  }
e61ab4ae4   Eric W. Biederman   sysfs: Implement ...
333
334
335
336
337
338
339
340
341
342
343
  
  int sysfs_permission(struct inode *inode, int mask)
  {
  	struct sysfs_dirent *sd = inode->i_private;
  
  	mutex_lock(&sysfs_mutex);
  	sysfs_refresh_inode(sd, inode);
  	mutex_unlock(&sysfs_mutex);
  
  	return generic_permission(inode, mask, NULL);
  }