dir.c 4.1 KB
Newer Older
G
Greg Kroah-Hartman 已提交
1
// SPDX-License-Identifier: GPL-2.0
L
Linus Torvalds 已提交
2
/*
T
Tejun Heo 已提交
3 4 5 6 7 8 9
 * fs/sysfs/dir.c - sysfs core and dir operation implementation
 *
 * Copyright (c) 2001-3 Patrick Mochel
 * Copyright (c) 2007 SUSE Linux Products GmbH
 * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
 *
 * Please see Documentation/filesystems/sysfs.txt for more information.
L
Linus Torvalds 已提交
10 11
 */

12
#define pr_fmt(fmt)	"sysfs: " fmt
L
Linus Torvalds 已提交
13 14 15

#include <linux/fs.h>
#include <linux/kobject.h>
16
#include <linux/slab.h>
L
Linus Torvalds 已提交
17 18
#include "sysfs.h"

19
DEFINE_SPINLOCK(sysfs_symlink_target_lock);
L
Linus Torvalds 已提交
20

21
void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
22
{
23
	char *buf;
24

25 26
	buf = kzalloc(PATH_MAX, GFP_KERNEL);
	if (buf)
27
		kernfs_path(parent, buf, PATH_MAX);
28

29 30
	pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name);
	dump_stack();
31

32
	kfree(buf);
33 34
}

L
Linus Torvalds 已提交
35
/**
36 37 38
 * sysfs_create_dir_ns - create a directory for an object with a namespace tag
 * @kobj: object we're creating directory for
 * @ns: the namespace tag to use
L
Linus Torvalds 已提交
39
 */
40
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
L
Linus Torvalds 已提交
41
{
42
	struct kernfs_node *parent, *kn;
43 44
	kuid_t uid;
	kgid_t gid;
L
Linus Torvalds 已提交
45 46 47

	BUG_ON(!kobj);

48
	if (kobj->parent)
49
		parent = kobj->parent->sd;
L
Linus Torvalds 已提交
50
	else
51
		parent = sysfs_root_kn;
L
Linus Torvalds 已提交
52

53
	if (!parent)
54 55
		return -ENOENT;

56 57
	kobject_get_ownership(kobj, &uid, &gid);

58
	kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
59
				  S_IRWXU | S_IRUGO | S_IXUGO, uid, gid,
60
				  kobj, ns);
61 62 63 64
	if (IS_ERR(kn)) {
		if (PTR_ERR(kn) == -EEXIST)
			sysfs_warn_dup(parent, kobject_name(kobj));
		return PTR_ERR(kn);
65 66
	}

67
	kobj->sd = kn;
68
	return 0;
L
Linus Torvalds 已提交
69 70
}

71 72 73 74 75 76 77 78
/**
 *	sysfs_remove_dir - remove an object's directory.
 *	@kobj:	object.
 *
 *	The only thing special about this is that we remove any files in
 *	the directory before we remove the directory, and we've inlined
 *	what used to be sysfs_rmdir() below, instead of calling separately.
 */
79
void sysfs_remove_dir(struct kobject *kobj)
80
{
81
	struct kernfs_node *kn = kobj->sd;
82

83 84 85 86 87
	/*
	 * In general, kboject owner is responsible for ensuring removal
	 * doesn't race with other operations and sysfs doesn't provide any
	 * protection; however, when @kobj is used as a symlink target, the
	 * symlinking entity usually doesn't own @kobj and thus has no
88 89
	 * control over removal.  @kobj->sd may be removed anytime
	 * and symlink code may end up dereferencing an already freed node.
90
	 *
91 92 93
	 * sysfs_symlink_target_lock synchronizes @kobj->sd
	 * disassociation against symlink operations so that symlink code
	 * 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) {
T
Tejun Heo 已提交
100
		WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR);
101
		kernfs_remove(kn);
T
Tejun Heo 已提交
102
	}
L
Linus Torvalds 已提交
103 104
}

105 106
int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
			const void *new_ns)
107
{
108 109
	struct kernfs_node *parent;
	int ret;
110

111 112 113 114
	parent = kernfs_get_parent(kobj->sd);
	ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
	kernfs_put(parent);
	return ret;
115 116
}

117 118
int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
		      const void *new_ns)
119
{
120 121
	struct kernfs_node *kn = kobj->sd;
	struct kernfs_node *new_parent;
122

123 124
	new_parent = new_parent_kobj && new_parent_kobj->sd ?
		new_parent_kobj->sd : sysfs_root_kn;
125

126
	return kernfs_rename_ns(kn, new_parent, kn->name, new_ns);
127
}
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

/**
 * sysfs_create_mount_point - create an always empty directory
 * @parent_kobj:  kobject that will contain this always empty directory
 * @name: The name of the always empty directory to add
 */
int sysfs_create_mount_point(struct kobject *parent_kobj, const char *name)
{
	struct kernfs_node *kn, *parent = parent_kobj->sd;

	kn = kernfs_create_empty_dir(parent, name);
	if (IS_ERR(kn)) {
		if (PTR_ERR(kn) == -EEXIST)
			sysfs_warn_dup(parent, name);
		return PTR_ERR(kn);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(sysfs_create_mount_point);

/**
 *	sysfs_remove_mount_point - remove an always empty directory.
 *	@parent_kobj: kobject that will contain this always empty directory
 *	@name: The name of the always empty directory to remove
 *
 */
void sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name)
{
	struct kernfs_node *parent = parent_kobj->sd;

	kernfs_remove_by_name_ns(parent, name, NULL);
}
EXPORT_SYMBOL_GPL(sysfs_remove_mount_point);