162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/sysfs/dir.c - sysfs core and dir operation implementation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2001-3 Patrick Mochel
662306a36Sopenharmony_ci * Copyright (c) 2007 SUSE Linux Products GmbH
762306a36Sopenharmony_ci * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Please see Documentation/filesystems/sysfs.rst for more information.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt)	"sysfs: " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/fs.h>
1562306a36Sopenharmony_ci#include <linux/kobject.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include "sysfs.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciDEFINE_SPINLOCK(sysfs_symlink_target_lock);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_civoid sysfs_warn_dup(struct kernfs_node *parent, const char *name)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	char *buf;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	buf = kzalloc(PATH_MAX, GFP_KERNEL);
2662306a36Sopenharmony_ci	if (buf)
2762306a36Sopenharmony_ci		kernfs_path(parent, buf, PATH_MAX);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name);
3062306a36Sopenharmony_ci	dump_stack();
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	kfree(buf);
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/**
3662306a36Sopenharmony_ci * sysfs_create_dir_ns - create a directory for an object with a namespace tag
3762306a36Sopenharmony_ci * @kobj: object we're creating directory for
3862306a36Sopenharmony_ci * @ns: the namespace tag to use
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ciint sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct kernfs_node *parent, *kn;
4362306a36Sopenharmony_ci	kuid_t uid;
4462306a36Sopenharmony_ci	kgid_t gid;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (WARN_ON(!kobj))
4762306a36Sopenharmony_ci		return -EINVAL;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (kobj->parent)
5062306a36Sopenharmony_ci		parent = kobj->parent->sd;
5162306a36Sopenharmony_ci	else
5262306a36Sopenharmony_ci		parent = sysfs_root_kn;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (!parent)
5562306a36Sopenharmony_ci		return -ENOENT;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	kobject_get_ownership(kobj, &uid, &gid);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 0755, uid, gid,
6062306a36Sopenharmony_ci				  kobj, ns);
6162306a36Sopenharmony_ci	if (IS_ERR(kn)) {
6262306a36Sopenharmony_ci		if (PTR_ERR(kn) == -EEXIST)
6362306a36Sopenharmony_ci			sysfs_warn_dup(parent, kobject_name(kobj));
6462306a36Sopenharmony_ci		return PTR_ERR(kn);
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	kobj->sd = kn;
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/**
7262306a36Sopenharmony_ci *	sysfs_remove_dir - remove an object's directory.
7362306a36Sopenharmony_ci *	@kobj:	object.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci *	The only thing special about this is that we remove any files in
7662306a36Sopenharmony_ci *	the directory before we remove the directory, and we've inlined
7762306a36Sopenharmony_ci *	what used to be sysfs_rmdir() below, instead of calling separately.
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_civoid sysfs_remove_dir(struct kobject *kobj)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct kernfs_node *kn = kobj->sd;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/*
8462306a36Sopenharmony_ci	 * In general, kboject owner is responsible for ensuring removal
8562306a36Sopenharmony_ci	 * doesn't race with other operations and sysfs doesn't provide any
8662306a36Sopenharmony_ci	 * protection; however, when @kobj is used as a symlink target, the
8762306a36Sopenharmony_ci	 * symlinking entity usually doesn't own @kobj and thus has no
8862306a36Sopenharmony_ci	 * control over removal.  @kobj->sd may be removed anytime
8962306a36Sopenharmony_ci	 * and symlink code may end up dereferencing an already freed node.
9062306a36Sopenharmony_ci	 *
9162306a36Sopenharmony_ci	 * sysfs_symlink_target_lock synchronizes @kobj->sd
9262306a36Sopenharmony_ci	 * disassociation against symlink operations so that symlink code
9362306a36Sopenharmony_ci	 * can safely dereference @kobj->sd.
9462306a36Sopenharmony_ci	 */
9562306a36Sopenharmony_ci	spin_lock(&sysfs_symlink_target_lock);
9662306a36Sopenharmony_ci	kobj->sd = NULL;
9762306a36Sopenharmony_ci	spin_unlock(&sysfs_symlink_target_lock);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (kn) {
10062306a36Sopenharmony_ci		WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR);
10162306a36Sopenharmony_ci		kernfs_remove(kn);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciint sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
10662306a36Sopenharmony_ci			const void *new_ns)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct kernfs_node *parent;
10962306a36Sopenharmony_ci	int ret;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	parent = kernfs_get_parent(kobj->sd);
11262306a36Sopenharmony_ci	ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
11362306a36Sopenharmony_ci	kernfs_put(parent);
11462306a36Sopenharmony_ci	return ret;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciint sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
11862306a36Sopenharmony_ci		      const void *new_ns)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct kernfs_node *kn = kobj->sd;
12162306a36Sopenharmony_ci	struct kernfs_node *new_parent;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	new_parent = new_parent_kobj && new_parent_kobj->sd ?
12462306a36Sopenharmony_ci		new_parent_kobj->sd : sysfs_root_kn;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return kernfs_rename_ns(kn, new_parent, kn->name, new_ns);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/**
13062306a36Sopenharmony_ci * sysfs_create_mount_point - create an always empty directory
13162306a36Sopenharmony_ci * @parent_kobj:  kobject that will contain this always empty directory
13262306a36Sopenharmony_ci * @name: The name of the always empty directory to add
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_ciint sysfs_create_mount_point(struct kobject *parent_kobj, const char *name)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct kernfs_node *kn, *parent = parent_kobj->sd;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	kn = kernfs_create_empty_dir(parent, name);
13962306a36Sopenharmony_ci	if (IS_ERR(kn)) {
14062306a36Sopenharmony_ci		if (PTR_ERR(kn) == -EEXIST)
14162306a36Sopenharmony_ci			sysfs_warn_dup(parent, name);
14262306a36Sopenharmony_ci		return PTR_ERR(kn);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_mount_point);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/**
15062306a36Sopenharmony_ci *	sysfs_remove_mount_point - remove an always empty directory.
15162306a36Sopenharmony_ci *	@parent_kobj: kobject that will contain this always empty directory
15262306a36Sopenharmony_ci *	@name: The name of the always empty directory to remove
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_civoid sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct kernfs_node *parent = parent_kobj->sd;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	kernfs_remove_by_name_ns(parent, name, NULL);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_mount_point);
162