File Index
Files and directories
Sign in
FILE

group.c

File preview with syntax highlighting.

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