162 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * fs/sysfs/dir.c - sysfs core and dir operation implementation4 *5 * Copyright (c) 2001-3 Patrick Mochel6 * Copyright (c) 2007 SUSE Linux Products GmbH7 * 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: " fmt13 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 tag37 * @kobj: object we're creating directory for38 * @ns: the namespace tag to use39 */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 else52 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 in76 * the directory before we remove the directory, and we've inlined77 * 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 removal85 * doesn't race with other operations and sysfs doesn't provide any86 * protection; however, when @kobj is used as a symlink target, the87 * symlinking entity usually doesn't own @kobj and thus has no88 * control over removal. @kobj->sd may be removed anytime89 * and symlink code may end up dereferencing an already freed node.90 *91 * sysfs_symlink_target_lock synchronizes @kobj->sd92 * disassociation against symlink operations so that symlink code93 * 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 directory131 * @parent_kobj: kobject that will contain this always empty directory132 * @name: The name of the always empty directory to add133 */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 directory152 * @name: The name of the always empty directory to remove153 *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);162