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 | return kn; |
467 | } |
468 | EXPORT_SYMBOL_GPL(sysfs_break_active_protection); |
469 | |
470 | /** |
471 | * sysfs_unbreak_active_protection - restore "active" protection |
472 | * @kn: Pointer returned by sysfs_break_active_protection(). |
473 | * |
474 | * Undo the effects of sysfs_break_active_protection(). Since this function |
475 | * calls kernfs_put() on the kernfs node that corresponds to the 'attr' |
476 | * argument passed to sysfs_break_active_protection() that attribute may have |
477 | * been removed between the sysfs_break_active_protection() and |
478 | * sysfs_unbreak_active_protection() calls, it is not safe to access @kn after |
479 | * this function has returned. |
480 | */ |
481 | void sysfs_unbreak_active_protection(struct kernfs_node *kn) |
482 | { |
483 | struct kobject *kobj = kn->parent->priv; |
484 | |
485 | kernfs_unbreak_active_protection(kn); |
486 | kernfs_put(kn); |
487 | kobject_put(kobj); |
488 | } |
489 | EXPORT_SYMBOL_GPL(sysfs_unbreak_active_protection); |
490 | |
491 | /** |
492 | * sysfs_remove_file_ns - remove an object attribute with a custom ns tag |
493 | * @kobj: object we're acting for |
494 | * @attr: attribute descriptor |
495 | * @ns: namespace tag of the file to remove |
496 | * |
497 | * Hash the attribute name and namespace tag and kill the victim. |
498 | */ |
499 | void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, |
500 | const void *ns) |
501 | { |
502 | struct kernfs_node *parent = kobj->sd; |
503 | |
504 | kernfs_remove_by_name_ns(parent, name: attr->name, ns); |
505 | } |
506 | EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); |
507 | |
508 | /** |
509 | * sysfs_remove_file_self - remove an object attribute from its own method |
510 | * @kobj: object we're acting for |
511 | * @attr: attribute descriptor |
512 | * |
513 | * See kernfs_remove_self() for details. |
514 | */ |
515 | bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr) |
516 | { |
517 | struct kernfs_node *parent = kobj->sd; |
518 | struct kernfs_node *kn; |
519 | bool ret; |
520 | |
521 | kn = kernfs_find_and_get(kn: parent, name: attr->name); |
522 | if (WARN_ON_ONCE(!kn)) |
523 | return false; |
524 | |
525 | ret = kernfs_remove_self(kn); |
526 | |
527 | kernfs_put(kn); |
528 | return ret; |
529 | } |
530 | EXPORT_SYMBOL_GPL(sysfs_remove_file_self); |
531 | |
532 | void sysfs_remove_files(struct kobject *kobj, const struct attribute * const *ptr) |
533 | { |
534 | int i; |
535 | |
536 | for (i = 0; ptr[i]; i++) |
537 | sysfs_remove_file(kobj, attr: ptr[i]); |
538 | } |
539 | EXPORT_SYMBOL_GPL(sysfs_remove_files); |
540 | |
541 | /** |
542 | * sysfs_remove_file_from_group - remove an attribute file from a group. |
543 | * @kobj: object we're acting for. |
544 | * @attr: attribute descriptor. |
545 | * @group: group name. |
546 | */ |
547 | void sysfs_remove_file_from_group(struct kobject *kobj, |
548 | const struct attribute *attr, const char *group) |
549 | { |
550 | struct kernfs_node *parent; |
551 | |
552 | if (group) { |
553 | parent = kernfs_find_and_get(kn: kobj->sd, name: group); |
554 | } else { |
555 | parent = kobj->sd; |
556 | kernfs_get(kn: parent); |
557 | } |
558 | |
559 | if (parent) { |
560 | kernfs_remove_by_name(parent, name: attr->name); |
561 | kernfs_put(kn: parent); |
562 | } |
563 | } |
564 | EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); |
565 | |
566 | /** |
567 | * sysfs_create_bin_file - create binary file for object. |
568 | * @kobj: object. |
569 | * @attr: attribute descriptor. |
570 | */ |
571 | int sysfs_create_bin_file(struct kobject *kobj, |
572 | const struct bin_attribute *attr) |
573 | { |
574 | kuid_t uid; |
575 | kgid_t gid; |
576 | |
577 | if (WARN_ON(!kobj || !kobj->sd || !attr)) |
578 | return -EINVAL; |
579 | |
580 | kobject_get_ownership(kobj, uid: &uid, gid: &gid); |
581 | return sysfs_add_bin_file_mode_ns(parent: kobj->sd, battr: attr, mode: attr->attr.mode, uid, |
582 | gid, NULL); |
583 | } |
584 | EXPORT_SYMBOL_GPL(sysfs_create_bin_file); |
585 | |
586 | /** |
587 | * sysfs_remove_bin_file - remove binary file for object. |
588 | * @kobj: object. |
589 | * @attr: attribute descriptor. |
590 | */ |
591 | void sysfs_remove_bin_file(struct kobject *kobj, |
592 | const struct bin_attribute *attr) |
593 | { |
594 | kernfs_remove_by_name(parent: kobj->sd, name: attr->attr.name); |
595 | } |
596 | EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); |
597 | |
598 | static int internal_change_owner(struct kernfs_node *kn, kuid_t kuid, |
599 | kgid_t kgid) |
600 | { |
601 | struct iattr newattrs = { |
602 | .ia_valid = ATTR_UID | ATTR_GID, |
603 | .ia_uid = kuid, |
604 | .ia_gid = kgid, |
605 | }; |
606 | return kernfs_setattr(kn, iattr: &newattrs); |
607 | } |
608 | |
609 | /** |
610 | * sysfs_link_change_owner - change owner of a sysfs file. |
611 | * @kobj: object of the kernfs_node the symlink is located in. |
612 | * @targ: object of the kernfs_node the symlink points to. |
613 | * @name: name of the link. |
614 | * @kuid: new owner's kuid |
615 | * @kgid: new owner's kgid |
616 | * |
617 | * This function looks up the sysfs symlink entry @name under @kobj and changes |
618 | * the ownership to @kuid/@kgid. The symlink is looked up in the namespace of |
619 | * @targ. |
620 | * |
621 | * Returns 0 on success or error code on failure. |
622 | */ |
623 | int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ, |
624 | const char *name, kuid_t kuid, kgid_t kgid) |
625 | { |
626 | struct kernfs_node *kn = NULL; |
627 | int error; |
628 | |
629 | if (!name || !kobj->state_in_sysfs || !targ->state_in_sysfs) |
630 | return -EINVAL; |
631 | |
632 | error = -ENOENT; |
633 | kn = kernfs_find_and_get_ns(parent: kobj->sd, name, ns: targ->sd->ns); |
634 | if (!kn) |
635 | goto out; |
636 | |
637 | error = -EINVAL; |
638 | if (kernfs_type(kn) != KERNFS_LINK) |
639 | goto out; |
640 | if (kn->symlink.target_kn->priv != targ) |
641 | goto out; |
642 | |
643 | error = internal_change_owner(kn, kuid, kgid); |
644 | |
645 | out: |
646 | kernfs_put(kn); |
647 | return error; |
648 | } |
649 | |
650 | /** |
651 | * sysfs_file_change_owner - change owner of a sysfs file. |
652 | * @kobj: object. |
653 | * @name: name of the file to change. |
654 | * @kuid: new owner's kuid |
655 | * @kgid: new owner's kgid |
656 | * |
657 | * This function looks up the sysfs entry @name under @kobj and changes the |
658 | * ownership to @kuid/@kgid. |
659 | * |
660 | * Returns 0 on success or error code on failure. |
661 | */ |
662 | int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, |
663 | kgid_t kgid) |
664 | { |
665 | struct kernfs_node *kn; |
666 | int error; |
667 | |
668 | if (!name) |
669 | return -EINVAL; |
670 | |
671 | if (!kobj->state_in_sysfs) |
672 | return -EINVAL; |
673 | |
674 | kn = kernfs_find_and_get(kn: kobj->sd, name); |
675 | if (!kn) |
676 | return -ENOENT; |
677 | |
678 | error = internal_change_owner(kn, kuid, kgid); |
679 | |
680 | kernfs_put(kn); |
681 | |
682 | return error; |
683 | } |
684 | EXPORT_SYMBOL_GPL(sysfs_file_change_owner); |
685 | |
686 | /** |
687 | * sysfs_change_owner - change owner of the given object. |
688 | * @kobj: object. |
689 | * @kuid: new owner's kuid |
690 | * @kgid: new owner's kgid |
691 | * |
692 | * Change the owner of the default directory, files, groups, and attributes of |
693 | * @kobj to @kuid/@kgid. Note that sysfs_change_owner mirrors how the sysfs |
694 | * entries for a kobject are added by driver core. In summary, |
695 | * sysfs_change_owner() takes care of the default directory entry for @kobj, |
696 | * the default attributes associated with the ktype of @kobj and the default |
697 | * attributes associated with the ktype of @kobj. |
698 | * Additional properties not added by driver core have to be changed by the |
699 | * driver or subsystem which created them. This is similar to how |
700 | * driver/subsystem specific entries are removed. |
701 | * |
702 | * Returns 0 on success or error code on failure. |
703 | */ |
704 | int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid) |
705 | { |
706 | int error; |
707 | const struct kobj_type *ktype; |
708 | |
709 | if (!kobj->state_in_sysfs) |
710 | return -EINVAL; |
711 | |
712 | /* Change the owner of the kobject itself. */ |
713 | error = internal_change_owner(kn: kobj->sd, kuid, kgid); |
714 | if (error) |
715 | return error; |
716 | |
717 | ktype = get_ktype(kobj); |
718 | if (ktype) { |
719 | /* |
720 | * Change owner of the default groups associated with the |
721 | * ktype of @kobj. |
722 | */ |
723 | error = sysfs_groups_change_owner(kobj, groups: ktype->default_groups, |
724 | kuid, kgid); |
725 | if (error) |
726 | return error; |
727 | } |
728 | |
729 | return 0; |
730 | } |
731 | EXPORT_SYMBOL_GPL(sysfs_change_owner); |
732 | |
733 | /** |
734 | * sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer. |
735 | * @buf: start of PAGE_SIZE buffer. |
736 | * @fmt: format |
737 | * @...: optional arguments to @format |
738 | * |
739 | * |
740 | * Returns number of characters written to @buf. |
741 | */ |
742 | int sysfs_emit(char *buf, const char *fmt, ...) |
743 | { |
744 | va_list args; |
745 | int len; |
746 | |
747 | if (WARN(!buf || offset_in_page(buf), |
748 | "invalid sysfs_emit: buf:%p\n" , buf)) |
749 | return 0; |
750 | |
751 | va_start(args, fmt); |
752 | len = vscnprintf(buf, PAGE_SIZE, fmt, args); |
753 | va_end(args); |
754 | |
755 | return len; |
756 | } |
757 | EXPORT_SYMBOL_GPL(sysfs_emit); |
758 | |
759 | /** |
760 | * sysfs_emit_at - scnprintf equivalent, aware of PAGE_SIZE buffer. |
761 | * @buf: start of PAGE_SIZE buffer. |
762 | * @at: offset in @buf to start write in bytes |
763 | * @at must be >= 0 && < PAGE_SIZE |
764 | * @fmt: format |
765 | * @...: optional arguments to @fmt |
766 | * |
767 | * |
768 | * Returns number of characters written starting at &@buf[@at]. |
769 | */ |
770 | int sysfs_emit_at(char *buf, int at, const char *fmt, ...) |
771 | { |
772 | va_list args; |
773 | int len; |
774 | |
775 | if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE, |
776 | "invalid sysfs_emit_at: buf:%p at:%d\n" , buf, at)) |
777 | return 0; |
778 | |
779 | va_start(args, fmt); |
780 | len = vscnprintf(buf: buf + at, PAGE_SIZE - at, fmt, args); |
781 | va_end(args); |
782 | |
783 | return len; |
784 | } |
785 | EXPORT_SYMBOL_GPL(sysfs_emit_at); |
786 | |