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 | |
19 | DEFINE_SPINLOCK(sysfs_symlink_target_lock); |
20 | |
21 | void 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(kn: parent, buf, PATH_MAX); |
28 | |
29 | pr_warn("cannot create duplicate filename '%s/%s'\n" , buf, name); |
30 | dump_stack(); |
31 | |
32 | kfree(objp: 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 | */ |
40 | int 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: &uid, gid: &gid); |
58 | |
59 | kn = kernfs_create_dir_ns(parent, name: kobject_name(kobj), mode: 0755, uid, gid, |
60 | priv: kobj, ns); |
61 | if (IS_ERR(ptr: kn)) { |
62 | if (PTR_ERR(ptr: kn) == -EEXIST) |
63 | sysfs_warn_dup(parent, name: kobject_name(kobj)); |
64 | return PTR_ERR(ptr: 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 | */ |
79 | void sysfs_remove_dir(struct kobject *kobj) |
80 | { |
81 | struct kernfs_node *kn = kobj->sd; |
82 | |
83 | /* |
84 | * In general, kboject 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(lock: &sysfs_symlink_target_lock); |
96 | kobj->sd = NULL; |
97 | spin_unlock(lock: &sysfs_symlink_target_lock); |
98 | |
99 | if (kn) { |
100 | WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR); |
101 | kernfs_remove(kn); |
102 | } |
103 | } |
104 | |
105 | int 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(kn: kobj->sd); |
112 | ret = kernfs_rename_ns(kn: kobj->sd, new_parent: parent, new_name, new_ns); |
113 | kernfs_put(kn: parent); |
114 | return ret; |
115 | } |
116 | |
117 | int 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, new_name: 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 | */ |
134 | int 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(ptr: kn)) { |
140 | if (PTR_ERR(ptr: kn) == -EEXIST) |
141 | sysfs_warn_dup(parent, name); |
142 | return PTR_ERR(ptr: kn); |
143 | } |
144 | |
145 | return 0; |
146 | } |
147 | EXPORT_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 | */ |
155 | void 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 | } |
161 | EXPORT_SYMBOL_GPL(sysfs_remove_mount_point); |
162 | |