162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/sysfs/file.c - sysfs regular (text) file implementation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2001-3 Patrick Mochel
662306a36Sopenharmony_ci * Copyright (c) 2007 SUSE Linux Products GmbH
762306a36Sopenharmony_ci * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Please see Documentation/filesystems/sysfs.rst for more information.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/kobject.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/list.h>
1662306a36Sopenharmony_ci#include <linux/mutex.h>
1762306a36Sopenharmony_ci#include <linux/seq_file.h>
1862306a36Sopenharmony_ci#include <linux/mm.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "sysfs.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * Determine ktype->sysfs_ops for the given kernfs_node.  This function
2462306a36Sopenharmony_ci * must be called while holding an active reference.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cistatic const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct kobject *kobj = kn->parent->priv;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (kn->flags & KERNFS_LOCKDEP)
3162306a36Sopenharmony_ci		lockdep_assert_held(kn);
3262306a36Sopenharmony_ci	return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*
3662306a36Sopenharmony_ci * Reads on sysfs are handled through seq_file, which takes care of hairy
3762306a36Sopenharmony_ci * details like buffering and seeking.  The following function pipes
3862306a36Sopenharmony_ci * sysfs_ops->show() result through seq_file.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_cistatic int sysfs_kf_seq_show(struct seq_file *sf, void *v)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct kernfs_open_file *of = sf->private;
4362306a36Sopenharmony_ci	struct kobject *kobj = of->kn->parent->priv;
4462306a36Sopenharmony_ci	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
4562306a36Sopenharmony_ci	ssize_t count;
4662306a36Sopenharmony_ci	char *buf;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (WARN_ON_ONCE(!ops->show))
4962306a36Sopenharmony_ci		return -EINVAL;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* acquire buffer and ensure that it's >= PAGE_SIZE and clear */
5262306a36Sopenharmony_ci	count = seq_get_buf(sf, &buf);
5362306a36Sopenharmony_ci	if (count < PAGE_SIZE) {
5462306a36Sopenharmony_ci		seq_commit(sf, -1);
5562306a36Sopenharmony_ci		return 0;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	memset(buf, 0, PAGE_SIZE);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	count = ops->show(kobj, of->kn->priv, buf);
6062306a36Sopenharmony_ci	if (count < 0)
6162306a36Sopenharmony_ci		return count;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/*
6462306a36Sopenharmony_ci	 * The code works fine with PAGE_SIZE return but it's likely to
6562306a36Sopenharmony_ci	 * indicate truncated result or overflow in normal use cases.
6662306a36Sopenharmony_ci	 */
6762306a36Sopenharmony_ci	if (count >= (ssize_t)PAGE_SIZE) {
6862306a36Sopenharmony_ci		printk("fill_read_buffer: %pS returned bad count\n",
6962306a36Sopenharmony_ci				ops->show);
7062306a36Sopenharmony_ci		/* Try to struggle along */
7162306a36Sopenharmony_ci		count = PAGE_SIZE - 1;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci	seq_commit(sf, count);
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf,
7862306a36Sopenharmony_ci				 size_t count, loff_t pos)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct bin_attribute *battr = of->kn->priv;
8162306a36Sopenharmony_ci	struct kobject *kobj = of->kn->parent->priv;
8262306a36Sopenharmony_ci	loff_t size = file_inode(of->file)->i_size;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (!count)
8562306a36Sopenharmony_ci		return 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (size) {
8862306a36Sopenharmony_ci		if (pos >= size)
8962306a36Sopenharmony_ci			return 0;
9062306a36Sopenharmony_ci		if (pos + count > size)
9162306a36Sopenharmony_ci			count = size - pos;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (!battr->read)
9562306a36Sopenharmony_ci		return -EIO;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return battr->read(of->file, kobj, battr, buf, pos, count);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* kernfs read callback for regular sysfs files with pre-alloc */
10162306a36Sopenharmony_cistatic ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
10262306a36Sopenharmony_ci			     size_t count, loff_t pos)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
10562306a36Sopenharmony_ci	struct kobject *kobj = of->kn->parent->priv;
10662306a36Sopenharmony_ci	ssize_t len;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/*
10962306a36Sopenharmony_ci	 * If buf != of->prealloc_buf, we don't know how
11062306a36Sopenharmony_ci	 * large it is, so cannot safely pass it to ->show
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	if (WARN_ON_ONCE(buf != of->prealloc_buf))
11362306a36Sopenharmony_ci		return 0;
11462306a36Sopenharmony_ci	len = ops->show(kobj, of->kn->priv, buf);
11562306a36Sopenharmony_ci	if (len < 0)
11662306a36Sopenharmony_ci		return len;
11762306a36Sopenharmony_ci	if (pos) {
11862306a36Sopenharmony_ci		if (len <= pos)
11962306a36Sopenharmony_ci			return 0;
12062306a36Sopenharmony_ci		len -= pos;
12162306a36Sopenharmony_ci		memmove(buf, buf + pos, len);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	return min_t(ssize_t, count, len);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* kernfs write callback for regular sysfs files */
12762306a36Sopenharmony_cistatic ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
12862306a36Sopenharmony_ci			      size_t count, loff_t pos)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
13162306a36Sopenharmony_ci	struct kobject *kobj = of->kn->parent->priv;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (!count)
13462306a36Sopenharmony_ci		return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return ops->store(kobj, of->kn->priv, buf, count);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* kernfs write callback for bin sysfs files */
14062306a36Sopenharmony_cistatic ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf,
14162306a36Sopenharmony_ci				  size_t count, loff_t pos)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct bin_attribute *battr = of->kn->priv;
14462306a36Sopenharmony_ci	struct kobject *kobj = of->kn->parent->priv;
14562306a36Sopenharmony_ci	loff_t size = file_inode(of->file)->i_size;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (size) {
14862306a36Sopenharmony_ci		if (size <= pos)
14962306a36Sopenharmony_ci			return -EFBIG;
15062306a36Sopenharmony_ci		count = min_t(ssize_t, count, size - pos);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci	if (!count)
15362306a36Sopenharmony_ci		return 0;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (!battr->write)
15662306a36Sopenharmony_ci		return -EIO;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return battr->write(of->file, kobj, battr, buf, pos, count);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int sysfs_kf_bin_mmap(struct kernfs_open_file *of,
16262306a36Sopenharmony_ci			     struct vm_area_struct *vma)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct bin_attribute *battr = of->kn->priv;
16562306a36Sopenharmony_ci	struct kobject *kobj = of->kn->parent->priv;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return battr->mmap(of->file, kobj, battr, vma);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int sysfs_kf_bin_open(struct kernfs_open_file *of)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct bin_attribute *battr = of->kn->priv;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (battr->f_mapping)
17562306a36Sopenharmony_ci		of->file->f_mapping = battr->f_mapping();
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_civoid sysfs_notify(struct kobject *kobj, const char *dir, const char *attr)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct kernfs_node *kn = kobj->sd, *tmp;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (kn && dir)
18562306a36Sopenharmony_ci		kn = kernfs_find_and_get(kn, dir);
18662306a36Sopenharmony_ci	else
18762306a36Sopenharmony_ci		kernfs_get(kn);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (kn && attr) {
19062306a36Sopenharmony_ci		tmp = kernfs_find_and_get(kn, attr);
19162306a36Sopenharmony_ci		kernfs_put(kn);
19262306a36Sopenharmony_ci		kn = tmp;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (kn) {
19662306a36Sopenharmony_ci		kernfs_notify(kn);
19762306a36Sopenharmony_ci		kernfs_put(kn);
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_notify);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_file_kfops_empty = {
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_file_kfops_ro = {
20662306a36Sopenharmony_ci	.seq_show	= sysfs_kf_seq_show,
20762306a36Sopenharmony_ci};
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_file_kfops_wo = {
21062306a36Sopenharmony_ci	.write		= sysfs_kf_write,
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_file_kfops_rw = {
21462306a36Sopenharmony_ci	.seq_show	= sysfs_kf_seq_show,
21562306a36Sopenharmony_ci	.write		= sysfs_kf_write,
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_prealloc_kfops_ro = {
21962306a36Sopenharmony_ci	.read		= sysfs_kf_read,
22062306a36Sopenharmony_ci	.prealloc	= true,
22162306a36Sopenharmony_ci};
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_prealloc_kfops_wo = {
22462306a36Sopenharmony_ci	.write		= sysfs_kf_write,
22562306a36Sopenharmony_ci	.prealloc	= true,
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_prealloc_kfops_rw = {
22962306a36Sopenharmony_ci	.read		= sysfs_kf_read,
23062306a36Sopenharmony_ci	.write		= sysfs_kf_write,
23162306a36Sopenharmony_ci	.prealloc	= true,
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_bin_kfops_ro = {
23562306a36Sopenharmony_ci	.read		= sysfs_kf_bin_read,
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_bin_kfops_wo = {
23962306a36Sopenharmony_ci	.write		= sysfs_kf_bin_write,
24062306a36Sopenharmony_ci};
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_bin_kfops_rw = {
24362306a36Sopenharmony_ci	.read		= sysfs_kf_bin_read,
24462306a36Sopenharmony_ci	.write		= sysfs_kf_bin_write,
24562306a36Sopenharmony_ci};
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic const struct kernfs_ops sysfs_bin_kfops_mmap = {
24862306a36Sopenharmony_ci	.read		= sysfs_kf_bin_read,
24962306a36Sopenharmony_ci	.write		= sysfs_kf_bin_write,
25062306a36Sopenharmony_ci	.mmap		= sysfs_kf_bin_mmap,
25162306a36Sopenharmony_ci	.open		= sysfs_kf_bin_open,
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ciint sysfs_add_file_mode_ns(struct kernfs_node *parent,
25562306a36Sopenharmony_ci		const struct attribute *attr, umode_t mode, kuid_t uid,
25662306a36Sopenharmony_ci		kgid_t gid, const void *ns)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct kobject *kobj = parent->priv;
25962306a36Sopenharmony_ci	const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
26062306a36Sopenharmony_ci	struct lock_class_key *key = NULL;
26162306a36Sopenharmony_ci	const struct kernfs_ops *ops = NULL;
26262306a36Sopenharmony_ci	struct kernfs_node *kn;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* every kobject with an attribute needs a ktype assigned */
26562306a36Sopenharmony_ci	if (WARN(!sysfs_ops, KERN_ERR
26662306a36Sopenharmony_ci			"missing sysfs attribute operations for kobject: %s\n",
26762306a36Sopenharmony_ci			kobject_name(kobj)))
26862306a36Sopenharmony_ci		return -EINVAL;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (mode & SYSFS_PREALLOC) {
27162306a36Sopenharmony_ci		if (sysfs_ops->show && sysfs_ops->store)
27262306a36Sopenharmony_ci			ops = &sysfs_prealloc_kfops_rw;
27362306a36Sopenharmony_ci		else if (sysfs_ops->show)
27462306a36Sopenharmony_ci			ops = &sysfs_prealloc_kfops_ro;
27562306a36Sopenharmony_ci		else if (sysfs_ops->store)
27662306a36Sopenharmony_ci			ops = &sysfs_prealloc_kfops_wo;
27762306a36Sopenharmony_ci	} else {
27862306a36Sopenharmony_ci		if (sysfs_ops->show && sysfs_ops->store)
27962306a36Sopenharmony_ci			ops = &sysfs_file_kfops_rw;
28062306a36Sopenharmony_ci		else if (sysfs_ops->show)
28162306a36Sopenharmony_ci			ops = &sysfs_file_kfops_ro;
28262306a36Sopenharmony_ci		else if (sysfs_ops->store)
28362306a36Sopenharmony_ci			ops = &sysfs_file_kfops_wo;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!ops)
28762306a36Sopenharmony_ci		ops = &sysfs_file_kfops_empty;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC
29062306a36Sopenharmony_ci	if (!attr->ignore_lockdep)
29162306a36Sopenharmony_ci		key = attr->key ?: (struct lock_class_key *)&attr->skey;
29262306a36Sopenharmony_ci#endif
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
29562306a36Sopenharmony_ci				  PAGE_SIZE, ops, (void *)attr, ns, key);
29662306a36Sopenharmony_ci	if (IS_ERR(kn)) {
29762306a36Sopenharmony_ci		if (PTR_ERR(kn) == -EEXIST)
29862306a36Sopenharmony_ci			sysfs_warn_dup(parent, attr->name);
29962306a36Sopenharmony_ci		return PTR_ERR(kn);
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	return 0;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ciint sysfs_add_bin_file_mode_ns(struct kernfs_node *parent,
30562306a36Sopenharmony_ci		const struct bin_attribute *battr, umode_t mode,
30662306a36Sopenharmony_ci		kuid_t uid, kgid_t gid, const void *ns)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	const struct attribute *attr = &battr->attr;
30962306a36Sopenharmony_ci	struct lock_class_key *key = NULL;
31062306a36Sopenharmony_ci	const struct kernfs_ops *ops;
31162306a36Sopenharmony_ci	struct kernfs_node *kn;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (battr->mmap)
31462306a36Sopenharmony_ci		ops = &sysfs_bin_kfops_mmap;
31562306a36Sopenharmony_ci	else if (battr->read && battr->write)
31662306a36Sopenharmony_ci		ops = &sysfs_bin_kfops_rw;
31762306a36Sopenharmony_ci	else if (battr->read)
31862306a36Sopenharmony_ci		ops = &sysfs_bin_kfops_ro;
31962306a36Sopenharmony_ci	else if (battr->write)
32062306a36Sopenharmony_ci		ops = &sysfs_bin_kfops_wo;
32162306a36Sopenharmony_ci	else
32262306a36Sopenharmony_ci		ops = &sysfs_file_kfops_empty;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC
32562306a36Sopenharmony_ci	if (!attr->ignore_lockdep)
32662306a36Sopenharmony_ci		key = attr->key ?: (struct lock_class_key *)&attr->skey;
32762306a36Sopenharmony_ci#endif
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
33062306a36Sopenharmony_ci				  battr->size, ops, (void *)attr, ns, key);
33162306a36Sopenharmony_ci	if (IS_ERR(kn)) {
33262306a36Sopenharmony_ci		if (PTR_ERR(kn) == -EEXIST)
33362306a36Sopenharmony_ci			sysfs_warn_dup(parent, attr->name);
33462306a36Sopenharmony_ci		return PTR_ERR(kn);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	return 0;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/**
34062306a36Sopenharmony_ci * sysfs_create_file_ns - create an attribute file for an object with custom ns
34162306a36Sopenharmony_ci * @kobj: object we're creating for
34262306a36Sopenharmony_ci * @attr: attribute descriptor
34362306a36Sopenharmony_ci * @ns: namespace the new file should belong to
34462306a36Sopenharmony_ci */
34562306a36Sopenharmony_ciint sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
34662306a36Sopenharmony_ci			 const void *ns)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	kuid_t uid;
34962306a36Sopenharmony_ci	kgid_t gid;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (WARN_ON(!kobj || !kobj->sd || !attr))
35262306a36Sopenharmony_ci		return -EINVAL;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	kobject_get_ownership(kobj, &uid, &gid);
35562306a36Sopenharmony_ci	return sysfs_add_file_mode_ns(kobj->sd, attr, attr->mode, uid, gid, ns);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_file_ns);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ciint sysfs_create_files(struct kobject *kobj, const struct attribute * const *ptr)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	int err = 0;
36262306a36Sopenharmony_ci	int i;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	for (i = 0; ptr[i] && !err; i++)
36562306a36Sopenharmony_ci		err = sysfs_create_file(kobj, ptr[i]);
36662306a36Sopenharmony_ci	if (err)
36762306a36Sopenharmony_ci		while (--i >= 0)
36862306a36Sopenharmony_ci			sysfs_remove_file(kobj, ptr[i]);
36962306a36Sopenharmony_ci	return err;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_files);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci/**
37462306a36Sopenharmony_ci * sysfs_add_file_to_group - add an attribute file to a pre-existing group.
37562306a36Sopenharmony_ci * @kobj: object we're acting for.
37662306a36Sopenharmony_ci * @attr: attribute descriptor.
37762306a36Sopenharmony_ci * @group: group name.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_ciint sysfs_add_file_to_group(struct kobject *kobj,
38062306a36Sopenharmony_ci		const struct attribute *attr, const char *group)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct kernfs_node *parent;
38362306a36Sopenharmony_ci	kuid_t uid;
38462306a36Sopenharmony_ci	kgid_t gid;
38562306a36Sopenharmony_ci	int error;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (group) {
38862306a36Sopenharmony_ci		parent = kernfs_find_and_get(kobj->sd, group);
38962306a36Sopenharmony_ci	} else {
39062306a36Sopenharmony_ci		parent = kobj->sd;
39162306a36Sopenharmony_ci		kernfs_get(parent);
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (!parent)
39562306a36Sopenharmony_ci		return -ENOENT;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	kobject_get_ownership(kobj, &uid, &gid);
39862306a36Sopenharmony_ci	error = sysfs_add_file_mode_ns(parent, attr, attr->mode, uid, gid,
39962306a36Sopenharmony_ci				       NULL);
40062306a36Sopenharmony_ci	kernfs_put(parent);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return error;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci/**
40762306a36Sopenharmony_ci * sysfs_chmod_file - update the modified mode value on an object attribute.
40862306a36Sopenharmony_ci * @kobj: object we're acting for.
40962306a36Sopenharmony_ci * @attr: attribute descriptor.
41062306a36Sopenharmony_ci * @mode: file permissions.
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci */
41362306a36Sopenharmony_ciint sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr,
41462306a36Sopenharmony_ci		     umode_t mode)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct kernfs_node *kn;
41762306a36Sopenharmony_ci	struct iattr newattrs;
41862306a36Sopenharmony_ci	int rc;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	kn = kernfs_find_and_get(kobj->sd, attr->name);
42162306a36Sopenharmony_ci	if (!kn)
42262306a36Sopenharmony_ci		return -ENOENT;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	newattrs.ia_mode = (mode & S_IALLUGO) | (kn->mode & ~S_IALLUGO);
42562306a36Sopenharmony_ci	newattrs.ia_valid = ATTR_MODE;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	rc = kernfs_setattr(kn, &newattrs);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	kernfs_put(kn);
43062306a36Sopenharmony_ci	return rc;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_chmod_file);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/**
43562306a36Sopenharmony_ci * sysfs_break_active_protection - break "active" protection
43662306a36Sopenharmony_ci * @kobj: The kernel object @attr is associated with.
43762306a36Sopenharmony_ci * @attr: The attribute to break the "active" protection for.
43862306a36Sopenharmony_ci *
43962306a36Sopenharmony_ci * With sysfs, just like kernfs, deletion of an attribute is postponed until
44062306a36Sopenharmony_ci * all active .show() and .store() callbacks have finished unless this function
44162306a36Sopenharmony_ci * is called. Hence this function is useful in methods that implement self
44262306a36Sopenharmony_ci * deletion.
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistruct kernfs_node *sysfs_break_active_protection(struct kobject *kobj,
44562306a36Sopenharmony_ci						  const struct attribute *attr)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct kernfs_node *kn;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	kobject_get(kobj);
45062306a36Sopenharmony_ci	kn = kernfs_find_and_get(kobj->sd, attr->name);
45162306a36Sopenharmony_ci	if (kn)
45262306a36Sopenharmony_ci		kernfs_break_active_protection(kn);
45362306a36Sopenharmony_ci	return kn;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_break_active_protection);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci/**
45862306a36Sopenharmony_ci * sysfs_unbreak_active_protection - restore "active" protection
45962306a36Sopenharmony_ci * @kn: Pointer returned by sysfs_break_active_protection().
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci * Undo the effects of sysfs_break_active_protection(). Since this function
46262306a36Sopenharmony_ci * calls kernfs_put() on the kernfs node that corresponds to the 'attr'
46362306a36Sopenharmony_ci * argument passed to sysfs_break_active_protection() that attribute may have
46462306a36Sopenharmony_ci * been removed between the sysfs_break_active_protection() and
46562306a36Sopenharmony_ci * sysfs_unbreak_active_protection() calls, it is not safe to access @kn after
46662306a36Sopenharmony_ci * this function has returned.
46762306a36Sopenharmony_ci */
46862306a36Sopenharmony_civoid sysfs_unbreak_active_protection(struct kernfs_node *kn)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct kobject *kobj = kn->parent->priv;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	kernfs_unbreak_active_protection(kn);
47362306a36Sopenharmony_ci	kernfs_put(kn);
47462306a36Sopenharmony_ci	kobject_put(kobj);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_unbreak_active_protection);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/**
47962306a36Sopenharmony_ci * sysfs_remove_file_ns - remove an object attribute with a custom ns tag
48062306a36Sopenharmony_ci * @kobj: object we're acting for
48162306a36Sopenharmony_ci * @attr: attribute descriptor
48262306a36Sopenharmony_ci * @ns: namespace tag of the file to remove
48362306a36Sopenharmony_ci *
48462306a36Sopenharmony_ci * Hash the attribute name and namespace tag and kill the victim.
48562306a36Sopenharmony_ci */
48662306a36Sopenharmony_civoid sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
48762306a36Sopenharmony_ci			  const void *ns)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct kernfs_node *parent = kobj->sd;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	kernfs_remove_by_name_ns(parent, attr->name, ns);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_file_ns);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/**
49662306a36Sopenharmony_ci * sysfs_remove_file_self - remove an object attribute from its own method
49762306a36Sopenharmony_ci * @kobj: object we're acting for
49862306a36Sopenharmony_ci * @attr: attribute descriptor
49962306a36Sopenharmony_ci *
50062306a36Sopenharmony_ci * See kernfs_remove_self() for details.
50162306a36Sopenharmony_ci */
50262306a36Sopenharmony_cibool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct kernfs_node *parent = kobj->sd;
50562306a36Sopenharmony_ci	struct kernfs_node *kn;
50662306a36Sopenharmony_ci	bool ret;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	kn = kernfs_find_and_get(parent, attr->name);
50962306a36Sopenharmony_ci	if (WARN_ON_ONCE(!kn))
51062306a36Sopenharmony_ci		return false;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	ret = kernfs_remove_self(kn);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	kernfs_put(kn);
51562306a36Sopenharmony_ci	return ret;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_file_self);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_civoid sysfs_remove_files(struct kobject *kobj, const struct attribute * const *ptr)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	int i;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	for (i = 0; ptr[i]; i++)
52462306a36Sopenharmony_ci		sysfs_remove_file(kobj, ptr[i]);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_files);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/**
52962306a36Sopenharmony_ci * sysfs_remove_file_from_group - remove an attribute file from a group.
53062306a36Sopenharmony_ci * @kobj: object we're acting for.
53162306a36Sopenharmony_ci * @attr: attribute descriptor.
53262306a36Sopenharmony_ci * @group: group name.
53362306a36Sopenharmony_ci */
53462306a36Sopenharmony_civoid sysfs_remove_file_from_group(struct kobject *kobj,
53562306a36Sopenharmony_ci		const struct attribute *attr, const char *group)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct kernfs_node *parent;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (group) {
54062306a36Sopenharmony_ci		parent = kernfs_find_and_get(kobj->sd, group);
54162306a36Sopenharmony_ci	} else {
54262306a36Sopenharmony_ci		parent = kobj->sd;
54362306a36Sopenharmony_ci		kernfs_get(parent);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (parent) {
54762306a36Sopenharmony_ci		kernfs_remove_by_name(parent, attr->name);
54862306a36Sopenharmony_ci		kernfs_put(parent);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/**
55462306a36Sopenharmony_ci *	sysfs_create_bin_file - create binary file for object.
55562306a36Sopenharmony_ci *	@kobj:	object.
55662306a36Sopenharmony_ci *	@attr:	attribute descriptor.
55762306a36Sopenharmony_ci */
55862306a36Sopenharmony_ciint sysfs_create_bin_file(struct kobject *kobj,
55962306a36Sopenharmony_ci			  const struct bin_attribute *attr)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	kuid_t uid;
56262306a36Sopenharmony_ci	kgid_t gid;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (WARN_ON(!kobj || !kobj->sd || !attr))
56562306a36Sopenharmony_ci		return -EINVAL;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	kobject_get_ownership(kobj, &uid, &gid);
56862306a36Sopenharmony_ci	return sysfs_add_bin_file_mode_ns(kobj->sd, attr, attr->attr.mode, uid,
56962306a36Sopenharmony_ci					   gid, NULL);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_bin_file);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/**
57462306a36Sopenharmony_ci *	sysfs_remove_bin_file - remove binary file for object.
57562306a36Sopenharmony_ci *	@kobj:	object.
57662306a36Sopenharmony_ci *	@attr:	attribute descriptor.
57762306a36Sopenharmony_ci */
57862306a36Sopenharmony_civoid sysfs_remove_bin_file(struct kobject *kobj,
57962306a36Sopenharmony_ci			   const struct bin_attribute *attr)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	kernfs_remove_by_name(kobj->sd, attr->attr.name);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int internal_change_owner(struct kernfs_node *kn, kuid_t kuid,
58662306a36Sopenharmony_ci				 kgid_t kgid)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct iattr newattrs = {
58962306a36Sopenharmony_ci		.ia_valid = ATTR_UID | ATTR_GID,
59062306a36Sopenharmony_ci		.ia_uid = kuid,
59162306a36Sopenharmony_ci		.ia_gid = kgid,
59262306a36Sopenharmony_ci	};
59362306a36Sopenharmony_ci	return kernfs_setattr(kn, &newattrs);
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci/**
59762306a36Sopenharmony_ci *	sysfs_link_change_owner - change owner of a sysfs file.
59862306a36Sopenharmony_ci *	@kobj:	object of the kernfs_node the symlink is located in.
59962306a36Sopenharmony_ci *	@targ:	object of the kernfs_node the symlink points to.
60062306a36Sopenharmony_ci *	@name:	name of the link.
60162306a36Sopenharmony_ci *	@kuid:	new owner's kuid
60262306a36Sopenharmony_ci *	@kgid:	new owner's kgid
60362306a36Sopenharmony_ci *
60462306a36Sopenharmony_ci * This function looks up the sysfs symlink entry @name under @kobj and changes
60562306a36Sopenharmony_ci * the ownership to @kuid/@kgid. The symlink is looked up in the namespace of
60662306a36Sopenharmony_ci * @targ.
60762306a36Sopenharmony_ci *
60862306a36Sopenharmony_ci * Returns 0 on success or error code on failure.
60962306a36Sopenharmony_ci */
61062306a36Sopenharmony_ciint sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ,
61162306a36Sopenharmony_ci			    const char *name, kuid_t kuid, kgid_t kgid)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct kernfs_node *kn = NULL;
61462306a36Sopenharmony_ci	int error;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (!name || !kobj->state_in_sysfs || !targ->state_in_sysfs)
61762306a36Sopenharmony_ci		return -EINVAL;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	error = -ENOENT;
62062306a36Sopenharmony_ci	kn = kernfs_find_and_get_ns(kobj->sd, name, targ->sd->ns);
62162306a36Sopenharmony_ci	if (!kn)
62262306a36Sopenharmony_ci		goto out;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	error = -EINVAL;
62562306a36Sopenharmony_ci	if (kernfs_type(kn) != KERNFS_LINK)
62662306a36Sopenharmony_ci		goto out;
62762306a36Sopenharmony_ci	if (kn->symlink.target_kn->priv != targ)
62862306a36Sopenharmony_ci		goto out;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	error = internal_change_owner(kn, kuid, kgid);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ciout:
63362306a36Sopenharmony_ci	kernfs_put(kn);
63462306a36Sopenharmony_ci	return error;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci/**
63862306a36Sopenharmony_ci *	sysfs_file_change_owner - change owner of a sysfs file.
63962306a36Sopenharmony_ci *	@kobj:	object.
64062306a36Sopenharmony_ci *	@name:	name of the file to change.
64162306a36Sopenharmony_ci *	@kuid:	new owner's kuid
64262306a36Sopenharmony_ci *	@kgid:	new owner's kgid
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * This function looks up the sysfs entry @name under @kobj and changes the
64562306a36Sopenharmony_ci * ownership to @kuid/@kgid.
64662306a36Sopenharmony_ci *
64762306a36Sopenharmony_ci * Returns 0 on success or error code on failure.
64862306a36Sopenharmony_ci */
64962306a36Sopenharmony_ciint sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid,
65062306a36Sopenharmony_ci			    kgid_t kgid)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct kernfs_node *kn;
65362306a36Sopenharmony_ci	int error;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (!name)
65662306a36Sopenharmony_ci		return -EINVAL;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (!kobj->state_in_sysfs)
65962306a36Sopenharmony_ci		return -EINVAL;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	kn = kernfs_find_and_get(kobj->sd, name);
66262306a36Sopenharmony_ci	if (!kn)
66362306a36Sopenharmony_ci		return -ENOENT;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	error = internal_change_owner(kn, kuid, kgid);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	kernfs_put(kn);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return error;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_file_change_owner);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci/**
67462306a36Sopenharmony_ci *	sysfs_change_owner - change owner of the given object.
67562306a36Sopenharmony_ci *	@kobj:	object.
67662306a36Sopenharmony_ci *	@kuid:	new owner's kuid
67762306a36Sopenharmony_ci *	@kgid:	new owner's kgid
67862306a36Sopenharmony_ci *
67962306a36Sopenharmony_ci * Change the owner of the default directory, files, groups, and attributes of
68062306a36Sopenharmony_ci * @kobj to @kuid/@kgid. Note that sysfs_change_owner mirrors how the sysfs
68162306a36Sopenharmony_ci * entries for a kobject are added by driver core. In summary,
68262306a36Sopenharmony_ci * sysfs_change_owner() takes care of the default directory entry for @kobj,
68362306a36Sopenharmony_ci * the default attributes associated with the ktype of @kobj and the default
68462306a36Sopenharmony_ci * attributes associated with the ktype of @kobj.
68562306a36Sopenharmony_ci * Additional properties not added by driver core have to be changed by the
68662306a36Sopenharmony_ci * driver or subsystem which created them. This is similar to how
68762306a36Sopenharmony_ci * driver/subsystem specific entries are removed.
68862306a36Sopenharmony_ci *
68962306a36Sopenharmony_ci * Returns 0 on success or error code on failure.
69062306a36Sopenharmony_ci */
69162306a36Sopenharmony_ciint sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	int error;
69462306a36Sopenharmony_ci	const struct kobj_type *ktype;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (!kobj->state_in_sysfs)
69762306a36Sopenharmony_ci		return -EINVAL;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* Change the owner of the kobject itself. */
70062306a36Sopenharmony_ci	error = internal_change_owner(kobj->sd, kuid, kgid);
70162306a36Sopenharmony_ci	if (error)
70262306a36Sopenharmony_ci		return error;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	ktype = get_ktype(kobj);
70562306a36Sopenharmony_ci	if (ktype) {
70662306a36Sopenharmony_ci		/*
70762306a36Sopenharmony_ci		 * Change owner of the default groups associated with the
70862306a36Sopenharmony_ci		 * ktype of @kobj.
70962306a36Sopenharmony_ci		 */
71062306a36Sopenharmony_ci		error = sysfs_groups_change_owner(kobj, ktype->default_groups,
71162306a36Sopenharmony_ci						  kuid, kgid);
71262306a36Sopenharmony_ci		if (error)
71362306a36Sopenharmony_ci			return error;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return 0;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_change_owner);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/**
72162306a36Sopenharmony_ci *	sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer.
72262306a36Sopenharmony_ci *	@buf:	start of PAGE_SIZE buffer.
72362306a36Sopenharmony_ci *	@fmt:	format
72462306a36Sopenharmony_ci *	@...:	optional arguments to @format
72562306a36Sopenharmony_ci *
72662306a36Sopenharmony_ci *
72762306a36Sopenharmony_ci * Returns number of characters written to @buf.
72862306a36Sopenharmony_ci */
72962306a36Sopenharmony_ciint sysfs_emit(char *buf, const char *fmt, ...)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	va_list args;
73262306a36Sopenharmony_ci	int len;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (WARN(!buf || offset_in_page(buf),
73562306a36Sopenharmony_ci		 "invalid sysfs_emit: buf:%p\n", buf))
73662306a36Sopenharmony_ci		return 0;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	va_start(args, fmt);
73962306a36Sopenharmony_ci	len = vscnprintf(buf, PAGE_SIZE, fmt, args);
74062306a36Sopenharmony_ci	va_end(args);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return len;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_emit);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci/**
74762306a36Sopenharmony_ci *	sysfs_emit_at - scnprintf equivalent, aware of PAGE_SIZE buffer.
74862306a36Sopenharmony_ci *	@buf:	start of PAGE_SIZE buffer.
74962306a36Sopenharmony_ci *	@at:	offset in @buf to start write in bytes
75062306a36Sopenharmony_ci *		@at must be >= 0 && < PAGE_SIZE
75162306a36Sopenharmony_ci *	@fmt:	format
75262306a36Sopenharmony_ci *	@...:	optional arguments to @fmt
75362306a36Sopenharmony_ci *
75462306a36Sopenharmony_ci *
75562306a36Sopenharmony_ci * Returns number of characters written starting at &@buf[@at].
75662306a36Sopenharmony_ci */
75762306a36Sopenharmony_ciint sysfs_emit_at(char *buf, int at, const char *fmt, ...)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	va_list args;
76062306a36Sopenharmony_ci	int len;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE,
76362306a36Sopenharmony_ci		 "invalid sysfs_emit_at: buf:%p at:%d\n", buf, at))
76462306a36Sopenharmony_ci		return 0;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	va_start(args, fmt);
76762306a36Sopenharmony_ci	len = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args);
76862306a36Sopenharmony_ci	va_end(args);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	return len;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_emit_at);
773