Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * fs/sysfs/dir.c - sysfs core and dir operation implementation
  4 *
  5 * Copyright (c) 2001-3 Patrick Mochel
  6 * Copyright (c) 2007 SUSE Linux Products GmbH
  7 * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
  8 *
  9 * Please see Documentation/filesystems/sysfs.rst for more information.
 10 */
 11
 12#define pr_fmt(fmt)	"sysfs: " fmt
 13
 14#include <linux/fs.h>
 15#include <linux/kobject.h>
 16#include <linux/slab.h>
 17#include "sysfs.h"
 18
 19DEFINE_SPINLOCK(sysfs_symlink_target_lock);
 20
 21void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
 22{
 23	char *buf;
 24
 25	buf = kzalloc(PATH_MAX, GFP_KERNEL);
 26	if (buf)
 27		kernfs_path(parent, buf, PATH_MAX);
 28
 29	pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name);
 30	dump_stack();
 31
 32	kfree(buf);
 33}
 34
 35/**
 36 * sysfs_create_dir_ns - create a directory for an object with a namespace tag
 37 * @kobj: object we're creating directory for
 38 * @ns: the namespace tag to use
 39 */
 40int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
 41{
 42	struct kernfs_node *parent, *kn;
 43	kuid_t uid;
 44	kgid_t gid;
 45
 46	if (WARN_ON(!kobj))
 47		return -EINVAL;
 48
 49	if (kobj->parent)
 50		parent = kobj->parent->sd;
 51	else
 52		parent = sysfs_root_kn;
 53
 54	if (!parent)
 55		return -ENOENT;
 56
 57	kobject_get_ownership(kobj, &uid, &gid);
 58
 59	kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 0755, uid, gid,
 
 60				  kobj, ns);
 61	if (IS_ERR(kn)) {
 62		if (PTR_ERR(kn) == -EEXIST)
 63			sysfs_warn_dup(parent, kobject_name(kobj));
 64		return PTR_ERR(kn);
 65	}
 66
 67	kobj->sd = kn;
 68	return 0;
 69}
 70
 71/**
 72 *	sysfs_remove_dir - remove an object's directory.
 73 *	@kobj:	object.
 74 *
 75 *	The only thing special about this is that we remove any files in
 76 *	the directory before we remove the directory, and we've inlined
 77 *	what used to be sysfs_rmdir() below, instead of calling separately.
 78 */
 79void sysfs_remove_dir(struct kobject *kobj)
 80{
 81	struct kernfs_node *kn = kobj->sd;
 82
 83	/*
 84	 * In general, kobject owner is responsible for ensuring removal
 85	 * doesn't race with other operations and sysfs doesn't provide any
 86	 * protection; however, when @kobj is used as a symlink target, the
 87	 * symlinking entity usually doesn't own @kobj and thus has no
 88	 * control over removal.  @kobj->sd may be removed anytime
 89	 * and symlink code may end up dereferencing an already freed node.
 90	 *
 91	 * sysfs_symlink_target_lock synchronizes @kobj->sd
 92	 * disassociation against symlink operations so that symlink code
 93	 * can safely dereference @kobj->sd.
 94	 */
 95	spin_lock(&sysfs_symlink_target_lock);
 96	kobj->sd = NULL;
 97	spin_unlock(&sysfs_symlink_target_lock);
 98
 99	if (kn) {
100		WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR);
101		kernfs_remove(kn);
102	}
103}
104
105int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
106			const void *new_ns)
107{
108	struct kernfs_node *parent;
109	int ret;
110
111	parent = kernfs_get_parent(kobj->sd);
112	ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
113	kernfs_put(parent);
114	return ret;
115}
116
117int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
118		      const void *new_ns)
119{
120	struct kernfs_node *kn = kobj->sd;
121	struct kernfs_node *new_parent;
122
123	new_parent = new_parent_kobj && new_parent_kobj->sd ?
124		new_parent_kobj->sd : sysfs_root_kn;
125
126	return kernfs_rename_ns(kn, new_parent, kn->name, new_ns);
127}
128
129/**
130 * sysfs_create_mount_point - create an always empty directory
131 * @parent_kobj:  kobject that will contain this always empty directory
132 * @name: The name of the always empty directory to add
133 */
134int sysfs_create_mount_point(struct kobject *parent_kobj, const char *name)
135{
136	struct kernfs_node *kn, *parent = parent_kobj->sd;
137
138	kn = kernfs_create_empty_dir(parent, name);
139	if (IS_ERR(kn)) {
140		if (PTR_ERR(kn) == -EEXIST)
141			sysfs_warn_dup(parent, name);
142		return PTR_ERR(kn);
143	}
144
145	return 0;
146}
147EXPORT_SYMBOL_GPL(sysfs_create_mount_point);
148
149/**
150 *	sysfs_remove_mount_point - remove an always empty directory.
151 *	@parent_kobj: kobject that will contain this always empty directory
152 *	@name: The name of the always empty directory to remove
153 *
154 */
155void sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name)
156{
157	struct kernfs_node *parent = parent_kobj->sd;
158
159	kernfs_remove_by_name_ns(parent, name, NULL);
160}
161EXPORT_SYMBOL_GPL(sysfs_remove_mount_point);
v5.4
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * fs/sysfs/dir.c - sysfs core and dir operation implementation
  4 *
  5 * Copyright (c) 2001-3 Patrick Mochel
  6 * Copyright (c) 2007 SUSE Linux Products GmbH
  7 * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
  8 *
  9 * Please see Documentation/filesystems/sysfs.txt for more information.
 10 */
 11
 12#define pr_fmt(fmt)	"sysfs: " fmt
 13
 14#include <linux/fs.h>
 15#include <linux/kobject.h>
 16#include <linux/slab.h>
 17#include "sysfs.h"
 18
 19DEFINE_SPINLOCK(sysfs_symlink_target_lock);
 20
 21void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
 22{
 23	char *buf;
 24
 25	buf = kzalloc(PATH_MAX, GFP_KERNEL);
 26	if (buf)
 27		kernfs_path(parent, buf, PATH_MAX);
 28
 29	pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name);
 30	dump_stack();
 31
 32	kfree(buf);
 33}
 34
 35/**
 36 * sysfs_create_dir_ns - create a directory for an object with a namespace tag
 37 * @kobj: object we're creating directory for
 38 * @ns: the namespace tag to use
 39 */
 40int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
 41{
 42	struct kernfs_node *parent, *kn;
 43	kuid_t uid;
 44	kgid_t gid;
 45
 46	if (WARN_ON(!kobj))
 47		return -EINVAL;
 48
 49	if (kobj->parent)
 50		parent = kobj->parent->sd;
 51	else
 52		parent = sysfs_root_kn;
 53
 54	if (!parent)
 55		return -ENOENT;
 56
 57	kobject_get_ownership(kobj, &uid, &gid);
 58
 59	kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
 60				  S_IRWXU | S_IRUGO | S_IXUGO, uid, gid,
 61				  kobj, ns);
 62	if (IS_ERR(kn)) {
 63		if (PTR_ERR(kn) == -EEXIST)
 64			sysfs_warn_dup(parent, kobject_name(kobj));
 65		return PTR_ERR(kn);
 66	}
 67
 68	kobj->sd = kn;
 69	return 0;
 70}
 71
 72/**
 73 *	sysfs_remove_dir - remove an object's directory.
 74 *	@kobj:	object.
 75 *
 76 *	The only thing special about this is that we remove any files in
 77 *	the directory before we remove the directory, and we've inlined
 78 *	what used to be sysfs_rmdir() below, instead of calling separately.
 79 */
 80void sysfs_remove_dir(struct kobject *kobj)
 81{
 82	struct kernfs_node *kn = kobj->sd;
 83
 84	/*
 85	 * In general, kboject owner is responsible for ensuring removal
 86	 * doesn't race with other operations and sysfs doesn't provide any
 87	 * protection; however, when @kobj is used as a symlink target, the
 88	 * symlinking entity usually doesn't own @kobj and thus has no
 89	 * control over removal.  @kobj->sd may be removed anytime
 90	 * and symlink code may end up dereferencing an already freed node.
 91	 *
 92	 * sysfs_symlink_target_lock synchronizes @kobj->sd
 93	 * disassociation against symlink operations so that symlink code
 94	 * can safely dereference @kobj->sd.
 95	 */
 96	spin_lock(&sysfs_symlink_target_lock);
 97	kobj->sd = NULL;
 98	spin_unlock(&sysfs_symlink_target_lock);
 99
100	if (kn) {
101		WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR);
102		kernfs_remove(kn);
103	}
104}
105
106int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
107			const void *new_ns)
108{
109	struct kernfs_node *parent;
110	int ret;
111
112	parent = kernfs_get_parent(kobj->sd);
113	ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
114	kernfs_put(parent);
115	return ret;
116}
117
118int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
119		      const void *new_ns)
120{
121	struct kernfs_node *kn = kobj->sd;
122	struct kernfs_node *new_parent;
123
124	new_parent = new_parent_kobj && new_parent_kobj->sd ?
125		new_parent_kobj->sd : sysfs_root_kn;
126
127	return kernfs_rename_ns(kn, new_parent, kn->name, new_ns);
128}
129
130/**
131 * sysfs_create_mount_point - create an always empty directory
132 * @parent_kobj:  kobject that will contain this always empty directory
133 * @name: The name of the always empty directory to add
134 */
135int sysfs_create_mount_point(struct kobject *parent_kobj, const char *name)
136{
137	struct kernfs_node *kn, *parent = parent_kobj->sd;
138
139	kn = kernfs_create_empty_dir(parent, name);
140	if (IS_ERR(kn)) {
141		if (PTR_ERR(kn) == -EEXIST)
142			sysfs_warn_dup(parent, name);
143		return PTR_ERR(kn);
144	}
145
146	return 0;
147}
148EXPORT_SYMBOL_GPL(sysfs_create_mount_point);
149
150/**
151 *	sysfs_remove_mount_point - remove an always empty directory.
152 *	@parent_kobj: kobject that will contain this always empty directory
153 *	@name: The name of the always empty directory to remove
154 *
155 */
156void sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name)
157{
158	struct kernfs_node *parent = parent_kobj->sd;
159
160	kernfs_remove_by_name_ns(parent, name, NULL);
161}
162EXPORT_SYMBOL_GPL(sysfs_remove_mount_point);