brintos

brintos / linux-shallow public Read only

0
0
Text · 20.1 KiB · d1995e2 Raw
815 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * fs/sysfs/file.c - sysfs regular (text) file implementation4 *5 * Copyright (c) 2001-3 Patrick Mochel6 * Copyright (c) 2007 SUSE Linux Products GmbH7 * 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 function24 * must be called while holding an active reference.25 */26static 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 hairy37 * details like buffering and seeking.  The following function pipes38 * sysfs_ops->show() result through seq_file.39 */40static 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(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(sf, &buf);53	if (count < PAGE_SIZE) {54		seq_commit(sf, -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 to65	 * 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(sf, count);74	return 0;75}76 77static 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(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 */101static 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(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 how110	 * large it is, so cannot safely pass it to ->show111	 */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 */127static 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(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 */140static 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(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 161static 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 170static 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	else179		return generic_file_llseek(of->file, offset, whence);180}181 182static 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 192void 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, dir);198	else199		kernfs_get(kn);200 201	if (kn && attr) {202		tmp = kernfs_find_and_get(kn, attr);203		kernfs_put(kn);204		kn = tmp;205	}206 207	if (kn) {208		kernfs_notify(kn);209		kernfs_put(kn);210	}211}212EXPORT_SYMBOL_GPL(sysfs_notify);213 214static const struct kernfs_ops sysfs_file_kfops_empty = {215};216 217static const struct kernfs_ops sysfs_file_kfops_ro = {218	.seq_show	= sysfs_kf_seq_show,219};220 221static const struct kernfs_ops sysfs_file_kfops_wo = {222	.write		= sysfs_kf_write,223};224 225static const struct kernfs_ops sysfs_file_kfops_rw = {226	.seq_show	= sysfs_kf_seq_show,227	.write		= sysfs_kf_write,228};229 230static const struct kernfs_ops sysfs_prealloc_kfops_ro = {231	.read		= sysfs_kf_read,232	.prealloc	= true,233};234 235static const struct kernfs_ops sysfs_prealloc_kfops_wo = {236	.write		= sysfs_kf_write,237	.prealloc	= true,238};239 240static const struct kernfs_ops sysfs_prealloc_kfops_rw = {241	.read		= sysfs_kf_read,242	.write		= sysfs_kf_write,243	.prealloc	= true,244};245 246static const struct kernfs_ops sysfs_bin_kfops_ro = {247	.read		= sysfs_kf_bin_read,248};249 250static const struct kernfs_ops sysfs_bin_kfops_wo = {251	.write		= sysfs_kf_bin_write,252};253 254static const struct kernfs_ops sysfs_bin_kfops_rw = {255	.read		= sysfs_kf_bin_read,256	.write		= sysfs_kf_bin_write,257};258 259static 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 267int 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_ERR279			"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_ALLOC303	if (!attr->ignore_lockdep)304		key = attr->key ?: (struct lock_class_key *)&attr->skey;305#endif306 307	kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,308				  PAGE_SIZE, ops, (void *)attr, ns, key);309	if (IS_ERR(kn)) {310		if (PTR_ERR(kn) == -EEXIST)311			sysfs_warn_dup(parent, attr->name);312		return PTR_ERR(kn);313	}314	return 0;315}316 317int 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	else335		ops = &sysfs_file_kfops_empty;336 337#ifdef CONFIG_DEBUG_LOCK_ALLOC338	if (!attr->ignore_lockdep)339		key = attr->key ?: (struct lock_class_key *)&attr->skey;340#endif341 342	kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,343				  battr->size, ops, (void *)attr, ns, key);344	if (IS_ERR(kn)) {345		if (PTR_ERR(kn) == -EEXIST)346			sysfs_warn_dup(parent, attr->name);347		return PTR_ERR(kn);348	}349	return 0;350}351 352/**353 * sysfs_create_file_ns - create an attribute file for an object with custom ns354 * @kobj: object we're creating for355 * @attr: attribute descriptor356 * @ns: namespace the new file should belong to357 */358int 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, &gid);368	return sysfs_add_file_mode_ns(kobj->sd, attr, attr->mode, uid, gid, ns);369}370EXPORT_SYMBOL_GPL(sysfs_create_file_ns);371 372int 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, ptr[i]);379	if (err)380		while (--i >= 0)381			sysfs_remove_file(kobj, ptr[i]);382	return err;383}384EXPORT_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 */392int 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(kobj->sd, group);402	} else {403		parent = kobj->sd;404		kernfs_get(parent);405	}406 407	if (!parent)408		return -ENOENT;409 410	kobject_get_ownership(kobj, &uid, &gid);411	error = sysfs_add_file_mode_ns(parent, attr, attr->mode, uid, gid,412				       NULL);413	kernfs_put(parent);414 415	return error;416}417EXPORT_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 */426int 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(kobj->sd, 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, &newattrs);441 442	kernfs_put(kn);443	return rc;444}445EXPORT_SYMBOL_GPL(sysfs_chmod_file);446 447/**448 * sysfs_break_active_protection - break "active" protection449 * @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 until453 * all active .show() and .store() callbacks have finished unless this function454 * is called. Hence this function is useful in methods that implement self455 * deletion.456 */457struct 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(kobj->sd, attr->name);464	if (kn)465		kernfs_break_active_protection(kn);466	else467		kobject_put(kobj);468	return kn;469}470EXPORT_SYMBOL_GPL(sysfs_break_active_protection);471 472/**473 * sysfs_unbreak_active_protection - restore "active" protection474 * @kn: Pointer returned by sysfs_break_active_protection().475 *476 * Undo the effects of sysfs_break_active_protection(). Since this function477 * calls kernfs_put() on the kernfs node that corresponds to the 'attr'478 * argument passed to sysfs_break_active_protection() that attribute may have479 * been removed between the sysfs_break_active_protection() and480 * sysfs_unbreak_active_protection() calls, it is not safe to access @kn after481 * this function has returned.482 */483void sysfs_unbreak_active_protection(struct kernfs_node *kn)484{485	struct kobject *kobj = kn->parent->priv;486 487	kernfs_unbreak_active_protection(kn);488	kernfs_put(kn);489	kobject_put(kobj);490}491EXPORT_SYMBOL_GPL(sysfs_unbreak_active_protection);492 493/**494 * sysfs_remove_file_ns - remove an object attribute with a custom ns tag495 * @kobj: object we're acting for496 * @attr: attribute descriptor497 * @ns: namespace tag of the file to remove498 *499 * Hash the attribute name and namespace tag and kill the victim.500 */501void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,502			  const void *ns)503{504	struct kernfs_node *parent = kobj->sd;505 506	kernfs_remove_by_name_ns(parent, attr->name, ns);507}508EXPORT_SYMBOL_GPL(sysfs_remove_file_ns);509 510/**511 * sysfs_remove_file_self - remove an object attribute from its own method512 * @kobj: object we're acting for513 * @attr: attribute descriptor514 *515 * See kernfs_remove_self() for details.516 */517bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr)518{519	struct kernfs_node *parent = kobj->sd;520	struct kernfs_node *kn;521	bool ret;522 523	kn = kernfs_find_and_get(parent, attr->name);524	if (WARN_ON_ONCE(!kn))525		return false;526 527	ret = kernfs_remove_self(kn);528 529	kernfs_put(kn);530	return ret;531}532EXPORT_SYMBOL_GPL(sysfs_remove_file_self);533 534void sysfs_remove_files(struct kobject *kobj, const struct attribute * const *ptr)535{536	int i;537 538	for (i = 0; ptr[i]; i++)539		sysfs_remove_file(kobj, ptr[i]);540}541EXPORT_SYMBOL_GPL(sysfs_remove_files);542 543/**544 * sysfs_remove_file_from_group - remove an attribute file from a group.545 * @kobj: object we're acting for.546 * @attr: attribute descriptor.547 * @group: group name.548 */549void sysfs_remove_file_from_group(struct kobject *kobj,550		const struct attribute *attr, const char *group)551{552	struct kernfs_node *parent;553 554	if (group) {555		parent = kernfs_find_and_get(kobj->sd, group);556	} else {557		parent = kobj->sd;558		kernfs_get(parent);559	}560 561	if (parent) {562		kernfs_remove_by_name(parent, attr->name);563		kernfs_put(parent);564	}565}566EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);567 568/**569 *	sysfs_create_bin_file - create binary file for object.570 *	@kobj:	object.571 *	@attr:	attribute descriptor.572 */573int sysfs_create_bin_file(struct kobject *kobj,574			  const struct bin_attribute *attr)575{576	kuid_t uid;577	kgid_t gid;578 579	if (WARN_ON(!kobj || !kobj->sd || !attr))580		return -EINVAL;581 582	kobject_get_ownership(kobj, &uid, &gid);583	return sysfs_add_bin_file_mode_ns(kobj->sd, attr, attr->attr.mode, uid,584					   gid, NULL);585}586EXPORT_SYMBOL_GPL(sysfs_create_bin_file);587 588/**589 *	sysfs_remove_bin_file - remove binary file for object.590 *	@kobj:	object.591 *	@attr:	attribute descriptor.592 */593void sysfs_remove_bin_file(struct kobject *kobj,594			   const struct bin_attribute *attr)595{596	kernfs_remove_by_name(kobj->sd, attr->attr.name);597}598EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);599 600static int internal_change_owner(struct kernfs_node *kn, kuid_t kuid,601				 kgid_t kgid)602{603	struct iattr newattrs = {604		.ia_valid = ATTR_UID | ATTR_GID,605		.ia_uid = kuid,606		.ia_gid = kgid,607	};608	return kernfs_setattr(kn, &newattrs);609}610 611/**612 *	sysfs_link_change_owner - change owner of a sysfs file.613 *	@kobj:	object of the kernfs_node the symlink is located in.614 *	@targ:	object of the kernfs_node the symlink points to.615 *	@name:	name of the link.616 *	@kuid:	new owner's kuid617 *	@kgid:	new owner's kgid618 *619 * This function looks up the sysfs symlink entry @name under @kobj and changes620 * the ownership to @kuid/@kgid. The symlink is looked up in the namespace of621 * @targ.622 *623 * Returns 0 on success or error code on failure.624 */625int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ,626			    const char *name, kuid_t kuid, kgid_t kgid)627{628	struct kernfs_node *kn = NULL;629	int error;630 631	if (!name || !kobj->state_in_sysfs || !targ->state_in_sysfs)632		return -EINVAL;633 634	error = -ENOENT;635	kn = kernfs_find_and_get_ns(kobj->sd, name, targ->sd->ns);636	if (!kn)637		goto out;638 639	error = -EINVAL;640	if (kernfs_type(kn) != KERNFS_LINK)641		goto out;642	if (kn->symlink.target_kn->priv != targ)643		goto out;644 645	error = internal_change_owner(kn, kuid, kgid);646 647out:648	kernfs_put(kn);649	return error;650}651 652/**653 *	sysfs_file_change_owner - change owner of a sysfs file.654 *	@kobj:	object.655 *	@name:	name of the file to change.656 *	@kuid:	new owner's kuid657 *	@kgid:	new owner's kgid658 *659 * This function looks up the sysfs entry @name under @kobj and changes the660 * ownership to @kuid/@kgid.661 *662 * Returns 0 on success or error code on failure.663 */664int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid,665			    kgid_t kgid)666{667	struct kernfs_node *kn;668	int error;669 670	if (!name)671		return -EINVAL;672 673	if (!kobj->state_in_sysfs)674		return -EINVAL;675 676	kn = kernfs_find_and_get(kobj->sd, name);677	if (!kn)678		return -ENOENT;679 680	error = internal_change_owner(kn, kuid, kgid);681 682	kernfs_put(kn);683 684	return error;685}686EXPORT_SYMBOL_GPL(sysfs_file_change_owner);687 688/**689 *	sysfs_change_owner - change owner of the given object.690 *	@kobj:	object.691 *	@kuid:	new owner's kuid692 *	@kgid:	new owner's kgid693 *694 * Change the owner of the default directory, files, groups, and attributes of695 * @kobj to @kuid/@kgid. Note that sysfs_change_owner mirrors how the sysfs696 * entries for a kobject are added by driver core. In summary,697 * sysfs_change_owner() takes care of the default directory entry for @kobj,698 * the default attributes associated with the ktype of @kobj and the default699 * attributes associated with the ktype of @kobj.700 * Additional properties not added by driver core have to be changed by the701 * driver or subsystem which created them. This is similar to how702 * driver/subsystem specific entries are removed.703 *704 * Returns 0 on success or error code on failure.705 */706int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid)707{708	int error;709	const struct kobj_type *ktype;710 711	if (!kobj->state_in_sysfs)712		return -EINVAL;713 714	/* Change the owner of the kobject itself. */715	error = internal_change_owner(kobj->sd, kuid, kgid);716	if (error)717		return error;718 719	ktype = get_ktype(kobj);720	if (ktype) {721		/*722		 * Change owner of the default groups associated with the723		 * ktype of @kobj.724		 */725		error = sysfs_groups_change_owner(kobj, ktype->default_groups,726						  kuid, kgid);727		if (error)728			return error;729	}730 731	return 0;732}733EXPORT_SYMBOL_GPL(sysfs_change_owner);734 735/**736 *	sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer.737 *	@buf:	start of PAGE_SIZE buffer.738 *	@fmt:	format739 *	@...:	optional arguments to @format740 *741 *742 * Returns number of characters written to @buf.743 */744int sysfs_emit(char *buf, const char *fmt, ...)745{746	va_list args;747	int len;748 749	if (WARN(!buf || offset_in_page(buf),750		 "invalid sysfs_emit: buf:%p\n", buf))751		return 0;752 753	va_start(args, fmt);754	len = vscnprintf(buf, PAGE_SIZE, fmt, args);755	va_end(args);756 757	return len;758}759EXPORT_SYMBOL_GPL(sysfs_emit);760 761/**762 *	sysfs_emit_at - scnprintf equivalent, aware of PAGE_SIZE buffer.763 *	@buf:	start of PAGE_SIZE buffer.764 *	@at:	offset in @buf to start write in bytes765 *		@at must be >= 0 && < PAGE_SIZE766 *	@fmt:	format767 *	@...:	optional arguments to @fmt768 *769 *770 * Returns number of characters written starting at &@buf[@at].771 */772int sysfs_emit_at(char *buf, int at, const char *fmt, ...)773{774	va_list args;775	int len;776 777	if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE,778		 "invalid sysfs_emit_at: buf:%p at:%d\n", buf, at))779		return 0;780 781	va_start(args, fmt);782	len = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args);783	va_end(args);784 785	return len;786}787EXPORT_SYMBOL_GPL(sysfs_emit_at);788 789/**790 *	sysfs_bin_attr_simple_read - read callback to simply copy from memory.791 *	@file:	attribute file which is being read.792 *	@kobj:	object to which the attribute belongs.793 *	@attr:	attribute descriptor.794 *	@buf:	destination buffer.795 *	@off:	offset in bytes from which to read.796 *	@count:	maximum number of bytes to read.797 *798 * Simple ->read() callback for bin_attributes backed by a buffer in memory.799 * The @private and @size members in struct bin_attribute must be set to the800 * buffer's location and size before the bin_attribute is created in sysfs.801 *802 * Bounds check for @off and @count is done in sysfs_kf_bin_read().803 * Negative value check for @off is done in vfs_setpos() and default_llseek().804 *805 * Returns number of bytes written to @buf.806 */807ssize_t sysfs_bin_attr_simple_read(struct file *file, struct kobject *kobj,808				   struct bin_attribute *attr, char *buf,809				   loff_t off, size_t count)810{811	memcpy(buf, attr->private + off, count);812	return count;813}814EXPORT_SYMBOL_GPL(sysfs_bin_attr_simple_read);815