18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fs/sysfs/symlink.c - sysfs symlink 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#include <linux/fs.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/kobject.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/security.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "sysfs.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int sysfs_do_create_link_sd(struct kernfs_node *parent, 218c2ecf20Sopenharmony_ci struct kobject *target_kobj, 228c2ecf20Sopenharmony_ci const char *name, int warn) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct kernfs_node *kn, *target = NULL; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (WARN_ON(!name || !parent)) 278c2ecf20Sopenharmony_ci return -EINVAL; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci /* 308c2ecf20Sopenharmony_ci * We don't own @target_kobj and it may be removed at any time. 318c2ecf20Sopenharmony_ci * Synchronize using sysfs_symlink_target_lock. See 328c2ecf20Sopenharmony_ci * sysfs_remove_dir() for details. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci spin_lock(&sysfs_symlink_target_lock); 358c2ecf20Sopenharmony_ci if (target_kobj->sd) { 368c2ecf20Sopenharmony_ci target = target_kobj->sd; 378c2ecf20Sopenharmony_ci kernfs_get(target); 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci spin_unlock(&sysfs_symlink_target_lock); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (!target) 428c2ecf20Sopenharmony_ci return -ENOENT; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci kn = kernfs_create_link(parent, name, target); 458c2ecf20Sopenharmony_ci kernfs_put(target); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (!IS_ERR(kn)) 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (warn && PTR_ERR(kn) == -EEXIST) 518c2ecf20Sopenharmony_ci sysfs_warn_dup(parent, name); 528c2ecf20Sopenharmony_ci return PTR_ERR(kn); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/** 568c2ecf20Sopenharmony_ci * sysfs_create_link_sd - create symlink to a given object. 578c2ecf20Sopenharmony_ci * @kn: directory we're creating the link in. 588c2ecf20Sopenharmony_ci * @target: object we're pointing to. 598c2ecf20Sopenharmony_ci * @name: name of the symlink. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ciint sysfs_create_link_sd(struct kernfs_node *kn, struct kobject *target, 628c2ecf20Sopenharmony_ci const char *name) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return sysfs_do_create_link_sd(kn, target, name, 1); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, 688c2ecf20Sopenharmony_ci const char *name, int warn) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct kernfs_node *parent = NULL; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!kobj) 738c2ecf20Sopenharmony_ci parent = sysfs_root_kn; 748c2ecf20Sopenharmony_ci else 758c2ecf20Sopenharmony_ci parent = kobj->sd; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (!parent) 788c2ecf20Sopenharmony_ci return -EFAULT; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return sysfs_do_create_link_sd(parent, target, name, warn); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/** 848c2ecf20Sopenharmony_ci * sysfs_create_link - create symlink between two objects. 858c2ecf20Sopenharmony_ci * @kobj: object whose directory we're creating the link in. 868c2ecf20Sopenharmony_ci * @target: object we're pointing to. 878c2ecf20Sopenharmony_ci * @name: name of the symlink. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ciint sysfs_create_link(struct kobject *kobj, struct kobject *target, 908c2ecf20Sopenharmony_ci const char *name) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci return sysfs_do_create_link(kobj, target, name, 1); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_link); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/** 978c2ecf20Sopenharmony_ci * sysfs_create_link_nowarn - create symlink between two objects. 988c2ecf20Sopenharmony_ci * @kobj: object whose directory we're creating the link in. 998c2ecf20Sopenharmony_ci * @target: object we're pointing to. 1008c2ecf20Sopenharmony_ci * @name: name of the symlink. 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * This function does the same as sysfs_create_link(), but it 1038c2ecf20Sopenharmony_ci * doesn't warn if the link already exists. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ciint sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, 1068c2ecf20Sopenharmony_ci const char *name) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci return sysfs_do_create_link(kobj, target, name, 0); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_link_nowarn); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/** 1138c2ecf20Sopenharmony_ci * sysfs_delete_link - remove symlink in object's directory. 1148c2ecf20Sopenharmony_ci * @kobj: object we're acting for. 1158c2ecf20Sopenharmony_ci * @targ: object we're pointing to. 1168c2ecf20Sopenharmony_ci * @name: name of the symlink to remove. 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * Unlike sysfs_remove_link sysfs_delete_link has enough information 1198c2ecf20Sopenharmony_ci * to successfully delete symlinks in tagged directories. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_civoid sysfs_delete_link(struct kobject *kobj, struct kobject *targ, 1228c2ecf20Sopenharmony_ci const char *name) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci const void *ns = NULL; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* 1278c2ecf20Sopenharmony_ci * We don't own @target and it may be removed at any time. 1288c2ecf20Sopenharmony_ci * Synchronize using sysfs_symlink_target_lock. See 1298c2ecf20Sopenharmony_ci * sysfs_remove_dir() for details. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci spin_lock(&sysfs_symlink_target_lock); 1328c2ecf20Sopenharmony_ci if (targ->sd && kernfs_ns_enabled(kobj->sd)) 1338c2ecf20Sopenharmony_ci ns = targ->sd->ns; 1348c2ecf20Sopenharmony_ci spin_unlock(&sysfs_symlink_target_lock); 1358c2ecf20Sopenharmony_ci kernfs_remove_by_name_ns(kobj->sd, name, ns); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/** 1398c2ecf20Sopenharmony_ci * sysfs_remove_link - remove symlink in object's directory. 1408c2ecf20Sopenharmony_ci * @kobj: object we're acting for. 1418c2ecf20Sopenharmony_ci * @name: name of the symlink to remove. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_civoid sysfs_remove_link(struct kobject *kobj, const char *name) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct kernfs_node *parent = NULL; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (!kobj) 1488c2ecf20Sopenharmony_ci parent = sysfs_root_kn; 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci parent = kobj->sd; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci kernfs_remove_by_name(parent, name); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_link); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/** 1578c2ecf20Sopenharmony_ci * sysfs_rename_link_ns - rename symlink in object's directory. 1588c2ecf20Sopenharmony_ci * @kobj: object we're acting for. 1598c2ecf20Sopenharmony_ci * @targ: object we're pointing to. 1608c2ecf20Sopenharmony_ci * @old: previous name of the symlink. 1618c2ecf20Sopenharmony_ci * @new: new name of the symlink. 1628c2ecf20Sopenharmony_ci * @new_ns: new namespace of the symlink. 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * A helper function for the common rename symlink idiom. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ciint sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, 1678c2ecf20Sopenharmony_ci const char *old, const char *new, const void *new_ns) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct kernfs_node *parent, *kn = NULL; 1708c2ecf20Sopenharmony_ci const void *old_ns = NULL; 1718c2ecf20Sopenharmony_ci int result; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (!kobj) 1748c2ecf20Sopenharmony_ci parent = sysfs_root_kn; 1758c2ecf20Sopenharmony_ci else 1768c2ecf20Sopenharmony_ci parent = kobj->sd; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (targ->sd) 1798c2ecf20Sopenharmony_ci old_ns = targ->sd->ns; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci result = -ENOENT; 1828c2ecf20Sopenharmony_ci kn = kernfs_find_and_get_ns(parent, old, old_ns); 1838c2ecf20Sopenharmony_ci if (!kn) 1848c2ecf20Sopenharmony_ci goto out; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci result = -EINVAL; 1878c2ecf20Sopenharmony_ci if (kernfs_type(kn) != KERNFS_LINK) 1888c2ecf20Sopenharmony_ci goto out; 1898c2ecf20Sopenharmony_ci if (kn->symlink.target_kn->priv != targ) 1908c2ecf20Sopenharmony_ci goto out; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci result = kernfs_rename_ns(kn, parent, new, new_ns); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ciout: 1958c2ecf20Sopenharmony_ci kernfs_put(kn); 1968c2ecf20Sopenharmony_ci return result; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_rename_link_ns); 199