1619daeeeSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
36d66f5cdSTejun Heo * fs/sysfs/file.c - sysfs regular (text) file implementation
46d66f5cdSTejun Heo *
56d66f5cdSTejun Heo * Copyright (c) 2001-3 Patrick Mochel
66d66f5cdSTejun Heo * Copyright (c) 2007 SUSE Linux Products GmbH
76d66f5cdSTejun Heo * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
86d66f5cdSTejun Heo *
90c1bc6b8SMauro Carvalho Chehab * Please see Documentation/filesystems/sysfs.rst for more information.
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/kobject.h>
14c6f87733SRobert P. J. Day #include <linux/slab.h>
1594bebf4dSOliver Neukum #include <linux/list.h>
1652e8c209SDave Young #include <linux/mutex.h>
1713c589d5STejun Heo #include <linux/seq_file.h>
182efc459dSJoe Perches #include <linux/mm.h>
191da177e4SLinus Torvalds
201da177e4SLinus Torvalds #include "sysfs.h"
21f6acf8bbSTejun Heo
22f6acf8bbSTejun Heo /*
23324a56e1STejun Heo * Determine ktype->sysfs_ops for the given kernfs_node. This function
24375b611eSTejun Heo * must be called while holding an active reference.
25375b611eSTejun Heo */
sysfs_file_ops(struct kernfs_node * kn)26324a56e1STejun Heo static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
27375b611eSTejun Heo {
28adc5e8b5STejun Heo struct kobject *kobj = kn->parent->priv;
29375b611eSTejun Heo
30df23fc39STejun Heo if (kn->flags & KERNFS_LOCKDEP)
31324a56e1STejun Heo lockdep_assert_held(kn);
32375b611eSTejun Heo return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
33375b611eSTejun Heo }
34375b611eSTejun Heo
3513c589d5STejun Heo /*
3613c589d5STejun Heo * Reads on sysfs are handled through seq_file, which takes care of hairy
3713c589d5STejun Heo * details like buffering and seeking. The following function pipes
3813c589d5STejun Heo * sysfs_ops->show() result through seq_file.
391da177e4SLinus Torvalds */
sysfs_kf_seq_show(struct seq_file * sf,void * v)40c2b19dafSTejun Heo static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
411da177e4SLinus Torvalds {
42c525aaddSTejun Heo struct kernfs_open_file *of = sf->private;
43adc5e8b5STejun Heo struct kobject *kobj = of->kn->parent->priv;
44324a56e1STejun Heo const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
451da177e4SLinus Torvalds ssize_t count;
46c2b19dafSTejun Heo char *buf;
471da177e4SLinus Torvalds
48820879eeSChristoph Hellwig if (WARN_ON_ONCE(!ops->show))
49820879eeSChristoph Hellwig return -EINVAL;
50820879eeSChristoph Hellwig
51f5c16f29STejun Heo /* acquire buffer and ensure that it's >= PAGE_SIZE and clear */
5213c589d5STejun Heo count = seq_get_buf(sf, &buf);
5313c589d5STejun Heo if (count < PAGE_SIZE) {
5413c589d5STejun Heo seq_commit(sf, -1);
5513c589d5STejun Heo return 0;
5613c589d5STejun Heo }
57f5c16f29STejun Heo memset(buf, 0, PAGE_SIZE);
581da177e4SLinus Torvalds
59324a56e1STejun Heo count = ops->show(kobj, of->kn->priv, buf);
6013c589d5STejun Heo if (count < 0)
6113c589d5STejun Heo return count;
620ab66088STejun Heo
638118a859SMiao Xie /*
648118a859SMiao Xie * The code works fine with PAGE_SIZE return but it's likely to
658118a859SMiao Xie * indicate truncated result or overflow in normal use cases.
668118a859SMiao Xie */
67815d2d50SAndrew Morton if (count >= (ssize_t)PAGE_SIZE) {
689e6d35ffSSergey Senozhatsky printk("fill_read_buffer: %pS returned bad count\n",
699e6d35ffSSergey Senozhatsky ops->show);
70815d2d50SAndrew Morton /* Try to struggle along */
71815d2d50SAndrew Morton count = PAGE_SIZE - 1;
72815d2d50SAndrew Morton }
7313c589d5STejun Heo seq_commit(sf, count);
7413c589d5STejun Heo return 0;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds
sysfs_kf_bin_read(struct kernfs_open_file * of,char * buf,size_t count,loff_t pos)77c525aaddSTejun Heo static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf,
78c2b19dafSTejun Heo size_t count, loff_t pos)
792f0c6b75STejun Heo {
80324a56e1STejun Heo struct bin_attribute *battr = of->kn->priv;
81adc5e8b5STejun Heo struct kobject *kobj = of->kn->parent->priv;
82c2b19dafSTejun Heo loff_t size = file_inode(of->file)->i_size;
832f0c6b75STejun Heo
84c2b19dafSTejun Heo if (!count)
852f0c6b75STejun Heo return 0;
862f0c6b75STejun Heo
872f0c6b75STejun Heo if (size) {
88eaa5cd92SVladimir Zapolskiy if (pos >= size)
892f0c6b75STejun Heo return 0;
90c2b19dafSTejun Heo if (pos + count > size)
91c2b19dafSTejun Heo count = size - pos;
922f0c6b75STejun Heo }
932f0c6b75STejun Heo
94c2b19dafSTejun Heo if (!battr->read)
95c2b19dafSTejun Heo return -EIO;
96c2b19dafSTejun Heo
97c2b19dafSTejun Heo return battr->read(of->file, kobj, battr, buf, pos, count);
98c2b19dafSTejun Heo }
99c2b19dafSTejun Heo
1004ef67a8cSNeilBrown /* kernfs read callback for regular sysfs files with pre-alloc */
sysfs_kf_read(struct kernfs_open_file * of,char * buf,size_t count,loff_t pos)1014ef67a8cSNeilBrown static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
1024ef67a8cSNeilBrown size_t count, loff_t pos)
1034ef67a8cSNeilBrown {
1044ef67a8cSNeilBrown const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
1054ef67a8cSNeilBrown struct kobject *kobj = of->kn->parent->priv;
106c8a139d0SNeilBrown ssize_t len;
1074ef67a8cSNeilBrown
1084ef67a8cSNeilBrown /*
1094ef67a8cSNeilBrown * If buf != of->prealloc_buf, we don't know how
1104ef67a8cSNeilBrown * large it is, so cannot safely pass it to ->show
1114ef67a8cSNeilBrown */
11217d0774fSKonstantin Khlebnikov if (WARN_ON_ONCE(buf != of->prealloc_buf))
1134ef67a8cSNeilBrown return 0;
11465da3484SNeilBrown len = ops->show(kobj, of->kn->priv, buf);
115c8a139d0SNeilBrown if (len < 0)
116c8a139d0SNeilBrown return len;
11717d0774fSKonstantin Khlebnikov if (pos) {
11817d0774fSKonstantin Khlebnikov if (len <= pos)
11917d0774fSKonstantin Khlebnikov return 0;
12017d0774fSKonstantin Khlebnikov len -= pos;
12117d0774fSKonstantin Khlebnikov memmove(buf, buf + pos, len);
12217d0774fSKonstantin Khlebnikov }
123c8a139d0SNeilBrown return min_t(ssize_t, count, len);
1244ef67a8cSNeilBrown }
1254ef67a8cSNeilBrown
12650b38ca0STejun Heo /* kernfs write callback for regular sysfs files */
sysfs_kf_write(struct kernfs_open_file * of,char * buf,size_t count,loff_t pos)127c525aaddSTejun Heo static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
12850b38ca0STejun Heo size_t count, loff_t pos)
1291da177e4SLinus Torvalds {
130324a56e1STejun Heo const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
131adc5e8b5STejun Heo struct kobject *kobj = of->kn->parent->priv;
132f9b9a621STejun Heo
13350b38ca0STejun Heo if (!count)
13450b38ca0STejun Heo return 0;
13550b38ca0STejun Heo
136324a56e1STejun Heo return ops->store(kobj, of->kn->priv, buf, count);
137f9b9a621STejun Heo }
1380ab66088STejun Heo
13950b38ca0STejun Heo /* kernfs write callback for bin sysfs files */
sysfs_kf_bin_write(struct kernfs_open_file * of,char * buf,size_t count,loff_t pos)140c525aaddSTejun Heo static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf,
14150b38ca0STejun Heo size_t count, loff_t pos)
14250b38ca0STejun Heo {
143324a56e1STejun Heo struct bin_attribute *battr = of->kn->priv;
144adc5e8b5STejun Heo struct kobject *kobj = of->kn->parent->priv;
14550b38ca0STejun Heo loff_t size = file_inode(of->file)->i_size;
1460ab66088STejun Heo
14750b38ca0STejun Heo if (size) {
14850b38ca0STejun Heo if (size <= pos)
14909368960SVladimir Zapolskiy return -EFBIG;
15050b38ca0STejun Heo count = min_t(ssize_t, count, size - pos);
15150b38ca0STejun Heo }
15250b38ca0STejun Heo if (!count)
15350b38ca0STejun Heo return 0;
15450b38ca0STejun Heo
15550b38ca0STejun Heo if (!battr->write)
15650b38ca0STejun Heo return -EIO;
15750b38ca0STejun Heo
15850b38ca0STejun Heo return battr->write(of->file, kobj, battr, buf, pos, count);
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds
sysfs_kf_bin_mmap(struct kernfs_open_file * of,struct vm_area_struct * vma)161c525aaddSTejun Heo static int sysfs_kf_bin_mmap(struct kernfs_open_file *of,
162fdbffaa4STejun Heo struct vm_area_struct *vma)
163fdbffaa4STejun Heo {
164324a56e1STejun Heo struct bin_attribute *battr = of->kn->priv;
165adc5e8b5STejun Heo struct kobject *kobj = of->kn->parent->priv;
166fdbffaa4STejun Heo
167fdbffaa4STejun Heo return battr->mmap(of->file, kobj, battr, vma);
168fdbffaa4STejun Heo }
169fdbffaa4STejun Heo
sysfs_kf_bin_open(struct kernfs_open_file * of)17074b30195SDaniel Vetter static int sysfs_kf_bin_open(struct kernfs_open_file *of)
17174b30195SDaniel Vetter {
17274b30195SDaniel Vetter struct bin_attribute *battr = of->kn->priv;
17374b30195SDaniel Vetter
174f06aff92SKrzysztof Wilczyński if (battr->f_mapping)
175f06aff92SKrzysztof Wilczyński of->file->f_mapping = battr->f_mapping();
17674b30195SDaniel Vetter
17774b30195SDaniel Vetter return 0;
17874b30195SDaniel Vetter }
17974b30195SDaniel Vetter
sysfs_notify(struct kobject * kobj,const char * dir,const char * attr)180324a56e1STejun Heo void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr)
181f1282c84SNeil Brown {
182324a56e1STejun Heo struct kernfs_node *kn = kobj->sd, *tmp;
183f1282c84SNeil Brown
184324a56e1STejun Heo if (kn && dir)
185324a56e1STejun Heo kn = kernfs_find_and_get(kn, dir);
186024f6471STejun Heo else
187324a56e1STejun Heo kernfs_get(kn);
18851225039STejun Heo
189324a56e1STejun Heo if (kn && attr) {
190324a56e1STejun Heo tmp = kernfs_find_and_get(kn, attr);
191324a56e1STejun Heo kernfs_put(kn);
192324a56e1STejun Heo kn = tmp;
193024f6471STejun Heo }
194024f6471STejun Heo
195324a56e1STejun Heo if (kn) {
196324a56e1STejun Heo kernfs_notify(kn);
197324a56e1STejun Heo kernfs_put(kn);
198024f6471STejun Heo }
1994508a7a7SNeilBrown }
2004508a7a7SNeilBrown EXPORT_SYMBOL_GPL(sysfs_notify);
2014508a7a7SNeilBrown
202f6acf8bbSTejun Heo static const struct kernfs_ops sysfs_file_kfops_empty = {
203f6acf8bbSTejun Heo };
204f6acf8bbSTejun Heo
205f6acf8bbSTejun Heo static const struct kernfs_ops sysfs_file_kfops_ro = {
206f6acf8bbSTejun Heo .seq_show = sysfs_kf_seq_show,
207f6acf8bbSTejun Heo };
208f6acf8bbSTejun Heo
209f6acf8bbSTejun Heo static const struct kernfs_ops sysfs_file_kfops_wo = {
210f6acf8bbSTejun Heo .write = sysfs_kf_write,
211f6acf8bbSTejun Heo };
212f6acf8bbSTejun Heo
213f6acf8bbSTejun Heo static const struct kernfs_ops sysfs_file_kfops_rw = {
214f6acf8bbSTejun Heo .seq_show = sysfs_kf_seq_show,
215f6acf8bbSTejun Heo .write = sysfs_kf_write,
216f6acf8bbSTejun Heo };
217f6acf8bbSTejun Heo
2184ef67a8cSNeilBrown static const struct kernfs_ops sysfs_prealloc_kfops_ro = {
2194ef67a8cSNeilBrown .read = sysfs_kf_read,
2204ef67a8cSNeilBrown .prealloc = true,
2214ef67a8cSNeilBrown };
2224ef67a8cSNeilBrown
2232b75869bSNeilBrown static const struct kernfs_ops sysfs_prealloc_kfops_wo = {
2242b75869bSNeilBrown .write = sysfs_kf_write,
2252b75869bSNeilBrown .prealloc = true,
2262b75869bSNeilBrown };
2272b75869bSNeilBrown
2282b75869bSNeilBrown static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
2294ef67a8cSNeilBrown .read = sysfs_kf_read,
2302b75869bSNeilBrown .write = sysfs_kf_write,
2312b75869bSNeilBrown .prealloc = true,
2322b75869bSNeilBrown };
2332b75869bSNeilBrown
234f6acf8bbSTejun Heo static const struct kernfs_ops sysfs_bin_kfops_ro = {
235f6acf8bbSTejun Heo .read = sysfs_kf_bin_read,
236f6acf8bbSTejun Heo };
237f6acf8bbSTejun Heo
238f6acf8bbSTejun Heo static const struct kernfs_ops sysfs_bin_kfops_wo = {
239f6acf8bbSTejun Heo .write = sysfs_kf_bin_write,
240f6acf8bbSTejun Heo };
241f6acf8bbSTejun Heo
242f6acf8bbSTejun Heo static const struct kernfs_ops sysfs_bin_kfops_rw = {
243f6acf8bbSTejun Heo .read = sysfs_kf_bin_read,
244f6acf8bbSTejun Heo .write = sysfs_kf_bin_write,
2459b2db6e1STejun Heo };
2469b2db6e1STejun Heo
2479b2db6e1STejun Heo static const struct kernfs_ops sysfs_bin_kfops_mmap = {
2489b2db6e1STejun Heo .read = sysfs_kf_bin_read,
2499b2db6e1STejun Heo .write = sysfs_kf_bin_write,
250f6acf8bbSTejun Heo .mmap = sysfs_kf_bin_mmap,
25174b30195SDaniel Vetter .open = sysfs_kf_bin_open,
252f6acf8bbSTejun Heo };
253f6acf8bbSTejun Heo
sysfs_add_file_mode_ns(struct kernfs_node * parent,const struct attribute * attr,umode_t mode,kuid_t uid,kgid_t gid,const void * ns)254324a56e1STejun Heo int sysfs_add_file_mode_ns(struct kernfs_node *parent,
2555cf3bb0dSChristoph Hellwig const struct attribute *attr, umode_t mode, kuid_t uid,
2565cf3bb0dSChristoph Hellwig kgid_t gid, const void *ns)
2571da177e4SLinus Torvalds {
2585cf3bb0dSChristoph Hellwig struct kobject *kobj = parent->priv;
2595cf3bb0dSChristoph Hellwig const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
260517e64f5STejun Heo struct lock_class_key *key = NULL;
261d1a1a960SChristoph Hellwig const struct kernfs_ops *ops = NULL;
262324a56e1STejun Heo struct kernfs_node *kn;
263f6acf8bbSTejun Heo
264f6acf8bbSTejun Heo /* every kobject with an attribute needs a ktype assigned */
265f6acf8bbSTejun Heo if (WARN(!sysfs_ops, KERN_ERR
266f6acf8bbSTejun Heo "missing sysfs attribute operations for kobject: %s\n",
267f6acf8bbSTejun Heo kobject_name(kobj)))
268f6acf8bbSTejun Heo return -EINVAL;
269f6acf8bbSTejun Heo
270d1a1a960SChristoph Hellwig if (mode & SYSFS_PREALLOC) {
271d1a1a960SChristoph Hellwig if (sysfs_ops->show && sysfs_ops->store)
2722b75869bSNeilBrown ops = &sysfs_prealloc_kfops_rw;
273d1a1a960SChristoph Hellwig else if (sysfs_ops->show)
2744ef67a8cSNeilBrown ops = &sysfs_prealloc_kfops_ro;
275d1a1a960SChristoph Hellwig else if (sysfs_ops->store)
2762b75869bSNeilBrown ops = &sysfs_prealloc_kfops_wo;
277d1a1a960SChristoph Hellwig } else {
278d1a1a960SChristoph Hellwig if (sysfs_ops->show && sysfs_ops->store)
279d1a1a960SChristoph Hellwig ops = &sysfs_file_kfops_rw;
280d1a1a960SChristoph Hellwig else if (sysfs_ops->show)
281d1a1a960SChristoph Hellwig ops = &sysfs_file_kfops_ro;
282d1a1a960SChristoph Hellwig else if (sysfs_ops->store)
2832b75869bSNeilBrown ops = &sysfs_file_kfops_wo;
284d1a1a960SChristoph Hellwig }
285d1a1a960SChristoph Hellwig
286d1a1a960SChristoph Hellwig if (!ops)
287f6acf8bbSTejun Heo ops = &sysfs_file_kfops_empty;
288471bd7b7STejun Heo
2895cf3bb0dSChristoph Hellwig #ifdef CONFIG_DEBUG_LOCK_ALLOC
2905cf3bb0dSChristoph Hellwig if (!attr->ignore_lockdep)
2915cf3bb0dSChristoph Hellwig key = attr->key ?: (struct lock_class_key *)&attr->skey;
2925cf3bb0dSChristoph Hellwig #endif
2935cf3bb0dSChristoph Hellwig
2945cf3bb0dSChristoph Hellwig kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
2955cf3bb0dSChristoph Hellwig PAGE_SIZE, ops, (void *)attr, ns, key);
2965cf3bb0dSChristoph Hellwig if (IS_ERR(kn)) {
2975cf3bb0dSChristoph Hellwig if (PTR_ERR(kn) == -EEXIST)
2985cf3bb0dSChristoph Hellwig sysfs_warn_dup(parent, attr->name);
2995cf3bb0dSChristoph Hellwig return PTR_ERR(kn);
3005cf3bb0dSChristoph Hellwig }
3015cf3bb0dSChristoph Hellwig return 0;
3025cf3bb0dSChristoph Hellwig }
3035cf3bb0dSChristoph Hellwig
sysfs_add_bin_file_mode_ns(struct kernfs_node * parent,const struct bin_attribute * battr,umode_t mode,kuid_t uid,kgid_t gid,const void * ns)3045cf3bb0dSChristoph Hellwig int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent,
3055cf3bb0dSChristoph Hellwig const struct bin_attribute *battr, umode_t mode,
3065cf3bb0dSChristoph Hellwig kuid_t uid, kgid_t gid, const void *ns)
3075cf3bb0dSChristoph Hellwig {
3085cf3bb0dSChristoph Hellwig const struct attribute *attr = &battr->attr;
3095cf3bb0dSChristoph Hellwig struct lock_class_key *key = NULL;
3105cf3bb0dSChristoph Hellwig const struct kernfs_ops *ops;
3115cf3bb0dSChristoph Hellwig struct kernfs_node *kn;
312f6acf8bbSTejun Heo
3139b2db6e1STejun Heo if (battr->mmap)
3149b2db6e1STejun Heo ops = &sysfs_bin_kfops_mmap;
3159b2db6e1STejun Heo else if (battr->read && battr->write)
316f6acf8bbSTejun Heo ops = &sysfs_bin_kfops_rw;
317f6acf8bbSTejun Heo else if (battr->read)
318f6acf8bbSTejun Heo ops = &sysfs_bin_kfops_ro;
319f6acf8bbSTejun Heo else if (battr->write)
320f6acf8bbSTejun Heo ops = &sysfs_bin_kfops_wo;
321f6acf8bbSTejun Heo else
322f6acf8bbSTejun Heo ops = &sysfs_file_kfops_empty;
323471bd7b7STejun Heo
324517e64f5STejun Heo #ifdef CONFIG_DEBUG_LOCK_ALLOC
325517e64f5STejun Heo if (!attr->ignore_lockdep)
326517e64f5STejun Heo key = attr->key ?: (struct lock_class_key *)&attr->skey;
327517e64f5STejun Heo #endif
3285f81880dSDmitry Torokhov
3295f81880dSDmitry Torokhov kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
3305cf3bb0dSChristoph Hellwig battr->size, ops, (void *)attr, ns, key);
331324a56e1STejun Heo if (IS_ERR(kn)) {
332324a56e1STejun Heo if (PTR_ERR(kn) == -EEXIST)
333324a56e1STejun Heo sysfs_warn_dup(parent, attr->name);
334324a56e1STejun Heo return PTR_ERR(kn);
335496f7394STejun Heo }
336496f7394STejun Heo return 0;
337496f7394STejun Heo }
338496f7394STejun Heo
3391da177e4SLinus Torvalds /**
34058292cbeSTejun Heo * sysfs_create_file_ns - create an attribute file for an object with custom ns
34158292cbeSTejun Heo * @kobj: object we're creating for
34258292cbeSTejun Heo * @attr: attribute descriptor
34358292cbeSTejun Heo * @ns: namespace the new file should belong to
3441da177e4SLinus Torvalds */
sysfs_create_file_ns(struct kobject * kobj,const struct attribute * attr,const void * ns)34558292cbeSTejun Heo int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
34658292cbeSTejun Heo const void *ns)
3471da177e4SLinus Torvalds {
3485f81880dSDmitry Torokhov kuid_t uid;
3495f81880dSDmitry Torokhov kgid_t gid;
3505f81880dSDmitry Torokhov
351de96e9feSGreg Kroah-Hartman if (WARN_ON(!kobj || !kobj->sd || !attr))
352de96e9feSGreg Kroah-Hartman return -EINVAL;
3531da177e4SLinus Torvalds
3545f81880dSDmitry Torokhov kobject_get_ownership(kobj, &uid, &gid);
3555cf3bb0dSChristoph Hellwig return sysfs_add_file_mode_ns(kobj->sd, attr, attr->mode, uid, gid, ns);
3561da177e4SLinus Torvalds }
35758292cbeSTejun Heo EXPORT_SYMBOL_GPL(sysfs_create_file_ns);
3581da177e4SLinus Torvalds
sysfs_create_files(struct kobject * kobj,const struct attribute * const * ptr)3599ee4685cSJani Nikula int sysfs_create_files(struct kobject *kobj, const struct attribute * const *ptr)
3601c205ae1SAndi Kleen {
3611c205ae1SAndi Kleen int err = 0;
3621c205ae1SAndi Kleen int i;
3631c205ae1SAndi Kleen
3641c205ae1SAndi Kleen for (i = 0; ptr[i] && !err; i++)
3651c205ae1SAndi Kleen err = sysfs_create_file(kobj, ptr[i]);
3661c205ae1SAndi Kleen if (err)
3671c205ae1SAndi Kleen while (--i >= 0)
3681c205ae1SAndi Kleen sysfs_remove_file(kobj, ptr[i]);
3691c205ae1SAndi Kleen return err;
3701c205ae1SAndi Kleen }
3711b866757SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_create_files);
3721da177e4SLinus Torvalds
3731da177e4SLinus Torvalds /**
374dfa87c82SAlan Stern * sysfs_add_file_to_group - add an attribute file to a pre-existing group.
375dfa87c82SAlan Stern * @kobj: object we're acting for.
376dfa87c82SAlan Stern * @attr: attribute descriptor.
377dfa87c82SAlan Stern * @group: group name.
378dfa87c82SAlan Stern */
sysfs_add_file_to_group(struct kobject * kobj,const struct attribute * attr,const char * group)379dfa87c82SAlan Stern int sysfs_add_file_to_group(struct kobject *kobj,
380dfa87c82SAlan Stern const struct attribute *attr, const char *group)
381dfa87c82SAlan Stern {
382324a56e1STejun Heo struct kernfs_node *parent;
3835f81880dSDmitry Torokhov kuid_t uid;
3845f81880dSDmitry Torokhov kgid_t gid;
385dfa87c82SAlan Stern int error;
386dfa87c82SAlan Stern
387ccf73cf3STejun Heo if (group) {
388324a56e1STejun Heo parent = kernfs_find_and_get(kobj->sd, group);
389ccf73cf3STejun Heo } else {
390324a56e1STejun Heo parent = kobj->sd;
391324a56e1STejun Heo kernfs_get(parent);
392ccf73cf3STejun Heo }
39311f24fbdSJames Bottomley
394324a56e1STejun Heo if (!parent)
395608e266aSTejun Heo return -ENOENT;
396608e266aSTejun Heo
3975f81880dSDmitry Torokhov kobject_get_ownership(kobj, &uid, &gid);
3985cf3bb0dSChristoph Hellwig error = sysfs_add_file_mode_ns(parent, attr, attr->mode, uid, gid,
3995cf3bb0dSChristoph Hellwig NULL);
400324a56e1STejun Heo kernfs_put(parent);
401608e266aSTejun Heo
402dfa87c82SAlan Stern return error;
403dfa87c82SAlan Stern }
404dfa87c82SAlan Stern EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
405dfa87c82SAlan Stern
4061da177e4SLinus Torvalds /**
40731e5abe9SKay Sievers * sysfs_chmod_file - update the modified mode value on an object attribute.
40831e5abe9SKay Sievers * @kobj: object we're acting for.
40931e5abe9SKay Sievers * @attr: attribute descriptor.
41031e5abe9SKay Sievers * @mode: file permissions.
41131e5abe9SKay Sievers *
41231e5abe9SKay Sievers */
sysfs_chmod_file(struct kobject * kobj,const struct attribute * attr,umode_t mode)41349c19400SJean Delvare int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr,
41448176a97SAl Viro umode_t mode)
41531e5abe9SKay Sievers {
416324a56e1STejun Heo struct kernfs_node *kn;
417bc062b1bSManeesh Soni struct iattr newattrs;
41851225039STejun Heo int rc;
41931e5abe9SKay Sievers
420324a56e1STejun Heo kn = kernfs_find_and_get(kobj->sd, attr->name);
421324a56e1STejun Heo if (!kn)
4225d60418eSTejun Heo return -ENOENT;
42351225039STejun Heo
424adc5e8b5STejun Heo newattrs.ia_mode = (mode & S_IALLUGO) | (kn->mode & ~S_IALLUGO);
4254c6974f5SEric W. Biederman newattrs.ia_valid = ATTR_MODE;
426f88123eaSTejun Heo
427324a56e1STejun Heo rc = kernfs_setattr(kn, &newattrs);
4285d60418eSTejun Heo
429324a56e1STejun Heo kernfs_put(kn);
43051225039STejun Heo return rc;
43131e5abe9SKay Sievers }
43231e5abe9SKay Sievers EXPORT_SYMBOL_GPL(sysfs_chmod_file);
43331e5abe9SKay Sievers
43431e5abe9SKay Sievers /**
4352afc9166SBart Van Assche * sysfs_break_active_protection - break "active" protection
4362afc9166SBart Van Assche * @kobj: The kernel object @attr is associated with.
4372afc9166SBart Van Assche * @attr: The attribute to break the "active" protection for.
4382afc9166SBart Van Assche *
4392afc9166SBart Van Assche * With sysfs, just like kernfs, deletion of an attribute is postponed until
4402afc9166SBart Van Assche * all active .show() and .store() callbacks have finished unless this function
4412afc9166SBart Van Assche * is called. Hence this function is useful in methods that implement self
4422afc9166SBart Van Assche * deletion.
4432afc9166SBart Van Assche */
sysfs_break_active_protection(struct kobject * kobj,const struct attribute * attr)4442afc9166SBart Van Assche struct kernfs_node *sysfs_break_active_protection(struct kobject *kobj,
4452afc9166SBart Van Assche const struct attribute *attr)
4462afc9166SBart Van Assche {
4472afc9166SBart Van Assche struct kernfs_node *kn;
4482afc9166SBart Van Assche
4492afc9166SBart Van Assche kobject_get(kobj);
4502afc9166SBart Van Assche kn = kernfs_find_and_get(kobj->sd, attr->name);
4512afc9166SBart Van Assche if (kn)
4522afc9166SBart Van Assche kernfs_break_active_protection(kn);
453*ac107356SAlan Stern else
454*ac107356SAlan Stern kobject_put(kobj);
4552afc9166SBart Van Assche return kn;
4562afc9166SBart Van Assche }
4572afc9166SBart Van Assche EXPORT_SYMBOL_GPL(sysfs_break_active_protection);
4582afc9166SBart Van Assche
4592afc9166SBart Van Assche /**
4602afc9166SBart Van Assche * sysfs_unbreak_active_protection - restore "active" protection
4612afc9166SBart Van Assche * @kn: Pointer returned by sysfs_break_active_protection().
4622afc9166SBart Van Assche *
4632afc9166SBart Van Assche * Undo the effects of sysfs_break_active_protection(). Since this function
4642afc9166SBart Van Assche * calls kernfs_put() on the kernfs node that corresponds to the 'attr'
4652afc9166SBart Van Assche * argument passed to sysfs_break_active_protection() that attribute may have
4662afc9166SBart Van Assche * been removed between the sysfs_break_active_protection() and
4672afc9166SBart Van Assche * sysfs_unbreak_active_protection() calls, it is not safe to access @kn after
4682afc9166SBart Van Assche * this function has returned.
4692afc9166SBart Van Assche */
sysfs_unbreak_active_protection(struct kernfs_node * kn)4702afc9166SBart Van Assche void sysfs_unbreak_active_protection(struct kernfs_node *kn)
4712afc9166SBart Van Assche {
4722afc9166SBart Van Assche struct kobject *kobj = kn->parent->priv;
4732afc9166SBart Van Assche
4742afc9166SBart Van Assche kernfs_unbreak_active_protection(kn);
4752afc9166SBart Van Assche kernfs_put(kn);
4762afc9166SBart Van Assche kobject_put(kobj);
4772afc9166SBart Van Assche }
4782afc9166SBart Van Assche EXPORT_SYMBOL_GPL(sysfs_unbreak_active_protection);
4792afc9166SBart Van Assche
4802afc9166SBart Van Assche /**
48158292cbeSTejun Heo * sysfs_remove_file_ns - remove an object attribute with a custom ns tag
48258292cbeSTejun Heo * @kobj: object we're acting for
48358292cbeSTejun Heo * @attr: attribute descriptor
48458292cbeSTejun Heo * @ns: namespace tag of the file to remove
4851da177e4SLinus Torvalds *
48658292cbeSTejun Heo * Hash the attribute name and namespace tag and kill the victim.
4871da177e4SLinus Torvalds */
sysfs_remove_file_ns(struct kobject * kobj,const struct attribute * attr,const void * ns)48858292cbeSTejun Heo void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
48958292cbeSTejun Heo const void *ns)
4901da177e4SLinus Torvalds {
491324a56e1STejun Heo struct kernfs_node *parent = kobj->sd;
492487505c2SEric W. Biederman
493324a56e1STejun Heo kernfs_remove_by_name_ns(parent, attr->name, ns);
4941da177e4SLinus Torvalds }
49558292cbeSTejun Heo EXPORT_SYMBOL_GPL(sysfs_remove_file_ns);
4961da177e4SLinus Torvalds
4976b0afc2aSTejun Heo /**
4986b0afc2aSTejun Heo * sysfs_remove_file_self - remove an object attribute from its own method
4996b0afc2aSTejun Heo * @kobj: object we're acting for
5006b0afc2aSTejun Heo * @attr: attribute descriptor
5016b0afc2aSTejun Heo *
5026b0afc2aSTejun Heo * See kernfs_remove_self() for details.
5036b0afc2aSTejun Heo */
sysfs_remove_file_self(struct kobject * kobj,const struct attribute * attr)5046b0afc2aSTejun Heo bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr)
5056b0afc2aSTejun Heo {
5066b0afc2aSTejun Heo struct kernfs_node *parent = kobj->sd;
5076b0afc2aSTejun Heo struct kernfs_node *kn;
5086b0afc2aSTejun Heo bool ret;
5096b0afc2aSTejun Heo
5106b0afc2aSTejun Heo kn = kernfs_find_and_get(parent, attr->name);
5116b0afc2aSTejun Heo if (WARN_ON_ONCE(!kn))
5126b0afc2aSTejun Heo return false;
5136b0afc2aSTejun Heo
5146b0afc2aSTejun Heo ret = kernfs_remove_self(kn);
5156b0afc2aSTejun Heo
5166b0afc2aSTejun Heo kernfs_put(kn);
5176b0afc2aSTejun Heo return ret;
5186b0afc2aSTejun Heo }
5199ddacff1SJack Wang EXPORT_SYMBOL_GPL(sysfs_remove_file_self);
5206b0afc2aSTejun Heo
sysfs_remove_files(struct kobject * kobj,const struct attribute * const * ptr)5219ee4685cSJani Nikula void sysfs_remove_files(struct kobject *kobj, const struct attribute * const *ptr)
5221c205ae1SAndi Kleen {
5231c205ae1SAndi Kleen int i;
5244bd4e92cSStephen Martin
5251c205ae1SAndi Kleen for (i = 0; ptr[i]; i++)
5261c205ae1SAndi Kleen sysfs_remove_file(kobj, ptr[i]);
5271c205ae1SAndi Kleen }
5281b866757SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(sysfs_remove_files);
5291da177e4SLinus Torvalds
530dfa87c82SAlan Stern /**
531dfa87c82SAlan Stern * sysfs_remove_file_from_group - remove an attribute file from a group.
532dfa87c82SAlan Stern * @kobj: object we're acting for.
533dfa87c82SAlan Stern * @attr: attribute descriptor.
534dfa87c82SAlan Stern * @group: group name.
535dfa87c82SAlan Stern */
sysfs_remove_file_from_group(struct kobject * kobj,const struct attribute * attr,const char * group)536dfa87c82SAlan Stern void sysfs_remove_file_from_group(struct kobject *kobj,
537dfa87c82SAlan Stern const struct attribute *attr, const char *group)
538dfa87c82SAlan Stern {
539324a56e1STejun Heo struct kernfs_node *parent;
540dfa87c82SAlan Stern
541ccf73cf3STejun Heo if (group) {
542324a56e1STejun Heo parent = kernfs_find_and_get(kobj->sd, group);
543ccf73cf3STejun Heo } else {
544324a56e1STejun Heo parent = kobj->sd;
545324a56e1STejun Heo kernfs_get(parent);
546ccf73cf3STejun Heo }
547ccf73cf3STejun Heo
548324a56e1STejun Heo if (parent) {
549324a56e1STejun Heo kernfs_remove_by_name(parent, attr->name);
550324a56e1STejun Heo kernfs_put(parent);
551dfa87c82SAlan Stern }
552dfa87c82SAlan Stern }
553dfa87c82SAlan Stern EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
554dfa87c82SAlan Stern
5553124eb16STejun Heo /**
5563124eb16STejun Heo * sysfs_create_bin_file - create binary file for object.
5573124eb16STejun Heo * @kobj: object.
5583124eb16STejun Heo * @attr: attribute descriptor.
5593124eb16STejun Heo */
sysfs_create_bin_file(struct kobject * kobj,const struct bin_attribute * attr)5603124eb16STejun Heo int sysfs_create_bin_file(struct kobject *kobj,
5613124eb16STejun Heo const struct bin_attribute *attr)
5623124eb16STejun Heo {
5635f81880dSDmitry Torokhov kuid_t uid;
5645f81880dSDmitry Torokhov kgid_t gid;
5655f81880dSDmitry Torokhov
566de96e9feSGreg Kroah-Hartman if (WARN_ON(!kobj || !kobj->sd || !attr))
567de96e9feSGreg Kroah-Hartman return -EINVAL;
5683124eb16STejun Heo
5695f81880dSDmitry Torokhov kobject_get_ownership(kobj, &uid, &gid);
5705cf3bb0dSChristoph Hellwig return sysfs_add_bin_file_mode_ns(kobj->sd, attr, attr->attr.mode, uid,
5715cf3bb0dSChristoph Hellwig gid, NULL);
5723124eb16STejun Heo }
5733124eb16STejun Heo EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
5743124eb16STejun Heo
5753124eb16STejun Heo /**
5763124eb16STejun Heo * sysfs_remove_bin_file - remove binary file for object.
5773124eb16STejun Heo * @kobj: object.
5783124eb16STejun Heo * @attr: attribute descriptor.
5793124eb16STejun Heo */
sysfs_remove_bin_file(struct kobject * kobj,const struct bin_attribute * attr)5803124eb16STejun Heo void sysfs_remove_bin_file(struct kobject *kobj,
5813124eb16STejun Heo const struct bin_attribute *attr)
5823124eb16STejun Heo {
583879f40d1STejun Heo kernfs_remove_by_name(kobj->sd, attr->attr.name);
5843124eb16STejun Heo }
5853124eb16STejun Heo EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
586f70ce185SChristian Brauner
internal_change_owner(struct kernfs_node * kn,kuid_t kuid,kgid_t kgid)587f70ce185SChristian Brauner static int internal_change_owner(struct kernfs_node *kn, kuid_t kuid,
588f70ce185SChristian Brauner kgid_t kgid)
589f70ce185SChristian Brauner {
590f70ce185SChristian Brauner struct iattr newattrs = {
591f70ce185SChristian Brauner .ia_valid = ATTR_UID | ATTR_GID,
592f70ce185SChristian Brauner .ia_uid = kuid,
593f70ce185SChristian Brauner .ia_gid = kgid,
594f70ce185SChristian Brauner };
595f70ce185SChristian Brauner return kernfs_setattr(kn, &newattrs);
596f70ce185SChristian Brauner }
597f70ce185SChristian Brauner
598f70ce185SChristian Brauner /**
5990666a3aeSChristian Brauner * sysfs_link_change_owner - change owner of a sysfs file.
6000666a3aeSChristian Brauner * @kobj: object of the kernfs_node the symlink is located in.
6010666a3aeSChristian Brauner * @targ: object of the kernfs_node the symlink points to.
6020666a3aeSChristian Brauner * @name: name of the link.
6030666a3aeSChristian Brauner * @kuid: new owner's kuid
6040666a3aeSChristian Brauner * @kgid: new owner's kgid
6050666a3aeSChristian Brauner *
6060666a3aeSChristian Brauner * This function looks up the sysfs symlink entry @name under @kobj and changes
6070666a3aeSChristian Brauner * the ownership to @kuid/@kgid. The symlink is looked up in the namespace of
6080666a3aeSChristian Brauner * @targ.
6090666a3aeSChristian Brauner *
6100666a3aeSChristian Brauner * Returns 0 on success or error code on failure.
6110666a3aeSChristian Brauner */
sysfs_link_change_owner(struct kobject * kobj,struct kobject * targ,const char * name,kuid_t kuid,kgid_t kgid)6120666a3aeSChristian Brauner int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ,
6130666a3aeSChristian Brauner const char *name, kuid_t kuid, kgid_t kgid)
6140666a3aeSChristian Brauner {
6150666a3aeSChristian Brauner struct kernfs_node *kn = NULL;
6160666a3aeSChristian Brauner int error;
6170666a3aeSChristian Brauner
6180666a3aeSChristian Brauner if (!name || !kobj->state_in_sysfs || !targ->state_in_sysfs)
6190666a3aeSChristian Brauner return -EINVAL;
6200666a3aeSChristian Brauner
6210666a3aeSChristian Brauner error = -ENOENT;
6220666a3aeSChristian Brauner kn = kernfs_find_and_get_ns(kobj->sd, name, targ->sd->ns);
6230666a3aeSChristian Brauner if (!kn)
6240666a3aeSChristian Brauner goto out;
6250666a3aeSChristian Brauner
6260666a3aeSChristian Brauner error = -EINVAL;
6270666a3aeSChristian Brauner if (kernfs_type(kn) != KERNFS_LINK)
6280666a3aeSChristian Brauner goto out;
6290666a3aeSChristian Brauner if (kn->symlink.target_kn->priv != targ)
6300666a3aeSChristian Brauner goto out;
6310666a3aeSChristian Brauner
6320666a3aeSChristian Brauner error = internal_change_owner(kn, kuid, kgid);
6330666a3aeSChristian Brauner
6340666a3aeSChristian Brauner out:
6350666a3aeSChristian Brauner kernfs_put(kn);
6360666a3aeSChristian Brauner return error;
6370666a3aeSChristian Brauner }
6380666a3aeSChristian Brauner
6390666a3aeSChristian Brauner /**
640f70ce185SChristian Brauner * sysfs_file_change_owner - change owner of a sysfs file.
641f70ce185SChristian Brauner * @kobj: object.
642f70ce185SChristian Brauner * @name: name of the file to change.
643f70ce185SChristian Brauner * @kuid: new owner's kuid
644f70ce185SChristian Brauner * @kgid: new owner's kgid
645f70ce185SChristian Brauner *
646f70ce185SChristian Brauner * This function looks up the sysfs entry @name under @kobj and changes the
647f70ce185SChristian Brauner * ownership to @kuid/@kgid.
648f70ce185SChristian Brauner *
649f70ce185SChristian Brauner * Returns 0 on success or error code on failure.
650f70ce185SChristian Brauner */
sysfs_file_change_owner(struct kobject * kobj,const char * name,kuid_t kuid,kgid_t kgid)651f70ce185SChristian Brauner int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid,
652f70ce185SChristian Brauner kgid_t kgid)
653f70ce185SChristian Brauner {
654f70ce185SChristian Brauner struct kernfs_node *kn;
655f70ce185SChristian Brauner int error;
656f70ce185SChristian Brauner
657f70ce185SChristian Brauner if (!name)
658f70ce185SChristian Brauner return -EINVAL;
659f70ce185SChristian Brauner
660f70ce185SChristian Brauner if (!kobj->state_in_sysfs)
661f70ce185SChristian Brauner return -EINVAL;
662f70ce185SChristian Brauner
663f70ce185SChristian Brauner kn = kernfs_find_and_get(kobj->sd, name);
664f70ce185SChristian Brauner if (!kn)
665f70ce185SChristian Brauner return -ENOENT;
666f70ce185SChristian Brauner
667f70ce185SChristian Brauner error = internal_change_owner(kn, kuid, kgid);
668f70ce185SChristian Brauner
669f70ce185SChristian Brauner kernfs_put(kn);
670f70ce185SChristian Brauner
671f70ce185SChristian Brauner return error;
672f70ce185SChristian Brauner }
673f70ce185SChristian Brauner EXPORT_SYMBOL_GPL(sysfs_file_change_owner);
6742c4f9401SChristian Brauner
6752c4f9401SChristian Brauner /**
6762c4f9401SChristian Brauner * sysfs_change_owner - change owner of the given object.
6772c4f9401SChristian Brauner * @kobj: object.
6782c4f9401SChristian Brauner * @kuid: new owner's kuid
6792c4f9401SChristian Brauner * @kgid: new owner's kgid
6802c4f9401SChristian Brauner *
6812c4f9401SChristian Brauner * Change the owner of the default directory, files, groups, and attributes of
6822c4f9401SChristian Brauner * @kobj to @kuid/@kgid. Note that sysfs_change_owner mirrors how the sysfs
6832c4f9401SChristian Brauner * entries for a kobject are added by driver core. In summary,
6842c4f9401SChristian Brauner * sysfs_change_owner() takes care of the default directory entry for @kobj,
6852c4f9401SChristian Brauner * the default attributes associated with the ktype of @kobj and the default
6862c4f9401SChristian Brauner * attributes associated with the ktype of @kobj.
6872c4f9401SChristian Brauner * Additional properties not added by driver core have to be changed by the
6882c4f9401SChristian Brauner * driver or subsystem which created them. This is similar to how
6892c4f9401SChristian Brauner * driver/subsystem specific entries are removed.
6902c4f9401SChristian Brauner *
6912c4f9401SChristian Brauner * Returns 0 on success or error code on failure.
6922c4f9401SChristian Brauner */
sysfs_change_owner(struct kobject * kobj,kuid_t kuid,kgid_t kgid)6932c4f9401SChristian Brauner int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid)
6942c4f9401SChristian Brauner {
6952c4f9401SChristian Brauner int error;
6962c4f9401SChristian Brauner const struct kobj_type *ktype;
6972c4f9401SChristian Brauner
6982c4f9401SChristian Brauner if (!kobj->state_in_sysfs)
6992c4f9401SChristian Brauner return -EINVAL;
7002c4f9401SChristian Brauner
7012c4f9401SChristian Brauner /* Change the owner of the kobject itself. */
7022c4f9401SChristian Brauner error = internal_change_owner(kobj->sd, kuid, kgid);
7032c4f9401SChristian Brauner if (error)
7042c4f9401SChristian Brauner return error;
7052c4f9401SChristian Brauner
7062c4f9401SChristian Brauner ktype = get_ktype(kobj);
7072c4f9401SChristian Brauner if (ktype) {
7082c4f9401SChristian Brauner /*
7092c4f9401SChristian Brauner * Change owner of the default groups associated with the
7102c4f9401SChristian Brauner * ktype of @kobj.
7112c4f9401SChristian Brauner */
7122c4f9401SChristian Brauner error = sysfs_groups_change_owner(kobj, ktype->default_groups,
7132c4f9401SChristian Brauner kuid, kgid);
7142c4f9401SChristian Brauner if (error)
7152c4f9401SChristian Brauner return error;
7162c4f9401SChristian Brauner }
7172c4f9401SChristian Brauner
7182c4f9401SChristian Brauner return 0;
7192c4f9401SChristian Brauner }
7202c4f9401SChristian Brauner EXPORT_SYMBOL_GPL(sysfs_change_owner);
7212efc459dSJoe Perches
7222efc459dSJoe Perches /**
7232efc459dSJoe Perches * sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer.
7242efc459dSJoe Perches * @buf: start of PAGE_SIZE buffer.
7252efc459dSJoe Perches * @fmt: format
7262efc459dSJoe Perches * @...: optional arguments to @format
7272efc459dSJoe Perches *
7282efc459dSJoe Perches *
7292efc459dSJoe Perches * Returns number of characters written to @buf.
7302efc459dSJoe Perches */
sysfs_emit(char * buf,const char * fmt,...)7312efc459dSJoe Perches int sysfs_emit(char *buf, const char *fmt, ...)
7322efc459dSJoe Perches {
7332efc459dSJoe Perches va_list args;
7342efc459dSJoe Perches int len;
7352efc459dSJoe Perches
7362efc459dSJoe Perches if (WARN(!buf || offset_in_page(buf),
7372efc459dSJoe Perches "invalid sysfs_emit: buf:%p\n", buf))
7382efc459dSJoe Perches return 0;
7392efc459dSJoe Perches
7402efc459dSJoe Perches va_start(args, fmt);
7412efc459dSJoe Perches len = vscnprintf(buf, PAGE_SIZE, fmt, args);
7422efc459dSJoe Perches va_end(args);
7432efc459dSJoe Perches
7442efc459dSJoe Perches return len;
7452efc459dSJoe Perches }
7462efc459dSJoe Perches EXPORT_SYMBOL_GPL(sysfs_emit);
7472efc459dSJoe Perches
7482efc459dSJoe Perches /**
7492efc459dSJoe Perches * sysfs_emit_at - scnprintf equivalent, aware of PAGE_SIZE buffer.
7502efc459dSJoe Perches * @buf: start of PAGE_SIZE buffer.
7512efc459dSJoe Perches * @at: offset in @buf to start write in bytes
7522efc459dSJoe Perches * @at must be >= 0 && < PAGE_SIZE
7532efc459dSJoe Perches * @fmt: format
7542efc459dSJoe Perches * @...: optional arguments to @fmt
7552efc459dSJoe Perches *
7562efc459dSJoe Perches *
7572efc459dSJoe Perches * Returns number of characters written starting at &@buf[@at].
7582efc459dSJoe Perches */
sysfs_emit_at(char * buf,int at,const char * fmt,...)7592efc459dSJoe Perches int sysfs_emit_at(char *buf, int at, const char *fmt, ...)
7602efc459dSJoe Perches {
7612efc459dSJoe Perches va_list args;
7622efc459dSJoe Perches int len;
7632efc459dSJoe Perches
7642efc459dSJoe Perches if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE,
7652efc459dSJoe Perches "invalid sysfs_emit_at: buf:%p at:%d\n", buf, at))
7662efc459dSJoe Perches return 0;
7672efc459dSJoe Perches
7682efc459dSJoe Perches va_start(args, fmt);
7692efc459dSJoe Perches len = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args);
7702efc459dSJoe Perches va_end(args);
7712efc459dSJoe Perches
7722efc459dSJoe Perches return len;
7732efc459dSJoe Perches }
7742efc459dSJoe Perches EXPORT_SYMBOL_GPL(sysfs_emit_at);
775