Branch data Line data Source code
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 : :
19 : : DEFINE_SPINLOCK(sysfs_symlink_target_lock);
20 : :
21 : 0 : void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
22 : : {
23 : : char *buf;
24 : :
25 : 0 : buf = kzalloc(PATH_MAX, GFP_KERNEL);
26 [ # # ]: 0 : if (buf)
27 : : kernfs_path(parent, buf, PATH_MAX);
28 : :
29 : 0 : pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name);
30 : 0 : dump_stack();
31 : :
32 : 0 : kfree(buf);
33 : 0 : }
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 : 277791 : 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 [ - + + - ]: 277791 : if (WARN_ON(!kobj))
47 : : return -EINVAL;
48 : :
49 [ + + ]: 277790 : if (kobj->parent)
50 : 275720 : parent = kobj->parent->sd;
51 : : else
52 : 2070 : parent = sysfs_root_kn;
53 : :
54 [ + + ]: 277790 : if (!parent)
55 : : return -ENOENT;
56 : :
57 : 277784 : kobject_get_ownership(kobj, &uid, &gid);
58 : :
59 : 277790 : kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
60 : : S_IRWXU | S_IRUGO | S_IXUGO, uid, gid,
61 : : kobj, ns);
62 [ - + ]: 277794 : if (IS_ERR(kn)) {
63 [ # # ]: 0 : if (PTR_ERR(kn) == -EEXIST)
64 : 0 : sysfs_warn_dup(parent, kobject_name(kobj));
65 : 0 : return PTR_ERR(kn);
66 : : }
67 : :
68 : 277794 : kobj->sd = kn;
69 : 277794 : 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 : : */
80 : 207 : void sysfs_remove_dir(struct kobject *kobj)
81 : : {
82 : 207 : 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 : 207 : kobj->sd = NULL;
98 : : spin_unlock(&sysfs_symlink_target_lock);
99 : :
100 [ + - ]: 207 : if (kn) {
101 [ - + # # ]: 207 : WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR);
102 : 207 : kernfs_remove(kn);
103 : : }
104 : 207 : }
105 : :
106 : 0 : int 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 : 0 : parent = kernfs_get_parent(kobj->sd);
113 : 0 : ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns);
114 : 0 : kernfs_put(parent);
115 : 0 : return ret;
116 : : }
117 : :
118 : 0 : int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj,
119 : : const void *new_ns)
120 : : {
121 : 0 : struct kernfs_node *kn = kobj->sd;
122 : : struct kernfs_node *new_parent;
123 : :
124 [ # # ]: 0 : new_parent = new_parent_kobj && new_parent_kobj->sd ?
125 [ # # ]: 0 : new_parent_kobj->sd : sysfs_root_kn;
126 : :
127 : 0 : 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 : : */
135 : 1449 : int sysfs_create_mount_point(struct kobject *parent_kobj, const char *name)
136 : : {
137 : 1449 : struct kernfs_node *kn, *parent = parent_kobj->sd;
138 : :
139 : 1449 : kn = kernfs_create_empty_dir(parent, name);
140 [ - + ]: 1449 : if (IS_ERR(kn)) {
141 [ # # ]: 0 : if (PTR_ERR(kn) == -EEXIST)
142 : 0 : sysfs_warn_dup(parent, name);
143 : 0 : return PTR_ERR(kn);
144 : : }
145 : :
146 : : return 0;
147 : : }
148 : : EXPORT_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 : : */
156 : 0 : void sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name)
157 : : {
158 : 0 : struct kernfs_node *parent = parent_kobj->sd;
159 : :
160 : 0 : kernfs_remove_by_name_ns(parent, name, NULL);
161 : 0 : }
162 : : EXPORT_SYMBOL_GPL(sysfs_remove_mount_point);
|