18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fs/sysfs/dir.c - sysfs core and dir operation implementation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2001-3 Patrick Mochel 68c2ecf20Sopenharmony_ci * Copyright (c) 2007 SUSE Linux Products GmbH 78c2ecf20Sopenharmony_ci * Copyright (c) 2007 Tejun Heo <teheo@suse.de> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Please see Documentation/filesystems/sysfs.rst for more information. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "sysfs: " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/fs.h> 158c2ecf20Sopenharmony_ci#include <linux/kobject.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include "sysfs.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(sysfs_symlink_target_lock); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_civoid sysfs_warn_dup(struct kernfs_node *parent, const char *name) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci char *buf; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci buf = kzalloc(PATH_MAX, GFP_KERNEL); 268c2ecf20Sopenharmony_ci if (buf) 278c2ecf20Sopenharmony_ci kernfs_path(parent, buf, PATH_MAX); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci pr_warn("cannot create duplicate filename '%s/%s'\n", buf, name); 308c2ecf20Sopenharmony_ci dump_stack(); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci kfree(buf); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/** 368c2ecf20Sopenharmony_ci * sysfs_create_dir_ns - create a directory for an object with a namespace tag 378c2ecf20Sopenharmony_ci * @kobj: object we're creating directory for 388c2ecf20Sopenharmony_ci * @ns: the namespace tag to use 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ciint sysfs_create_dir_ns(struct kobject *kobj, const void *ns) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct kernfs_node *parent, *kn; 438c2ecf20Sopenharmony_ci kuid_t uid; 448c2ecf20Sopenharmony_ci kgid_t gid; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (WARN_ON(!kobj)) 478c2ecf20Sopenharmony_ci return -EINVAL; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (kobj->parent) 508c2ecf20Sopenharmony_ci parent = kobj->parent->sd; 518c2ecf20Sopenharmony_ci else 528c2ecf20Sopenharmony_ci parent = sysfs_root_kn; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!parent) 558c2ecf20Sopenharmony_ci return -ENOENT; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci kobject_get_ownership(kobj, &uid, &gid); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 608c2ecf20Sopenharmony_ci S_IRWXU | S_IRUGO | S_IXUGO, uid, gid, 618c2ecf20Sopenharmony_ci kobj, ns); 628c2ecf20Sopenharmony_ci if (IS_ERR(kn)) { 638c2ecf20Sopenharmony_ci if (PTR_ERR(kn) == -EEXIST) 648c2ecf20Sopenharmony_ci sysfs_warn_dup(parent, kobject_name(kobj)); 658c2ecf20Sopenharmony_ci return PTR_ERR(kn); 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci kobj->sd = kn; 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/** 738c2ecf20Sopenharmony_ci * sysfs_remove_dir - remove an object's directory. 748c2ecf20Sopenharmony_ci * @kobj: object. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * The only thing special about this is that we remove any files in 778c2ecf20Sopenharmony_ci * the directory before we remove the directory, and we've inlined 788c2ecf20Sopenharmony_ci * what used to be sysfs_rmdir() below, instead of calling separately. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_civoid sysfs_remove_dir(struct kobject *kobj) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct kernfs_node *kn = kobj->sd; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* 858c2ecf20Sopenharmony_ci * In general, kboject owner is responsible for ensuring removal 868c2ecf20Sopenharmony_ci * doesn't race with other operations and sysfs doesn't provide any 878c2ecf20Sopenharmony_ci * protection; however, when @kobj is used as a symlink target, the 888c2ecf20Sopenharmony_ci * symlinking entity usually doesn't own @kobj and thus has no 898c2ecf20Sopenharmony_ci * control over removal. @kobj->sd may be removed anytime 908c2ecf20Sopenharmony_ci * and symlink code may end up dereferencing an already freed node. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * sysfs_symlink_target_lock synchronizes @kobj->sd 938c2ecf20Sopenharmony_ci * disassociation against symlink operations so that symlink code 948c2ecf20Sopenharmony_ci * can safely dereference @kobj->sd. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci spin_lock(&sysfs_symlink_target_lock); 978c2ecf20Sopenharmony_ci kobj->sd = NULL; 988c2ecf20Sopenharmony_ci spin_unlock(&sysfs_symlink_target_lock); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (kn) { 1018c2ecf20Sopenharmony_ci WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR); 1028c2ecf20Sopenharmony_ci kernfs_remove(kn); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciint sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, 1078c2ecf20Sopenharmony_ci const void *new_ns) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct kernfs_node *parent; 1108c2ecf20Sopenharmony_ci int ret; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci parent = kernfs_get_parent(kobj->sd); 1138c2ecf20Sopenharmony_ci ret = kernfs_rename_ns(kobj->sd, parent, new_name, new_ns); 1148c2ecf20Sopenharmony_ci kernfs_put(parent); 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ciint sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, 1198c2ecf20Sopenharmony_ci const void *new_ns) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct kernfs_node *kn = kobj->sd; 1228c2ecf20Sopenharmony_ci struct kernfs_node *new_parent; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci new_parent = new_parent_kobj && new_parent_kobj->sd ? 1258c2ecf20Sopenharmony_ci new_parent_kobj->sd : sysfs_root_kn; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return kernfs_rename_ns(kn, new_parent, kn->name, new_ns); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/** 1318c2ecf20Sopenharmony_ci * sysfs_create_mount_point - create an always empty directory 1328c2ecf20Sopenharmony_ci * @parent_kobj: kobject that will contain this always empty directory 1338c2ecf20Sopenharmony_ci * @name: The name of the always empty directory to add 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ciint sysfs_create_mount_point(struct kobject *parent_kobj, const char *name) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct kernfs_node *kn, *parent = parent_kobj->sd; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci kn = kernfs_create_empty_dir(parent, name); 1408c2ecf20Sopenharmony_ci if (IS_ERR(kn)) { 1418c2ecf20Sopenharmony_ci if (PTR_ERR(kn) == -EEXIST) 1428c2ecf20Sopenharmony_ci sysfs_warn_dup(parent, name); 1438c2ecf20Sopenharmony_ci return PTR_ERR(kn); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_mount_point); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * sysfs_remove_mount_point - remove an always empty directory. 1528c2ecf20Sopenharmony_ci * @parent_kobj: kobject that will contain this always empty directory 1538c2ecf20Sopenharmony_ci * @name: The name of the always empty directory to remove 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_civoid sysfs_remove_mount_point(struct kobject *parent_kobj, const char *name) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct kernfs_node *parent = parent_kobj->sd; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci kernfs_remove_by_name_ns(parent, name, NULL); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_mount_point); 163