162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fs/sysfs/symlink.c - sysfs symlink implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2001-3 Patrick Mochel 662306a36Sopenharmony_ci * Copyright (c) 2007 SUSE Linux Products GmbH 762306a36Sopenharmony_ci * Copyright (c) 2007 Tejun Heo <teheo@suse.de> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Please see Documentation/filesystems/sysfs.rst for more information. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/fs.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/kobject.h> 1562306a36Sopenharmony_ci#include <linux/mutex.h> 1662306a36Sopenharmony_ci#include <linux/security.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "sysfs.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic int sysfs_do_create_link_sd(struct kernfs_node *parent, 2162306a36Sopenharmony_ci struct kobject *target_kobj, 2262306a36Sopenharmony_ci const char *name, int warn) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct kernfs_node *kn, *target = NULL; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (WARN_ON(!name || !parent)) 2762306a36Sopenharmony_ci return -EINVAL; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* 3062306a36Sopenharmony_ci * We don't own @target_kobj and it may be removed at any time. 3162306a36Sopenharmony_ci * Synchronize using sysfs_symlink_target_lock. See 3262306a36Sopenharmony_ci * sysfs_remove_dir() for details. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci spin_lock(&sysfs_symlink_target_lock); 3562306a36Sopenharmony_ci if (target_kobj->sd) { 3662306a36Sopenharmony_ci target = target_kobj->sd; 3762306a36Sopenharmony_ci kernfs_get(target); 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci spin_unlock(&sysfs_symlink_target_lock); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (!target) 4262306a36Sopenharmony_ci return -ENOENT; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci kn = kernfs_create_link(parent, name, target); 4562306a36Sopenharmony_ci kernfs_put(target); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (!IS_ERR(kn)) 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (warn && PTR_ERR(kn) == -EEXIST) 5162306a36Sopenharmony_ci sysfs_warn_dup(parent, name); 5262306a36Sopenharmony_ci return PTR_ERR(kn); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * sysfs_create_link_sd - create symlink to a given object. 5762306a36Sopenharmony_ci * @kn: directory we're creating the link in. 5862306a36Sopenharmony_ci * @target: object we're pointing to. 5962306a36Sopenharmony_ci * @name: name of the symlink. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ciint sysfs_create_link_sd(struct kernfs_node *kn, struct kobject *target, 6262306a36Sopenharmony_ci const char *name) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci return sysfs_do_create_link_sd(kn, target, name, 1); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, 6862306a36Sopenharmony_ci const char *name, int warn) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct kernfs_node *parent = NULL; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!kobj) 7362306a36Sopenharmony_ci parent = sysfs_root_kn; 7462306a36Sopenharmony_ci else 7562306a36Sopenharmony_ci parent = kobj->sd; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (!parent) 7862306a36Sopenharmony_ci return -EFAULT; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return sysfs_do_create_link_sd(parent, target, name, warn); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * sysfs_create_link - create symlink between two objects. 8562306a36Sopenharmony_ci * @kobj: object whose directory we're creating the link in. 8662306a36Sopenharmony_ci * @target: object we're pointing to. 8762306a36Sopenharmony_ci * @name: name of the symlink. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ciint sysfs_create_link(struct kobject *kobj, struct kobject *target, 9062306a36Sopenharmony_ci const char *name) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci return sysfs_do_create_link(kobj, target, name, 1); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_link); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * sysfs_create_link_nowarn - create symlink between two objects. 9862306a36Sopenharmony_ci * @kobj: object whose directory we're creating the link in. 9962306a36Sopenharmony_ci * @target: object we're pointing to. 10062306a36Sopenharmony_ci * @name: name of the symlink. 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * This function does the same as sysfs_create_link(), but it 10362306a36Sopenharmony_ci * doesn't warn if the link already exists. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ciint sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, 10662306a36Sopenharmony_ci const char *name) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return sysfs_do_create_link(kobj, target, name, 0); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_link_nowarn); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * sysfs_delete_link - remove symlink in object's directory. 11462306a36Sopenharmony_ci * @kobj: object we're acting for. 11562306a36Sopenharmony_ci * @targ: object we're pointing to. 11662306a36Sopenharmony_ci * @name: name of the symlink to remove. 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * Unlike sysfs_remove_link sysfs_delete_link has enough information 11962306a36Sopenharmony_ci * to successfully delete symlinks in tagged directories. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_civoid sysfs_delete_link(struct kobject *kobj, struct kobject *targ, 12262306a36Sopenharmony_ci const char *name) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci const void *ns = NULL; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* 12762306a36Sopenharmony_ci * We don't own @target and it may be removed at any time. 12862306a36Sopenharmony_ci * Synchronize using sysfs_symlink_target_lock. See 12962306a36Sopenharmony_ci * sysfs_remove_dir() for details. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci spin_lock(&sysfs_symlink_target_lock); 13262306a36Sopenharmony_ci if (targ->sd && kernfs_ns_enabled(kobj->sd)) 13362306a36Sopenharmony_ci ns = targ->sd->ns; 13462306a36Sopenharmony_ci spin_unlock(&sysfs_symlink_target_lock); 13562306a36Sopenharmony_ci kernfs_remove_by_name_ns(kobj->sd, name, ns); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/** 13962306a36Sopenharmony_ci * sysfs_remove_link - remove symlink in object's directory. 14062306a36Sopenharmony_ci * @kobj: object we're acting for. 14162306a36Sopenharmony_ci * @name: name of the symlink to remove. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_civoid sysfs_remove_link(struct kobject *kobj, const char *name) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct kernfs_node *parent = NULL; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!kobj) 14862306a36Sopenharmony_ci parent = sysfs_root_kn; 14962306a36Sopenharmony_ci else 15062306a36Sopenharmony_ci parent = kobj->sd; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci kernfs_remove_by_name(parent, name); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_link); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/** 15762306a36Sopenharmony_ci * sysfs_rename_link_ns - rename symlink in object's directory. 15862306a36Sopenharmony_ci * @kobj: object we're acting for. 15962306a36Sopenharmony_ci * @targ: object we're pointing to. 16062306a36Sopenharmony_ci * @old: previous name of the symlink. 16162306a36Sopenharmony_ci * @new: new name of the symlink. 16262306a36Sopenharmony_ci * @new_ns: new namespace of the symlink. 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * A helper function for the common rename symlink idiom. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ciint sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, 16762306a36Sopenharmony_ci const char *old, const char *new, const void *new_ns) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct kernfs_node *parent, *kn = NULL; 17062306a36Sopenharmony_ci const void *old_ns = NULL; 17162306a36Sopenharmony_ci int result; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!kobj) 17462306a36Sopenharmony_ci parent = sysfs_root_kn; 17562306a36Sopenharmony_ci else 17662306a36Sopenharmony_ci parent = kobj->sd; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (targ->sd) 17962306a36Sopenharmony_ci old_ns = targ->sd->ns; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci result = -ENOENT; 18262306a36Sopenharmony_ci kn = kernfs_find_and_get_ns(parent, old, old_ns); 18362306a36Sopenharmony_ci if (!kn) 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci result = -EINVAL; 18762306a36Sopenharmony_ci if (kernfs_type(kn) != KERNFS_LINK) 18862306a36Sopenharmony_ci goto out; 18962306a36Sopenharmony_ci if (kn->symlink.target_kn->priv != targ) 19062306a36Sopenharmony_ci goto out; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci result = kernfs_rename_ns(kn, parent, new, new_ns); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciout: 19562306a36Sopenharmony_ci kernfs_put(kn); 19662306a36Sopenharmony_ci return result; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_rename_link_ns); 199