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);
v4.17
  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
 44	BUG_ON(!kobj);
 
 45
 46	if (kobj->parent)
 47		parent = kobj->parent->sd;
 48	else
 49		parent = sysfs_root_kn;
 50
 51	if (!parent)
 52		return -ENOENT;
 53
 54	kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
 55				  S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns);
 
 
 56	if (IS_ERR(kn)) {
 57		if (PTR_ERR(kn) == -EEXIST)
 58			sysfs_warn_dup(parent, kobject_name(kobj));
 59		return PTR_ERR(kn);
 60	}
 61
 62	kobj->sd = kn;
 63	return 0;
 64}
 65
 66/**
 67 *	sysfs_remove_dir - remove an object's directory.
 68 *	@kobj:	object.
 69 *
 70 *	The only thing special about this is that we remove any files in
 71 *	the directory before we remove the directory, and we've inlined
 72 *	what used to be sysfs_rmdir() below, instead of calling separately.
 73 */
 74void sysfs_remove_dir(struct kobject *kobj)
 75{
 76	struct kernfs_node *kn = kobj->sd;
 77
 78	/*
 79	 * In general, kboject owner is responsible for ensuring removal
 80	 * doesn't race with other operations and sysfs doesn't provide any
 81	 * protection; however, when @kobj is used as a symlink target, the
 82	 * symlinking entity usually doesn't own @kobj and thus has no
 83	 * control over removal.  @kobj->sd may be removed anytime
 84	 * and symlink code may end up dereferencing an already freed node.
 85	 *
 86	 * sysfs_symlink_target_lock synchronizes @kobj->sd
 87	 * disassociation against symlink operations so that symlink code
 88	 * can safely dereference @kobj->sd.
 89	 */
 90	spin_lock(&sysfs_symlink_target_lock);
 91	kobj->sd = NULL;
 92	spin_unlock(&sysfs_symlink_target_lock);
 93
 94	if (kn) {
 95		WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR);
 96		kernfs_remove(kn);
 97	}
 98}
 99
100int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
101			const void *new_ns)
102{
103	struct kernfs_node *parent;
104	int ret;
105
106	parent = kernfs_get_parent(kobj->sd);
107	ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
108	kernfs_put(parent);
109	return ret;
110}
111
112int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
113		      const void *new_ns)
114{
115	struct kernfs_node *kn = kobj->sd;
116	struct kernfs_node *new_parent;
117
118	new_parent = new_parent_kobj && new_parent_kobj->sd ?
119		new_parent_kobj->sd : sysfs_root_kn;
120
121	return kernfs_rename_ns(kn, new_parent, kn->name, new_ns);
122}
123
124/**
125 * sysfs_create_mount_point - create an always empty directory
126 * @parent_kobj:  kobject that will contain this always empty directory
127 * @name: The name of the always empty directory to add
128 */
129int sysfs_create_mount_point(struct kobject *parent_kobj, const char *name)
130{
131	struct kernfs_node *kn, *parent = parent_kobj->sd;
132
133	kn = kernfs_create_empty_dir(parent, name);
134	if (IS_ERR(kn)) {
135		if (PTR_ERR(kn) == -EEXIST)
136			sysfs_warn_dup(parent, name);
137		return PTR_ERR(kn);
138	}
139
140	return 0;
141}
142EXPORT_SYMBOL_GPL(sysfs_create_mount_point);
143
144/**
145 *	sysfs_remove_mount_point - remove an always empty directory.
146 *	@parent_kobj: kobject that will contain this always empty directory
147 *	@name: The name of the always empty directory to remove
148 *
149 */
150void sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name)
151{
152	struct kernfs_node *parent = parent_kobj->sd;
153
154	kernfs_remove_by_name_ns(parent, name, NULL);
155}
156EXPORT_SYMBOL_GPL(sysfs_remove_mount_point);