1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * fs/sysfs/file.c - sysfs regular (text) file implementation |
4 | * |
5 | * Copyright (c) 2001-3 Patrick Mochel |
6 | * Copyright (c) 2007 SUSE Linux Products GmbH |
7 | * Copyright (c) 2007 Tejun Heo <teheo@suse.de> |
8 | * |
9 | * Please see Documentation/filesystems/sysfs.rst for more information. |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/kobject.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/list.h> |
16 | #include <linux/mutex.h> |
17 | #include <linux/seq_file.h> |
18 | #include <linux/mm.h> |
19 | |
20 | #include "sysfs.h" |
21 | |
22 | /* |
23 | * Determine ktype->sysfs_ops for the given kernfs_node. This function |
24 | * must be called while holding an active reference. |
25 | */ |
26 | static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) |
27 | { |
28 | struct kobject *kobj = kn->parent->priv; |
29 | |
30 | if (kn->flags & KERNFS_LOCKDEP) |
31 | lockdep_assert_held(kn); |
32 | return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; |
33 | } |
34 | |
35 | /* |
36 | * Reads on sysfs are handled through seq_file, which takes care of hairy |
37 | * details like buffering and seeking. The following function pipes |
38 | * sysfs_ops->show() result through seq_file. |
39 | */ |
40 | static int sysfs_kf_seq_show(struct seq_file *sf, void *v) |
41 | { |
42 | struct kernfs_open_file *of = sf->private; |
43 | struct kobject *kobj = of->kn->parent->priv; |
44 | const struct sysfs_ops *ops = sysfs_file_ops(kn: of->kn); |
45 | ssize_t count; |
46 | char *buf; |
47 | |
48 | if (WARN_ON_ONCE(!ops->show)) |
49 | return -EINVAL; |
50 | |
51 | /* acquire buffer and ensure that it's >= PAGE_SIZE and clear */ |
52 | count = seq_get_buf(m: sf, bufp: &buf); |
53 | if (count < PAGE_SIZE) { |
54 | seq_commit(m: sf, num: -1); |
55 | return 0; |
56 | } |
57 | memset(buf, 0, PAGE_SIZE); |
58 | |
59 | count = ops->show(kobj, of->kn->priv, buf); |
60 | if (count < 0) |
61 | return count; |
62 | |
63 | /* |
64 | * The code works fine with PAGE_SIZE return but it's likely to |
65 | * indicate truncated result or overflow in normal use cases. |
66 | */ |
67 | if (count >= (ssize_t)PAGE_SIZE) { |
68 | printk("fill_read_buffer: %pS returned bad count\n", |
69 | ops->show); |
70 | /* Try to struggle along */ |
71 | count = PAGE_SIZE - 1; |
72 | } |
73 | seq_commit(m: sf, num: count); |
74 | return 0; |
75 | } |
76 | |
77 | static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf, |
78 | size_t count, loff_t pos) |
79 | { |
80 | struct bin_attribute *battr = of->kn->priv; |
81 | struct kobject *kobj = of->kn->parent->priv; |
82 | loff_t size = file_inode(f: of->file)->i_size; |
83 | |
84 | if (!count) |
85 | return 0; |
86 | |
87 | if (size) { |
88 | if (pos >= size) |
89 | return 0; |
90 | if (pos + count > size) |
91 | count = size - pos; |
92 | } |
93 | |
94 | if (!battr->read) |
95 | return -EIO; |
96 | |
97 | return battr->read(of->file, kobj, battr, buf, pos, count); |
98 | } |
99 | |
100 | /* kernfs read callback for regular sysfs files with pre-alloc */ |
101 | static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf, |
102 | size_t count, loff_t pos) |
103 | { |
104 | const struct sysfs_ops *ops = sysfs_file_ops(kn: of->kn); |
105 | struct kobject *kobj = of->kn->parent->priv; |
106 | ssize_t len; |
107 | |
108 | /* |
109 | * If buf != of->prealloc_buf, we don't know how |
110 | * large it is, so cannot safely pass it to ->show |
111 | */ |
112 | if (WARN_ON_ONCE(buf != of->prealloc_buf)) |
113 | return 0; |
114 | len = ops->show(kobj, of->kn->priv, buf); |
115 | if (len < 0) |
116 | return len; |
117 | if (pos) { |
118 | if (len <= pos) |
119 | return 0; |
120 | len -= pos; |
121 | memmove(buf, buf + pos, len); |
122 | } |
123 | return min_t(ssize_t, count, len); |
124 | } |
125 | |
126 | /* kernfs write callback for regular sysfs files */ |
127 | static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf, |
128 | size_t count, loff_t pos) |
129 | { |
130 | const struct sysfs_ops *ops = sysfs_file_ops(kn: of->kn); |
131 | struct kobject *kobj = of->kn->parent->priv; |
132 | |
133 | if (!count) |
134 | return 0; |
135 | |
136 | return ops->store(kobj, of->kn->priv, buf, count); |
137 | } |
138 | |
139 | /* kernfs write callback for bin sysfs files */ |
140 | static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf, |
141 | size_t count, loff_t pos) |
142 | { |
143 | struct bin_attribute *battr = of->kn->priv; |
144 | struct kobject *kobj = of->kn->parent->priv; |
145 | loff_t size = file_inode(f: of->file)->i_size; |
146 | |
147 | if (size) { |
148 | if (size <= pos) |
149 | return -EFBIG; |
150 | count = min_t(ssize_t, count, size - pos); |
151 | } |
152 | if (!count) |
153 | return 0; |
154 | |
155 | if (!battr->write) |
156 | return -EIO; |
157 | |
158 | return battr->write(of->file, kobj, battr, buf, pos, count); |
159 | } |
160 | |
161 | static int sysfs_kf_bin_mmap(struct kernfs_open_file *of, |
162 | struct vm_area_struct *vma) |
163 | { |
164 | struct bin_attribute *battr = of->kn->priv; |
165 | struct kobject *kobj = of->kn->parent->priv; |
166 | |
167 | return battr->mmap(of->file, kobj, battr, vma); |
168 | } |
169 | |
170 | static loff_t sysfs_kf_bin_llseek(struct kernfs_open_file *of, loff_t offset, |
171 | int whence) |
172 | { |
173 | struct bin_attribute *battr = of->kn->priv; |
174 | struct kobject *kobj = of->kn->parent->priv; |
175 | |
176 | if (battr->llseek) |
177 | return battr->llseek(of->file, kobj, battr, offset, whence); |
178 | else |
179 | return generic_file_llseek(file: of->file, offset, whence); |
180 | } |
181 | |
182 | static int sysfs_kf_bin_open(struct kernfs_open_file *of) |
183 | { |
184 | struct bin_attribute *battr = of->kn->priv; |
185 | |
186 | if (battr->f_mapping) |
187 | of->file->f_mapping = battr->f_mapping(); |
188 | |
189 | return 0; |
190 | } |
191 | |
192 | void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr) |
193 | { |
194 | struct kernfs_node *kn = kobj->sd, *tmp; |
195 | |
196 | if (kn && dir) |
197 | kn = kernfs_find_and_get(kn, name: dir); |
198 | else |
199 | kernfs_get(kn); |
200 | |
201 | if (kn && attr) { |
202 | tmp = kernfs_find_and_get(kn, name: attr); |
203 | kernfs_put(kn); |
204 | kn = tmp; |
205 | } |
206 | |
207 | if (kn) { |
208 | kernfs_notify(kn); |
209 | kernfs_put(kn); |
210 | } |
211 | } |
212 | EXPORT_SYMBOL_GPL(sysfs_notify); |
213 | |
214 | static const struct kernfs_ops sysfs_file_kfops_empty = { |
215 | }; |
216 | |
217 | static const struct kernfs_ops sysfs_file_kfops_ro = { |
218 | .seq_show = sysfs_kf_seq_show, |
219 | }; |
220 | |
221 | static const struct kernfs_ops sysfs_file_kfops_wo = { |
222 | .write = sysfs_kf_write, |
223 | }; |
224 | |
225 | static const struct kernfs_ops sysfs_file_kfops_rw = { |
226 | .seq_show = sysfs_kf_seq_show, |
227 | .write = sysfs_kf_write, |
228 | }; |
229 | |
230 | static const struct kernfs_ops sysfs_prealloc_kfops_ro = { |
231 | .read = sysfs_kf_read, |
232 | .prealloc = true, |
233 | }; |
234 | |
235 | static const struct kernfs_ops sysfs_prealloc_kfops_wo = { |
236 | .write = sysfs_kf_write, |
237 | .prealloc = true, |
238 | }; |
239 | |
240 | static const struct kernfs_ops sysfs_prealloc_kfops_rw = { |
241 | .read = sysfs_kf_read, |
242 | .write = sysfs_kf_write, |
243 | .prealloc = true, |
244 | }; |
245 | |
246 | static const struct kernfs_ops sysfs_bin_kfops_ro = { |
247 | .read = sysfs_kf_bin_read, |
248 | }; |
249 | |
250 | static const struct kernfs_ops sysfs_bin_kfops_wo = { |
251 | .write = sysfs_kf_bin_write, |
252 | }; |
253 | |
254 | static const struct kernfs_ops sysfs_bin_kfops_rw = { |
255 | .read = sysfs_kf_bin_read, |
256 | .write = sysfs_kf_bin_write, |
257 | }; |
258 | |
259 | static const struct kernfs_ops sysfs_bin_kfops_mmap = { |
260 | .read = sysfs_kf_bin_read, |
261 | .write = sysfs_kf_bin_write, |
262 | .mmap = sysfs_kf_bin_mmap, |
263 | .open = sysfs_kf_bin_open, |
264 | .llseek = sysfs_kf_bin_llseek, |
265 | }; |
266 | |
267 | int sysfs_add_file_mode_ns(struct kernfs_node *parent, |
268 | const struct attribute *attr, umode_t mode, kuid_t uid, |
269 | kgid_t gid, const void *ns) |
270 | { |
271 | struct kobject *kobj = parent->priv; |
272 | const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; |
273 | struct lock_class_key *key = NULL; |
274 | const struct kernfs_ops *ops = NULL; |
275 | struct kernfs_node *kn; |
276 | |
277 | /* every kobject with an attribute needs a ktype assigned */ |
278 | if (WARN(!sysfs_ops, KERN_ERR |
279 | "missing sysfs attribute operations for kobject: %s\n", |
280 | kobject_name(kobj))) |
281 | return -EINVAL; |
282 | |
283 | if (mode & SYSFS_PREALLOC) { |
284 | if (sysfs_ops->show && sysfs_ops->store) |
285 | ops = &sysfs_prealloc_kfops_rw; |
286 | else if (sysfs_ops->show) |
287 | ops = &sysfs_prealloc_kfops_ro; |
288 | else if (sysfs_ops->store) |
289 | ops = &sysfs_prealloc_kfops_wo; |
290 | } else { |
291 | if (sysfs_ops->show && sysfs_ops->store) |
292 | ops = &sysfs_file_kfops_rw; |
293 | else if (sysfs_ops->show) |
294 | ops = &sysfs_file_kfops_ro; |
295 | else if (sysfs_ops->store) |
296 | ops = &sysfs_file_kfops_wo; |
297 | } |
298 | |
299 | if (!ops) |
300 | ops = &sysfs_file_kfops_empty; |
301 | |
302 | #ifdef CONFIG_DEBUG_LOCK_ALLOC |
303 | if (!attr->ignore_lockdep) |
304 | key = attr->key ?: (struct lock_class_key *)&attr->skey; |
305 | #endif |
306 | |
307 | kn = __kernfs_create_file(parent, name: attr->name, mode: mode & 0777, uid, gid, |
308 | PAGE_SIZE, ops, priv: (void *)attr, ns, key); |
309 | if (IS_ERR(ptr: kn)) { |
310 | if (PTR_ERR(ptr: kn) == -EEXIST) |
311 | sysfs_warn_dup(parent, name: attr->name); |
312 | return PTR_ERR(ptr: kn); |
313 | } |
314 | return 0; |
315 | } |
316 | |
317 | int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent, |
318 | const struct bin_attribute *battr, umode_t mode, |
319 | kuid_t uid, kgid_t gid, const void *ns) |
320 | { |
321 | const struct attribute *attr = &battr->attr; |
322 | struct lock_class_key *key = NULL; |
323 | const struct kernfs_ops *ops; |
324 | struct kernfs_node *kn; |
325 | |
326 | if (battr->mmap) |
327 | ops = &sysfs_bin_kfops_mmap; |
328 | else if (battr->read && battr->write) |
329 | ops = &sysfs_bin_kfops_rw; |
330 | else if (battr->read) |
331 | ops = &sysfs_bin_kfops_ro; |
332 | else if (battr->write) |
333 | ops = &sysfs_bin_kfops_wo; |
334 | else |
335 | ops = &sysfs_file_kfops_empty; |
336 | |
337 | #ifdef CONFIG_DEBUG_LOCK_ALLOC |
338 | if (!attr->ignore_lockdep) |
339 | key = attr->key ?: (struct lock_class_key *)&attr->skey; |
340 | #endif |
341 | |
342 | kn = __kernfs_create_file(parent, name: attr->name, mode: mode & 0777, uid, gid, |
343 | size: battr->size, ops, priv: (void *)attr, ns, key); |
344 | if (IS_ERR(ptr: kn)) { |
345 | if (PTR_ERR(ptr: kn) == -EEXIST) |
346 | sysfs_warn_dup(parent, name: attr->name); |
347 | return PTR_ERR(ptr: kn); |
348 | } |
349 | return 0; |
350 | } |
351 | |
352 | /** |
353 | * sysfs_create_file_ns - create an attribute file for an object with custom ns |
354 | * @kobj: object we're creating for |
355 | * @attr: attribute descriptor |
356 | * @ns: namespace the new file should belong to |
357 | */ |
358 | int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, |
359 | const void *ns) |
360 | { |
361 | kuid_t uid; |
362 | kgid_t gid; |
363 | |
364 | if (WARN_ON(!kobj || !kobj->sd || !attr)) |
365 | return -EINVAL; |
366 | |
367 | kobject_get_ownership(kobj, uid: &uid, gid: &gid); |
368 | return sysfs_add_file_mode_ns(parent: kobj->sd, attr, mode: attr->mode, uid, gid, ns); |
369 | } |
370 | EXPORT_SYMBOL_GPL(sysfs_create_file_ns); |
371 | |
372 | int sysfs_create_files(struct kobject *kobj, const struct attribute * const *ptr) |
373 | { |
374 | int err = 0; |
375 | int i; |
376 | |
377 | for (i = 0; ptr[i] && !err; i++) |
378 | err = sysfs_create_file(kobj, attr: ptr[i]); |
379 | if (err) |
380 | while (--i >= 0) |
381 | sysfs_remove_file(kobj, attr: ptr[i]); |
382 | return err; |
383 | } |
384 | EXPORT_SYMBOL_GPL(sysfs_create_files); |
385 | |
386 | /** |
387 | * sysfs_add_file_to_group - add an attribute file to a pre-existing group. |
388 | * @kobj: object we're acting for. |
389 | * @attr: attribute descriptor. |
390 | * @group: group name. |
391 | */ |
392 | int sysfs_add_file_to_group(struct kobject *kobj, |
393 | const struct attribute *attr, const char *group) |
394 | { |
395 | struct kernfs_node *parent; |
396 | kuid_t uid; |
397 | kgid_t gid; |
398 | int error; |
399 | |
400 | if (group) { |
401 | parent = kernfs_find_and_get(kn: kobj->sd, name: group); |
402 | } else { |
403 | parent = kobj->sd; |
404 | kernfs_get(kn: parent); |
405 | } |
406 | |
407 | if (!parent) |
408 | return -ENOENT; |
409 | |
410 | kobject_get_ownership(kobj, uid: &uid, gid: &gid); |
411 | error = sysfs_add_file_mode_ns(parent, attr, mode: attr->mode, uid, gid, |
412 | NULL); |
413 | kernfs_put(kn: parent); |
414 | |
415 | return error; |
416 | } |
417 | EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); |
418 | |
419 | /** |
420 | * sysfs_chmod_file - update the modified mode value on an object attribute. |
421 | * @kobj: object we're acting for. |
422 | * @attr: attribute descriptor. |
423 | * @mode: file permissions. |
424 | * |
425 | */ |
426 | int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, |
427 | umode_t mode) |
428 | { |
429 | struct kernfs_node *kn; |
430 | struct iattr newattrs; |
431 | int rc; |
432 | |
433 | kn = kernfs_find_and_get(kn: kobj->sd, name: attr->name); |
434 | if (!kn) |
435 | return -ENOENT; |
436 | |
437 | newattrs.ia_mode = (mode & S_IALLUGO) | (kn->mode & ~S_IALLUGO); |
438 | newattrs.ia_valid = ATTR_MODE; |
439 | |
440 | rc = kernfs_setattr(kn, iattr: &newattrs); |
441 | |
442 | kernfs_put(kn); |
443 | return rc; |
444 | } |
445 | EXPORT_SYMBOL_GPL(sysfs_chmod_file); |
446 | |
447 | /** |
448 | * sysfs_break_active_protection - break "active" protection |
449 | * @kobj: The kernel object @attr is associated with. |
450 | * @attr: The attribute to break the "active" protection for. |
451 | * |
452 | * With sysfs, just like kernfs, deletion of an attribute is postponed until |
453 | * all active .show() and .store() callbacks have finished unless this function |
454 | * is called. Hence this function is useful in methods that implement self |
455 | * deletion. |
456 | */ |
457 | struct kernfs_node *sysfs_break_active_protection(struct kobject *kobj, |
458 | const struct attribute *attr) |
459 | { |
460 | struct kernfs_node *kn; |
461 | |
462 | kobject_get(kobj); |
463 | kn = kernfs_find_and_get(kn: kobj->sd, name: attr->name); |
464 | if (kn) |
465 | kernfs_break_active_protection(kn); |
466 | else |
467 | kobject_put(kobj); |
468 | return kn; |
469 | } |
470 | EXPORT_SYMBOL_GPL(sysfs_break_active_protection); |
471 | |
472 | /** |
473 | * sysfs_unbreak_active_protection - restore "active" protection |
474 | * @kn: Pointer returned by sysfs_break_active_protection(). |
475 | * |
476 | * Undo the effects of sysfs_break_active_protection(). Since this function |
477 | * calls kernfs_put() on the kernfs node that corresponds to the 'attr' |
478 | * argument passed to sysfs_break_active_protection() that attribute may have |
479 | * been removed between the sysfs_break_active_protection() and |
480 | * sysfs_unbreak_active_protection() calls, it is not safe to access @kn after |
481 | * this function has returned. |
482 | */ |
483 | void sysfs_unbreak_active_protection(struct kernfs_node *kn) |
484 | { |
485 | struct kobject *kobj = kn->parent->priv; |
486 | |
487 | kernfs_unbreak_active_protection(kn); |
488 | kernfs_put(kn); |
489 | kobject_put(kobj); |
490 | } |
491 | EXPORT_SYMBOL_GPL(sysfs_unbreak_active_protection); |
492 | |
493 | /** |
494 | * sysfs_remove_file_ns - remove an object attribute with a custom ns tag |
495 | * @kobj: object we're acting for |
496 | * @attr: attribute descriptor |
497 | * @ns: namespace tag of the file to remove |
498 | * |
499 | * Hash the attribute name and namespace tag and kill the victim. |
500 | */ |
501 | void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, |
502 | const void *ns) |
503 | { |
504 | struct kernfs_node *parent = kobj->sd; |
505 | |
506 | kernfs_remove_by_name_ns(parent, name: attr->name, ns); |
507 | } |
508 | EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); |
509 | |
510 | /** |
511 | * sysfs_remove_file_self - remove an object attribute from its own method |
512 | * @kobj: object we're acting for |
513 | * @attr: attribute descriptor |
514 | * |
515 | * See kernfs_remove_self() for details. |
516 | */ |
517 | bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr) |
518 | { |
519 | struct kernfs_node *parent = kobj->sd; |
520 | struct kernfs_node *kn; |
521 | bool ret; |
522 | |
523 | kn = kernfs_find_and_get(kn: parent, name: attr->name); |
524 | if (WARN_ON_ONCE(!kn)) |
525 | return false; |
526 | |
527 | ret = kernfs_remove_self(kn); |
528 | |
529 | kernfs_put(kn); |
530 | return ret; |
531 | } |
532 | EXPORT_SYMBOL_GPL(sysfs_remove_file_self); |
533 | |
534 | void sysfs_remove_files(struct kobject *kobj, const struct attribute * const *ptr) |
535 | { |
536 | int i; |
537 | |
538 | for (i = 0; ptr[i]; i++) |
539 | sysfs_remove_file(kobj, attr: ptr[i]); |
540 | } |
541 | EXPORT_SYMBOL_GPL(sysfs_remove_files); |
542 | |
543 | /** |
544 | * sysfs_remove_file_from_group - remove an attribute file from a group. |
545 | * @kobj: object we're acting for. |
546 | * @attr: attribute descriptor. |
547 | * @group: group name. |
548 | */ |
549 | void sysfs_remove_file_from_group(struct kobject *kobj, |
550 | const struct attribute *attr, const char *group) |
551 | { |
552 | struct kernfs_node *parent; |
553 | |
554 | if (group) { |
555 | parent = kernfs_find_and_get(kn: kobj->sd, name: group); |
556 | } else { |
557 | parent = kobj->sd; |
558 | kernfs_get(kn: parent); |
559 | } |
560 | |
561 | if (parent) { |
562 | kernfs_remove_by_name(parent, name: attr->name); |
563 | kernfs_put(kn: parent); |
564 | } |
565 | } |
566 | EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); |
567 | |
568 | /** |
569 | * sysfs_create_bin_file - create binary file for object. |
570 | * @kobj: object. |
571 | * @attr: attribute descriptor. |
572 | */ |
573 | int sysfs_create_bin_file(struct kobject *kobj, |
574 | const struct bin_attribute *attr) |
575 | { |
576 | kuid_t uid; |
577 | kgid_t gid; |
578 | |
579 | if (WARN_ON(!kobj || !kobj->sd || !attr)) |
580 | return -EINVAL; |
581 | |
582 | kobject_get_ownership(kobj, uid: &uid, gid: &gid); |
583 | return sysfs_add_bin_file_mode_ns(parent: kobj->sd, battr: attr, mode: attr->attr.mode, uid, |
584 | gid, NULL); |
585 | } |
586 | EXPORT_SYMBOL_GPL(sysfs_create_bin_file); |
587 | |
588 | /** |
589 | * sysfs_remove_bin_file - remove binary file for object. |
590 | * @kobj: object. |
591 | * @attr: attribute descriptor. |
592 | */ |
593 | void sysfs_remove_bin_file(struct kobject *kobj, |
594 | const struct bin_attribute *attr) |
595 | { |
596 | kernfs_remove_by_name(parent: kobj->sd, name: attr->attr.name); |
597 | } |
598 | EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); |
599 | |
600 | static int internal_change_owner(struct kernfs_node *kn, kuid_t kuid, |
601 | kgid_t kgid) |
602 | { |
603 | struct iattr newattrs = { |
604 | .ia_valid = ATTR_UID | ATTR_GID, |
605 | .ia_uid = kuid, |
606 | .ia_gid = kgid, |
607 | }; |
608 | return kernfs_setattr(kn, iattr: &newattrs); |
609 | } |
610 | |
611 | /** |
612 | * sysfs_link_change_owner - change owner of a sysfs file. |
613 | * @kobj: object of the kernfs_node the symlink is located in. |
614 | * @targ: object of the kernfs_node the symlink points to. |
615 | * @name: name of the link. |
616 | * @kuid: new owner's kuid |
617 | * @kgid: new owner's kgid |
618 | * |
619 | * This function looks up the sysfs symlink entry @name under @kobj and changes |
620 | * the ownership to @kuid/@kgid. The symlink is looked up in the namespace of |
621 | * @targ. |
622 | * |
623 | * Returns 0 on success or error code on failure. |
624 | */ |
625 | int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ, |
626 | const char *name, kuid_t kuid, kgid_t kgid) |
627 | { |
628 | struct kernfs_node *kn = NULL; |
629 | int error; |
630 | |
631 | if (!name || !kobj->state_in_sysfs || !targ->state_in_sysfs) |
632 | return -EINVAL; |
633 | |
634 | error = -ENOENT; |
635 | kn = kernfs_find_and_get_ns(parent: kobj->sd, name, ns: targ->sd->ns); |
636 | if (!kn) |
637 | goto out; |
638 | |
639 | error = -EINVAL; |
640 | if (kernfs_type(kn) != KERNFS_LINK) |
641 | goto out; |
642 | if (kn->symlink.target_kn->priv != targ) |
643 | goto out; |
644 | |
645 | error = internal_change_owner(kn, kuid, kgid); |
646 | |
647 | out: |
648 | kernfs_put(kn); |
649 | return error; |
650 | } |
651 | |
652 | /** |
653 | * sysfs_file_change_owner - change owner of a sysfs file. |
654 | * @kobj: object. |
655 | * @name: name of the file to change. |
656 | * @kuid: new owner's kuid |
657 | * @kgid: new owner's kgid |
658 | * |
659 | * This function looks up the sysfs entry @name under @kobj and changes the |
660 | * ownership to @kuid/@kgid. |
661 | * |
662 | * Returns 0 on success or error code on failure. |
663 | */ |
664 | int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, |
665 | kgid_t kgid) |
666 | { |
667 | struct kernfs_node *kn; |
668 | int error; |
669 | |
670 | if (!name) |
671 | return -EINVAL; |
672 | |
673 | if (!kobj->state_in_sysfs) |
674 | return -EINVAL; |
675 | |
676 | kn = kernfs_find_and_get(kn: kobj->sd, name); |
677 | if (!kn) |
678 | return -ENOENT; |
679 | |
680 | error = internal_change_owner(kn, kuid, kgid); |
681 | |
682 | kernfs_put(kn); |
683 | |
684 | return error; |
685 | } |
686 | EXPORT_SYMBOL_GPL(sysfs_file_change_owner); |
687 | |
688 | /** |
689 | * sysfs_change_owner - change owner of the given object. |
690 | * @kobj: object. |
691 | * @kuid: new owner's kuid |
692 | * @kgid: new owner's kgid |
693 | * |
694 | * Change the owner of the default directory, files, groups, and attributes of |
695 | * @kobj to @kuid/@kgid. Note that sysfs_change_owner mirrors how the sysfs |
696 | * entries for a kobject are added by driver core. In summary, |
697 | * sysfs_change_owner() takes care of the default directory entry for @kobj, |
698 | * the default attributes associated with the ktype of @kobj and the default |
699 | * attributes associated with the ktype of @kobj. |
700 | * Additional properties not added by driver core have to be changed by the |
701 | * driver or subsystem which created them. This is similar to how |
702 | * driver/subsystem specific entries are removed. |
703 | * |
704 | * Returns 0 on success or error code on failure. |
705 | */ |
706 | int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid) |
707 | { |
708 | int error; |
709 | const struct kobj_type *ktype; |
710 | |
711 | if (!kobj->state_in_sysfs) |
712 | return -EINVAL; |
713 | |
714 | /* Change the owner of the kobject itself. */ |
715 | error = internal_change_owner(kn: kobj->sd, kuid, kgid); |
716 | if (error) |
717 | return error; |
718 | |
719 | ktype = get_ktype(kobj); |
720 | if (ktype) { |
721 | /* |
722 | * Change owner of the default groups associated with the |
723 | * ktype of @kobj. |
724 | */ |
725 | error = sysfs_groups_change_owner(kobj, groups: ktype->default_groups, |
726 | kuid, kgid); |
727 | if (error) |
728 | return error; |
729 | } |
730 | |
731 | return 0; |
732 | } |
733 | EXPORT_SYMBOL_GPL(sysfs_change_owner); |
734 | |
735 | /** |
736 | * sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer. |
737 | * @buf: start of PAGE_SIZE buffer. |
738 | * @fmt: format |
739 | * @...: optional arguments to @format |
740 | * |
741 | * |
742 | * Returns number of characters written to @buf. |
743 | */ |
744 | int sysfs_emit(char *buf, const char *fmt, ...) |
745 | { |
746 | va_list args; |
747 | int len; |
748 | |
749 | if (WARN(!buf || offset_in_page(buf), |
750 | "invalid sysfs_emit: buf:%p\n", buf)) |
751 | return 0; |
752 | |
753 | va_start(args, fmt); |
754 | len = vscnprintf(buf, PAGE_SIZE, fmt, args); |
755 | va_end(args); |
756 | |
757 | return len; |
758 | } |
759 | EXPORT_SYMBOL_GPL(sysfs_emit); |
760 | |
761 | /** |
762 | * sysfs_emit_at - scnprintf equivalent, aware of PAGE_SIZE buffer. |
763 | * @buf: start of PAGE_SIZE buffer. |
764 | * @at: offset in @buf to start write in bytes |
765 | * @at must be >= 0 && < PAGE_SIZE |
766 | * @fmt: format |
767 | * @...: optional arguments to @fmt |
768 | * |
769 | * |
770 | * Returns number of characters written starting at &@buf[@at]. |
771 | */ |
772 | int sysfs_emit_at(char *buf, int at, const char *fmt, ...) |
773 | { |
774 | va_list args; |
775 | int len; |
776 | |
777 | if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE, |
778 | "invalid sysfs_emit_at: buf:%p at:%d\n", buf, at)) |
779 | return 0; |
780 | |
781 | va_start(args, fmt); |
782 | len = vscnprintf(buf: buf + at, PAGE_SIZE - at, fmt, args); |
783 | va_end(args); |
784 | |
785 | return len; |
786 | } |
787 | EXPORT_SYMBOL_GPL(sysfs_emit_at); |
788 |
Definitions
- sysfs_file_ops
- sysfs_kf_seq_show
- sysfs_kf_bin_read
- sysfs_kf_read
- sysfs_kf_write
- sysfs_kf_bin_write
- sysfs_kf_bin_mmap
- sysfs_kf_bin_llseek
- sysfs_kf_bin_open
- sysfs_notify
- sysfs_file_kfops_empty
- sysfs_file_kfops_ro
- sysfs_file_kfops_wo
- sysfs_file_kfops_rw
- sysfs_prealloc_kfops_ro
- sysfs_prealloc_kfops_wo
- sysfs_prealloc_kfops_rw
- sysfs_bin_kfops_ro
- sysfs_bin_kfops_wo
- sysfs_bin_kfops_rw
- sysfs_bin_kfops_mmap
- sysfs_add_file_mode_ns
- sysfs_add_bin_file_mode_ns
- sysfs_create_file_ns
- sysfs_create_files
- sysfs_add_file_to_group
- sysfs_chmod_file
- sysfs_break_active_protection
- sysfs_unbreak_active_protection
- sysfs_remove_file_ns
- sysfs_remove_file_self
- sysfs_remove_files
- sysfs_remove_file_from_group
- sysfs_create_bin_file
- sysfs_remove_bin_file
- internal_change_owner
- sysfs_link_change_owner
- sysfs_file_change_owner
- sysfs_change_owner
- sysfs_emit
Improve your Profiling and Debugging skills
Find out more