brintos

brintos / linux-shallow public Read only

0
0
Text · 15.5 KiB · 6f8c21c Raw
611 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * fs/sysfs/group.c - Operations for adding/removing multiple files at once.4 *5 * Copyright (c) 2003 Patrick Mochel6 * Copyright (c) 2003 Open Source Development Lab7 * Copyright (c) 2013 Greg Kroah-Hartman8 * Copyright (c) 2013 The Linux Foundation9 */10 11#include <linux/kobject.h>12#include <linux/module.h>13#include <linux/dcache.h>14#include <linux/namei.h>15#include <linux/err.h>16#include <linux/fs.h>17#include "sysfs.h"18 19 20static void remove_files(struct kernfs_node *parent,21			 const struct attribute_group *grp)22{23	struct attribute *const *attr;24	struct bin_attribute *const *bin_attr;25 26	if (grp->attrs)27		for (attr = grp->attrs; *attr; attr++)28			kernfs_remove_by_name(parent, (*attr)->name);29	if (grp->bin_attrs)30		for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)31			kernfs_remove_by_name(parent, (*bin_attr)->attr.name);32}33 34static umode_t __first_visible(const struct attribute_group *grp, struct kobject *kobj)35{36	if (grp->attrs && grp->attrs[0] && grp->is_visible)37		return grp->is_visible(kobj, grp->attrs[0], 0);38 39	if (grp->bin_attrs && grp->bin_attrs[0] && grp->is_bin_visible)40		return grp->is_bin_visible(kobj, grp->bin_attrs[0], 0);41 42	return 0;43}44 45static HWJS_SUSPENDS int create_files(struct kernfs_node *parent, struct kobject *kobj,46			kuid_t uid, kgid_t gid,47			const struct attribute_group *grp, int update)48{49	struct attribute *const *attr;50	struct bin_attribute *const *bin_attr;51	int error = 0, i;52 53	if (grp->attrs) {54		for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {55			umode_t mode = (*attr)->mode;56 57			/*58			 * In update mode, we're changing the permissions or59			 * visibility.  Do this by first removing then60			 * re-adding (if required) the file.61			 */62			if (update)63				kernfs_remove_by_name(parent, (*attr)->name);64			if (grp->is_visible) {65				mode = grp->is_visible(kobj, *attr, i);66				mode &= ~SYSFS_GROUP_INVISIBLE;67				if (!mode)68					continue;69			}70 71			WARN(mode & ~(SYSFS_PREALLOC | 0664),72			     "Attribute %s: Invalid permissions 0%o\n",73			     (*attr)->name, mode);74 75			mode &= SYSFS_PREALLOC | 0664;76			error = sysfs_add_file_mode_ns(parent, *attr, mode, uid,77						       gid, NULL);78			if (unlikely(error))79				break;80		}81		if (error) {82			remove_files(parent, grp);83			goto exit;84		}85	}86 87	if (grp->bin_attrs) {88		for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {89			umode_t mode = (*bin_attr)->attr.mode;90 91			if (update)92				kernfs_remove_by_name(parent,93						(*bin_attr)->attr.name);94			if (grp->is_bin_visible) {95				mode = grp->is_bin_visible(kobj, *bin_attr, i);96				mode &= ~SYSFS_GROUP_INVISIBLE;97				if (!mode)98					continue;99			}100 101			WARN(mode & ~(SYSFS_PREALLOC | 0664),102			     "Attribute %s: Invalid permissions 0%o\n",103			     (*bin_attr)->attr.name, mode);104 105			mode &= SYSFS_PREALLOC | 0664;106			error = sysfs_add_bin_file_mode_ns(parent, *bin_attr,107							   mode, uid, gid,108							   NULL);109			if (error)110				break;111		}112		if (error)113			remove_files(parent, grp);114	}115exit:116	return error;117}118 119 120static HWJS_SUSPENDS int internal_create_group(struct kobject *kobj, int update,121				 const struct attribute_group *grp)122{123	struct kernfs_node *kn;124	kuid_t uid;125	kgid_t gid;126	int error;127 128	if (WARN_ON(!kobj || (!update && !kobj->sd)))129		return -EINVAL;130 131	/* Updates may happen before the object has been instantiated */132	if (unlikely(update && !kobj->sd))133		return -EINVAL;134 135	if (!grp->attrs && !grp->bin_attrs) {136		pr_debug("sysfs: (bin_)attrs not set by subsystem for group: %s/%s, skipping\n",137			 kobj->name, grp->name ?: "");138		return 0;139	}140 141	kobject_get_ownership(kobj, &uid, &gid);142	if (grp->name) {143		umode_t mode = __first_visible(grp, kobj);144 145		if (mode & SYSFS_GROUP_INVISIBLE)146			mode = 0;147		else148			mode = S_IRWXU | S_IRUGO | S_IXUGO;149 150		if (update) {151			kn = kernfs_find_and_get(kobj->sd, grp->name);152			if (!kn) {153				pr_debug("attr grp %s/%s not created yet\n",154					 kobj->name, grp->name);155				/* may have been invisible prior to this update */156				update = 0;157			} else if (!mode) {158				sysfs_remove_group(kobj, grp);159				kernfs_put(kn);160				return 0;161			}162		}163 164		if (!update) {165			if (!mode)166				return 0;167			kn = kernfs_create_dir_ns(kobj->sd, grp->name, mode,168						  uid, gid, kobj, NULL);169			if (IS_ERR(kn)) {170				if (PTR_ERR(kn) == -EEXIST)171					sysfs_warn_dup(kobj->sd, grp->name);172				return PTR_ERR(kn);173			}174		}175	} else {176		kn = kobj->sd;177	}178 179	kernfs_get(kn);180	error = create_files(kn, kobj, uid, gid, grp, update);181	if (error) {182		if (grp->name)183			kernfs_remove(kn);184	}185	kernfs_put(kn);186 187	if (grp->name && update)188		kernfs_put(kn);189 190	return error;191}192 193/**194 * sysfs_create_group - given a directory kobject, create an attribute group195 * @kobj:	The kobject to create the group on196 * @grp:	The attribute group to create197 *198 * This function creates a group for the first time.  It will explicitly199 * warn and error if any of the attribute files being created already exist.200 *201 * Returns 0 on success or error code on failure.202 */203int sysfs_create_group(struct kobject *kobj,204		       const struct attribute_group *grp)205{206	return internal_create_group(kobj, 0, grp);207}208EXPORT_SYMBOL_GPL(sysfs_create_group);209 210static HWJS_SUSPENDS int internal_create_groups(struct kobject *kobj, int update,211				  const struct attribute_group **groups)212{213	int error = 0;214	int i;215 216	if (!groups)217		return 0;218 219	for (i = 0; groups[i]; i++) {220		error = internal_create_group(kobj, update, groups[i]);221		if (error) {222			while (--i >= 0)223				sysfs_remove_group(kobj, groups[i]);224			break;225		}226	}227	return error;228}229 230/**231 * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups232 * @kobj:	The kobject to create the group on233 * @groups:	The attribute groups to create, NULL terminated234 *235 * This function creates a bunch of attribute groups.  If an error occurs when236 * creating a group, all previously created groups will be removed, unwinding237 * everything back to the original state when this function was called.238 * It will explicitly warn and error if any of the attribute files being239 * created already exist.240 *241 * Returns 0 on success or error code from sysfs_create_group on failure.242 */243int sysfs_create_groups(struct kobject *kobj,244			const struct attribute_group **groups)245{246	return internal_create_groups(kobj, 0, groups);247}248EXPORT_SYMBOL_GPL(sysfs_create_groups);249 250/**251 * sysfs_update_groups - given a directory kobject, create a bunch of attribute groups252 * @kobj:	The kobject to update the group on253 * @groups:	The attribute groups to update, NULL terminated254 *255 * This function update a bunch of attribute groups.  If an error occurs when256 * updating a group, all previously updated groups will be removed together257 * with already existing (not updated) attributes.258 *259 * Returns 0 on success or error code from sysfs_update_group on failure.260 */261int sysfs_update_groups(struct kobject *kobj,262			const struct attribute_group **groups)263{264	return internal_create_groups(kobj, 1, groups);265}266EXPORT_SYMBOL_GPL(sysfs_update_groups);267 268/**269 * sysfs_update_group - given a directory kobject, update an attribute group270 * @kobj:	The kobject to update the group on271 * @grp:	The attribute group to update272 *273 * This function updates an attribute group.  Unlike274 * sysfs_create_group(), it will explicitly not warn or error if any275 * of the attribute files being created already exist.  Furthermore,276 * if the visibility of the files has changed through the is_visible()277 * callback, it will update the permissions and add or remove the278 * relevant files. Changing a group's name (subdirectory name under279 * kobj's directory in sysfs) is not allowed.280 *281 * The primary use for this function is to call it after making a change282 * that affects group visibility.283 *284 * Returns 0 on success or error code on failure.285 */286int sysfs_update_group(struct kobject *kobj,287		       const struct attribute_group *grp)288{289	return internal_create_group(kobj, 1, grp);290}291EXPORT_SYMBOL_GPL(sysfs_update_group);292 293/**294 * sysfs_remove_group: remove a group from a kobject295 * @kobj:	kobject to remove the group from296 * @grp:	group to remove297 *298 * This function removes a group of attributes from a kobject.  The attributes299 * previously have to have been created for this group, otherwise it will fail.300 */301void sysfs_remove_group(struct kobject *kobj,302			const struct attribute_group *grp)303{304	struct kernfs_node *parent = kobj->sd;305	struct kernfs_node *kn;306 307	if (grp->name) {308		kn = kernfs_find_and_get(parent, grp->name);309		if (!kn) {310			pr_debug("sysfs group '%s' not found for kobject '%s'\n",311				 grp->name, kobject_name(kobj));312			return;313		}314	} else {315		kn = parent;316		kernfs_get(kn);317	}318 319	remove_files(kn, grp);320	if (grp->name)321		kernfs_remove(kn);322 323	kernfs_put(kn);324}325EXPORT_SYMBOL_GPL(sysfs_remove_group);326 327/**328 * sysfs_remove_groups - remove a list of groups329 *330 * @kobj:	The kobject for the groups to be removed from331 * @groups:	NULL terminated list of groups to be removed332 *333 * If groups is not NULL, remove the specified groups from the kobject.334 */335void sysfs_remove_groups(struct kobject *kobj,336			 const struct attribute_group **groups)337{338	int i;339 340	if (!groups)341		return;342	for (i = 0; groups[i]; i++)343		sysfs_remove_group(kobj, groups[i]);344}345EXPORT_SYMBOL_GPL(sysfs_remove_groups);346 347/**348 * sysfs_merge_group - merge files into a pre-existing named attribute group.349 * @kobj:	The kobject containing the group.350 * @grp:	The files to create and the attribute group they belong to.351 *352 * This function returns an error if the group doesn't exist, the .name field is353 * NULL or any of the files already exist in that group, in which case none of354 * the new files are created.355 */356int sysfs_merge_group(struct kobject *kobj,357		       const struct attribute_group *grp)358{359	struct kernfs_node *parent;360	kuid_t uid;361	kgid_t gid;362	int error = 0;363	struct attribute *const *attr;364	int i;365 366	parent = kernfs_find_and_get(kobj->sd, grp->name);367	if (!parent)368		return -ENOENT;369 370	kobject_get_ownership(kobj, &uid, &gid);371 372	for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))373		error = sysfs_add_file_mode_ns(parent, *attr, (*attr)->mode,374					       uid, gid, NULL);375	if (error) {376		while (--i >= 0)377			kernfs_remove_by_name(parent, (*--attr)->name);378	}379	kernfs_put(parent);380 381	return error;382}383EXPORT_SYMBOL_GPL(sysfs_merge_group);384 385/**386 * sysfs_unmerge_group - remove files from a pre-existing named attribute group.387 * @kobj:	The kobject containing the group.388 * @grp:	The files to remove and the attribute group they belong to.389 */390void sysfs_unmerge_group(struct kobject *kobj,391		       const struct attribute_group *grp)392{393	struct kernfs_node *parent;394	struct attribute *const *attr;395 396	parent = kernfs_find_and_get(kobj->sd, grp->name);397	if (parent) {398		for (attr = grp->attrs; *attr; ++attr)399			kernfs_remove_by_name(parent, (*attr)->name);400		kernfs_put(parent);401	}402}403EXPORT_SYMBOL_GPL(sysfs_unmerge_group);404 405/**406 * sysfs_add_link_to_group - add a symlink to an attribute group.407 * @kobj:	The kobject containing the group.408 * @group_name:	The name of the group.409 * @target:	The target kobject of the symlink to create.410 * @link_name:	The name of the symlink to create.411 */412int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,413			    struct kobject *target, const char *link_name)414{415	struct kernfs_node *parent;416	int error = 0;417 418	parent = kernfs_find_and_get(kobj->sd, group_name);419	if (!parent)420		return -ENOENT;421 422	error = sysfs_create_link_sd(parent, target, link_name);423	kernfs_put(parent);424 425	return error;426}427EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);428 429/**430 * sysfs_remove_link_from_group - remove a symlink from an attribute group.431 * @kobj:	The kobject containing the group.432 * @group_name:	The name of the group.433 * @link_name:	The name of the symlink to remove.434 */435void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,436				  const char *link_name)437{438	struct kernfs_node *parent;439 440	parent = kernfs_find_and_get(kobj->sd, group_name);441	if (parent) {442		kernfs_remove_by_name(parent, link_name);443		kernfs_put(parent);444	}445}446EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);447 448/**449 * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing450 * to a group or an attribute451 * @kobj:		The kobject containing the group.452 * @target_kobj:	The target kobject.453 * @target_name:	The name of the target group or attribute.454 * @symlink_name:	The name of the symlink file (target_name will be455 *			considered if symlink_name is NULL).456 */457int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,458					 struct kobject *target_kobj,459					 const char *target_name,460					 const char *symlink_name)461{462	struct kernfs_node *target;463	struct kernfs_node *entry;464	struct kernfs_node *link;465 466	/*467	 * We don't own @target_kobj and it may be removed at any time.468	 * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()469	 * for details.470	 */471	spin_lock(&sysfs_symlink_target_lock);472	target = target_kobj->sd;473	if (target)474		kernfs_get(target);475	spin_unlock(&sysfs_symlink_target_lock);476	if (!target)477		return -ENOENT;478 479	entry = kernfs_find_and_get(target, target_name);480	if (!entry) {481		kernfs_put(target);482		return -ENOENT;483	}484 485	if (!symlink_name)486		symlink_name = target_name;487 488	link = kernfs_create_link(kobj->sd, symlink_name, entry);489	if (PTR_ERR(link) == -EEXIST)490		sysfs_warn_dup(kobj->sd, symlink_name);491 492	kernfs_put(entry);493	kernfs_put(target);494	return PTR_ERR_OR_ZERO(link);495}496EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj);497 498static int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn,499					  const struct attribute_group *grp,500					  struct iattr *newattrs)501{502	struct kernfs_node *kn;503	int error;504 505	if (grp->attrs) {506		struct attribute *const *attr;507 508		for (attr = grp->attrs; *attr; attr++) {509			kn = kernfs_find_and_get(grp_kn, (*attr)->name);510			if (!kn)511				return -ENOENT;512 513			error = kernfs_setattr(kn, newattrs);514			kernfs_put(kn);515			if (error)516				return error;517		}518	}519 520	if (grp->bin_attrs) {521		struct bin_attribute *const *bin_attr;522 523		for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {524			kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);525			if (!kn)526				return -ENOENT;527 528			error = kernfs_setattr(kn, newattrs);529			kernfs_put(kn);530			if (error)531				return error;532		}533	}534 535	return 0;536}537 538/**539 * sysfs_group_change_owner - change owner of an attribute group.540 * @kobj:	The kobject containing the group.541 * @grp:	The attribute group.542 * @kuid:	new owner's kuid543 * @kgid:	new owner's kgid544 *545 * Returns 0 on success or error code on failure.546 */547int sysfs_group_change_owner(struct kobject *kobj,548			     const struct attribute_group *grp, kuid_t kuid,549			     kgid_t kgid)550{551	struct kernfs_node *grp_kn;552	int error;553	struct iattr newattrs = {554		.ia_valid = ATTR_UID | ATTR_GID,555		.ia_uid = kuid,556		.ia_gid = kgid,557	};558 559	if (!kobj->state_in_sysfs)560		return -EINVAL;561 562	if (grp->name) {563		grp_kn = kernfs_find_and_get(kobj->sd, grp->name);564	} else {565		kernfs_get(kobj->sd);566		grp_kn = kobj->sd;567	}568	if (!grp_kn)569		return -ENOENT;570 571	error = kernfs_setattr(grp_kn, &newattrs);572	if (!error)573		error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs);574 575	kernfs_put(grp_kn);576 577	return error;578}579EXPORT_SYMBOL_GPL(sysfs_group_change_owner);580 581/**582 * sysfs_groups_change_owner - change owner of a set of attribute groups.583 * @kobj:	The kobject containing the groups.584 * @groups:	The attribute groups.585 * @kuid:	new owner's kuid586 * @kgid:	new owner's kgid587 *588 * Returns 0 on success or error code on failure.589 */590int sysfs_groups_change_owner(struct kobject *kobj,591			      const struct attribute_group **groups,592			      kuid_t kuid, kgid_t kgid)593{594	int error = 0, i;595 596	if (!kobj->state_in_sysfs)597		return -EINVAL;598 599	if (!groups)600		return 0;601 602	for (i = 0; groups[i]; i++) {603		error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);604		if (error)605			break;606	}607 608	return error;609}610EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);611