18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fs/sysfs/group.c - Operations for adding/removing multiple files at once. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2003 Patrick Mochel 68c2ecf20Sopenharmony_ci * Copyright (c) 2003 Open Source Development Lab 78c2ecf20Sopenharmony_ci * Copyright (c) 2013 Greg Kroah-Hartman 88c2ecf20Sopenharmony_ci * Copyright (c) 2013 The Linux Foundation 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kobject.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/dcache.h> 148c2ecf20Sopenharmony_ci#include <linux/namei.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/fs.h> 178c2ecf20Sopenharmony_ci#include "sysfs.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void remove_files(struct kernfs_node *parent, 218c2ecf20Sopenharmony_ci const struct attribute_group *grp) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct attribute *const *attr; 248c2ecf20Sopenharmony_ci struct bin_attribute *const *bin_attr; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (grp->attrs) 278c2ecf20Sopenharmony_ci for (attr = grp->attrs; *attr; attr++) 288c2ecf20Sopenharmony_ci kernfs_remove_by_name(parent, (*attr)->name); 298c2ecf20Sopenharmony_ci if (grp->bin_attrs) 308c2ecf20Sopenharmony_ci for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) 318c2ecf20Sopenharmony_ci kernfs_remove_by_name(parent, (*bin_attr)->attr.name); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int create_files(struct kernfs_node *parent, struct kobject *kobj, 358c2ecf20Sopenharmony_ci kuid_t uid, kgid_t gid, 368c2ecf20Sopenharmony_ci const struct attribute_group *grp, int update) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct attribute *const *attr; 398c2ecf20Sopenharmony_ci struct bin_attribute *const *bin_attr; 408c2ecf20Sopenharmony_ci int error = 0, i; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (grp->attrs) { 438c2ecf20Sopenharmony_ci for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) { 448c2ecf20Sopenharmony_ci umode_t mode = (*attr)->mode; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* 478c2ecf20Sopenharmony_ci * In update mode, we're changing the permissions or 488c2ecf20Sopenharmony_ci * visibility. Do this by first removing then 498c2ecf20Sopenharmony_ci * re-adding (if required) the file. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci if (update) 528c2ecf20Sopenharmony_ci kernfs_remove_by_name(parent, (*attr)->name); 538c2ecf20Sopenharmony_ci if (grp->is_visible) { 548c2ecf20Sopenharmony_ci mode = grp->is_visible(kobj, *attr, i); 558c2ecf20Sopenharmony_ci if (!mode) 568c2ecf20Sopenharmony_ci continue; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci WARN(mode & ~(SYSFS_PREALLOC | 0664), 608c2ecf20Sopenharmony_ci "Attribute %s: Invalid permissions 0%o\n", 618c2ecf20Sopenharmony_ci (*attr)->name, mode); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci mode &= SYSFS_PREALLOC | 0664; 648c2ecf20Sopenharmony_ci error = sysfs_add_file_mode_ns(parent, *attr, false, 658c2ecf20Sopenharmony_ci mode, uid, gid, NULL); 668c2ecf20Sopenharmony_ci if (unlikely(error)) 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci if (error) { 708c2ecf20Sopenharmony_ci remove_files(parent, grp); 718c2ecf20Sopenharmony_ci goto exit; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (grp->bin_attrs) { 768c2ecf20Sopenharmony_ci for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) { 778c2ecf20Sopenharmony_ci umode_t mode = (*bin_attr)->attr.mode; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (update) 808c2ecf20Sopenharmony_ci kernfs_remove_by_name(parent, 818c2ecf20Sopenharmony_ci (*bin_attr)->attr.name); 828c2ecf20Sopenharmony_ci if (grp->is_bin_visible) { 838c2ecf20Sopenharmony_ci mode = grp->is_bin_visible(kobj, *bin_attr, i); 848c2ecf20Sopenharmony_ci if (!mode) 858c2ecf20Sopenharmony_ci continue; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci WARN(mode & ~(SYSFS_PREALLOC | 0664), 898c2ecf20Sopenharmony_ci "Attribute %s: Invalid permissions 0%o\n", 908c2ecf20Sopenharmony_ci (*bin_attr)->attr.name, mode); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci mode &= SYSFS_PREALLOC | 0664; 938c2ecf20Sopenharmony_ci error = sysfs_add_file_mode_ns(parent, 948c2ecf20Sopenharmony_ci &(*bin_attr)->attr, true, 958c2ecf20Sopenharmony_ci mode, 968c2ecf20Sopenharmony_ci uid, gid, NULL); 978c2ecf20Sopenharmony_ci if (error) 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci if (error) 1018c2ecf20Sopenharmony_ci remove_files(parent, grp); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ciexit: 1048c2ecf20Sopenharmony_ci return error; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int internal_create_group(struct kobject *kobj, int update, 1098c2ecf20Sopenharmony_ci const struct attribute_group *grp) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct kernfs_node *kn; 1128c2ecf20Sopenharmony_ci kuid_t uid; 1138c2ecf20Sopenharmony_ci kgid_t gid; 1148c2ecf20Sopenharmony_ci int error; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (WARN_ON(!kobj || (!update && !kobj->sd))) 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Updates may happen before the object has been instantiated */ 1208c2ecf20Sopenharmony_ci if (unlikely(update && !kobj->sd)) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci if (!grp->attrs && !grp->bin_attrs) { 1238c2ecf20Sopenharmony_ci WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n", 1248c2ecf20Sopenharmony_ci kobj->name, grp->name ?: ""); 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci kobject_get_ownership(kobj, &uid, &gid); 1288c2ecf20Sopenharmony_ci if (grp->name) { 1298c2ecf20Sopenharmony_ci if (update) { 1308c2ecf20Sopenharmony_ci kn = kernfs_find_and_get(kobj->sd, grp->name); 1318c2ecf20Sopenharmony_ci if (!kn) { 1328c2ecf20Sopenharmony_ci pr_warn("Can't update unknown attr grp name: %s/%s\n", 1338c2ecf20Sopenharmony_ci kobj->name, grp->name); 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci } else { 1378c2ecf20Sopenharmony_ci kn = kernfs_create_dir_ns(kobj->sd, grp->name, 1388c2ecf20Sopenharmony_ci S_IRWXU | S_IRUGO | S_IXUGO, 1398c2ecf20Sopenharmony_ci uid, gid, kobj, NULL); 1408c2ecf20Sopenharmony_ci if (IS_ERR(kn)) { 1418c2ecf20Sopenharmony_ci if (PTR_ERR(kn) == -EEXIST) 1428c2ecf20Sopenharmony_ci sysfs_warn_dup(kobj->sd, grp->name); 1438c2ecf20Sopenharmony_ci return PTR_ERR(kn); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci } else 1478c2ecf20Sopenharmony_ci kn = kobj->sd; 1488c2ecf20Sopenharmony_ci kernfs_get(kn); 1498c2ecf20Sopenharmony_ci error = create_files(kn, kobj, uid, gid, grp, update); 1508c2ecf20Sopenharmony_ci if (error) { 1518c2ecf20Sopenharmony_ci if (grp->name) 1528c2ecf20Sopenharmony_ci kernfs_remove(kn); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci kernfs_put(kn); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (grp->name && update) 1578c2ecf20Sopenharmony_ci kernfs_put(kn); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return error; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/** 1638c2ecf20Sopenharmony_ci * sysfs_create_group - given a directory kobject, create an attribute group 1648c2ecf20Sopenharmony_ci * @kobj: The kobject to create the group on 1658c2ecf20Sopenharmony_ci * @grp: The attribute group to create 1668c2ecf20Sopenharmony_ci * 1678c2ecf20Sopenharmony_ci * This function creates a group for the first time. It will explicitly 1688c2ecf20Sopenharmony_ci * warn and error if any of the attribute files being created already exist. 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Returns 0 on success or error code on failure. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ciint sysfs_create_group(struct kobject *kobj, 1738c2ecf20Sopenharmony_ci const struct attribute_group *grp) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci return internal_create_group(kobj, 0, grp); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_group); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int internal_create_groups(struct kobject *kobj, int update, 1808c2ecf20Sopenharmony_ci const struct attribute_group **groups) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci int error = 0; 1838c2ecf20Sopenharmony_ci int i; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!groups) 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci for (i = 0; groups[i]; i++) { 1898c2ecf20Sopenharmony_ci error = internal_create_group(kobj, update, groups[i]); 1908c2ecf20Sopenharmony_ci if (error) { 1918c2ecf20Sopenharmony_ci while (--i >= 0) 1928c2ecf20Sopenharmony_ci sysfs_remove_group(kobj, groups[i]); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci return error; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/** 2008c2ecf20Sopenharmony_ci * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups 2018c2ecf20Sopenharmony_ci * @kobj: The kobject to create the group on 2028c2ecf20Sopenharmony_ci * @groups: The attribute groups to create, NULL terminated 2038c2ecf20Sopenharmony_ci * 2048c2ecf20Sopenharmony_ci * This function creates a bunch of attribute groups. If an error occurs when 2058c2ecf20Sopenharmony_ci * creating a group, all previously created groups will be removed, unwinding 2068c2ecf20Sopenharmony_ci * everything back to the original state when this function was called. 2078c2ecf20Sopenharmony_ci * It will explicitly warn and error if any of the attribute files being 2088c2ecf20Sopenharmony_ci * created already exist. 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * Returns 0 on success or error code from sysfs_create_group on failure. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ciint sysfs_create_groups(struct kobject *kobj, 2138c2ecf20Sopenharmony_ci const struct attribute_group **groups) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci return internal_create_groups(kobj, 0, groups); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_create_groups); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/** 2208c2ecf20Sopenharmony_ci * sysfs_update_groups - given a directory kobject, create a bunch of attribute groups 2218c2ecf20Sopenharmony_ci * @kobj: The kobject to update the group on 2228c2ecf20Sopenharmony_ci * @groups: The attribute groups to update, NULL terminated 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * This function update a bunch of attribute groups. If an error occurs when 2258c2ecf20Sopenharmony_ci * updating a group, all previously updated groups will be removed together 2268c2ecf20Sopenharmony_ci * with already existing (not updated) attributes. 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * Returns 0 on success or error code from sysfs_update_group on failure. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ciint sysfs_update_groups(struct kobject *kobj, 2318c2ecf20Sopenharmony_ci const struct attribute_group **groups) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci return internal_create_groups(kobj, 1, groups); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_update_groups); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/** 2388c2ecf20Sopenharmony_ci * sysfs_update_group - given a directory kobject, update an attribute group 2398c2ecf20Sopenharmony_ci * @kobj: The kobject to update the group on 2408c2ecf20Sopenharmony_ci * @grp: The attribute group to update 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * This function updates an attribute group. Unlike 2438c2ecf20Sopenharmony_ci * sysfs_create_group(), it will explicitly not warn or error if any 2448c2ecf20Sopenharmony_ci * of the attribute files being created already exist. Furthermore, 2458c2ecf20Sopenharmony_ci * if the visibility of the files has changed through the is_visible() 2468c2ecf20Sopenharmony_ci * callback, it will update the permissions and add or remove the 2478c2ecf20Sopenharmony_ci * relevant files. Changing a group's name (subdirectory name under 2488c2ecf20Sopenharmony_ci * kobj's directory in sysfs) is not allowed. 2498c2ecf20Sopenharmony_ci * 2508c2ecf20Sopenharmony_ci * The primary use for this function is to call it after making a change 2518c2ecf20Sopenharmony_ci * that affects group visibility. 2528c2ecf20Sopenharmony_ci * 2538c2ecf20Sopenharmony_ci * Returns 0 on success or error code on failure. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ciint sysfs_update_group(struct kobject *kobj, 2568c2ecf20Sopenharmony_ci const struct attribute_group *grp) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci return internal_create_group(kobj, 1, grp); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_update_group); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/** 2638c2ecf20Sopenharmony_ci * sysfs_remove_group: remove a group from a kobject 2648c2ecf20Sopenharmony_ci * @kobj: kobject to remove the group from 2658c2ecf20Sopenharmony_ci * @grp: group to remove 2668c2ecf20Sopenharmony_ci * 2678c2ecf20Sopenharmony_ci * This function removes a group of attributes from a kobject. The attributes 2688c2ecf20Sopenharmony_ci * previously have to have been created for this group, otherwise it will fail. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_civoid sysfs_remove_group(struct kobject *kobj, 2718c2ecf20Sopenharmony_ci const struct attribute_group *grp) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct kernfs_node *parent = kobj->sd; 2748c2ecf20Sopenharmony_ci struct kernfs_node *kn; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (grp->name) { 2778c2ecf20Sopenharmony_ci kn = kernfs_find_and_get(parent, grp->name); 2788c2ecf20Sopenharmony_ci if (!kn) { 2798c2ecf20Sopenharmony_ci WARN(!kn, KERN_WARNING 2808c2ecf20Sopenharmony_ci "sysfs group '%s' not found for kobject '%s'\n", 2818c2ecf20Sopenharmony_ci grp->name, kobject_name(kobj)); 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci } else { 2858c2ecf20Sopenharmony_ci kn = parent; 2868c2ecf20Sopenharmony_ci kernfs_get(kn); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci remove_files(kn, grp); 2908c2ecf20Sopenharmony_ci if (grp->name) 2918c2ecf20Sopenharmony_ci kernfs_remove(kn); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci kernfs_put(kn); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_group); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/** 2988c2ecf20Sopenharmony_ci * sysfs_remove_groups - remove a list of groups 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * @kobj: The kobject for the groups to be removed from 3018c2ecf20Sopenharmony_ci * @groups: NULL terminated list of groups to be removed 3028c2ecf20Sopenharmony_ci * 3038c2ecf20Sopenharmony_ci * If groups is not NULL, remove the specified groups from the kobject. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_civoid sysfs_remove_groups(struct kobject *kobj, 3068c2ecf20Sopenharmony_ci const struct attribute_group **groups) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci int i; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (!groups) 3118c2ecf20Sopenharmony_ci return; 3128c2ecf20Sopenharmony_ci for (i = 0; groups[i]; i++) 3138c2ecf20Sopenharmony_ci sysfs_remove_group(kobj, groups[i]); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_groups); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/** 3188c2ecf20Sopenharmony_ci * sysfs_merge_group - merge files into a pre-existing attribute group. 3198c2ecf20Sopenharmony_ci * @kobj: The kobject containing the group. 3208c2ecf20Sopenharmony_ci * @grp: The files to create and the attribute group they belong to. 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * This function returns an error if the group doesn't exist or any of the 3238c2ecf20Sopenharmony_ci * files already exist in that group, in which case none of the new files 3248c2ecf20Sopenharmony_ci * are created. 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ciint sysfs_merge_group(struct kobject *kobj, 3278c2ecf20Sopenharmony_ci const struct attribute_group *grp) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct kernfs_node *parent; 3308c2ecf20Sopenharmony_ci kuid_t uid; 3318c2ecf20Sopenharmony_ci kgid_t gid; 3328c2ecf20Sopenharmony_ci int error = 0; 3338c2ecf20Sopenharmony_ci struct attribute *const *attr; 3348c2ecf20Sopenharmony_ci int i; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci parent = kernfs_find_and_get(kobj->sd, grp->name); 3378c2ecf20Sopenharmony_ci if (!parent) 3388c2ecf20Sopenharmony_ci return -ENOENT; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci kobject_get_ownership(kobj, &uid, &gid); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) 3438c2ecf20Sopenharmony_ci error = sysfs_add_file_mode_ns(parent, *attr, false, 3448c2ecf20Sopenharmony_ci (*attr)->mode, uid, gid, NULL); 3458c2ecf20Sopenharmony_ci if (error) { 3468c2ecf20Sopenharmony_ci while (--i >= 0) 3478c2ecf20Sopenharmony_ci kernfs_remove_by_name(parent, (*--attr)->name); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci kernfs_put(parent); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return error; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_merge_group); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/** 3568c2ecf20Sopenharmony_ci * sysfs_unmerge_group - remove files from a pre-existing attribute group. 3578c2ecf20Sopenharmony_ci * @kobj: The kobject containing the group. 3588c2ecf20Sopenharmony_ci * @grp: The files to remove and the attribute group they belong to. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_civoid sysfs_unmerge_group(struct kobject *kobj, 3618c2ecf20Sopenharmony_ci const struct attribute_group *grp) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct kernfs_node *parent; 3648c2ecf20Sopenharmony_ci struct attribute *const *attr; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci parent = kernfs_find_and_get(kobj->sd, grp->name); 3678c2ecf20Sopenharmony_ci if (parent) { 3688c2ecf20Sopenharmony_ci for (attr = grp->attrs; *attr; ++attr) 3698c2ecf20Sopenharmony_ci kernfs_remove_by_name(parent, (*attr)->name); 3708c2ecf20Sopenharmony_ci kernfs_put(parent); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_unmerge_group); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/** 3768c2ecf20Sopenharmony_ci * sysfs_add_link_to_group - add a symlink to an attribute group. 3778c2ecf20Sopenharmony_ci * @kobj: The kobject containing the group. 3788c2ecf20Sopenharmony_ci * @group_name: The name of the group. 3798c2ecf20Sopenharmony_ci * @target: The target kobject of the symlink to create. 3808c2ecf20Sopenharmony_ci * @link_name: The name of the symlink to create. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ciint sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, 3838c2ecf20Sopenharmony_ci struct kobject *target, const char *link_name) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct kernfs_node *parent; 3868c2ecf20Sopenharmony_ci int error = 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci parent = kernfs_find_and_get(kobj->sd, group_name); 3898c2ecf20Sopenharmony_ci if (!parent) 3908c2ecf20Sopenharmony_ci return -ENOENT; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci error = sysfs_create_link_sd(parent, target, link_name); 3938c2ecf20Sopenharmony_ci kernfs_put(parent); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return error; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_add_link_to_group); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/** 4008c2ecf20Sopenharmony_ci * sysfs_remove_link_from_group - remove a symlink from an attribute group. 4018c2ecf20Sopenharmony_ci * @kobj: The kobject containing the group. 4028c2ecf20Sopenharmony_ci * @group_name: The name of the group. 4038c2ecf20Sopenharmony_ci * @link_name: The name of the symlink to remove. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_civoid sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, 4068c2ecf20Sopenharmony_ci const char *link_name) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct kernfs_node *parent; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci parent = kernfs_find_and_get(kobj->sd, group_name); 4118c2ecf20Sopenharmony_ci if (parent) { 4128c2ecf20Sopenharmony_ci kernfs_remove_by_name(parent, link_name); 4138c2ecf20Sopenharmony_ci kernfs_put(parent); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/** 4198c2ecf20Sopenharmony_ci * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing 4208c2ecf20Sopenharmony_ci * to a group or an attribute 4218c2ecf20Sopenharmony_ci * @kobj: The kobject containing the group. 4228c2ecf20Sopenharmony_ci * @target_kobj: The target kobject. 4238c2ecf20Sopenharmony_ci * @target_name: The name of the target group or attribute. 4248c2ecf20Sopenharmony_ci * @symlink_name: The name of the symlink file (target_name will be 4258c2ecf20Sopenharmony_ci * considered if symlink_name is NULL). 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ciint compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, 4288c2ecf20Sopenharmony_ci struct kobject *target_kobj, 4298c2ecf20Sopenharmony_ci const char *target_name, 4308c2ecf20Sopenharmony_ci const char *symlink_name) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct kernfs_node *target; 4338c2ecf20Sopenharmony_ci struct kernfs_node *entry; 4348c2ecf20Sopenharmony_ci struct kernfs_node *link; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * We don't own @target_kobj and it may be removed at any time. 4388c2ecf20Sopenharmony_ci * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir() 4398c2ecf20Sopenharmony_ci * for details. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci spin_lock(&sysfs_symlink_target_lock); 4428c2ecf20Sopenharmony_ci target = target_kobj->sd; 4438c2ecf20Sopenharmony_ci if (target) 4448c2ecf20Sopenharmony_ci kernfs_get(target); 4458c2ecf20Sopenharmony_ci spin_unlock(&sysfs_symlink_target_lock); 4468c2ecf20Sopenharmony_ci if (!target) 4478c2ecf20Sopenharmony_ci return -ENOENT; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci entry = kernfs_find_and_get(target_kobj->sd, target_name); 4508c2ecf20Sopenharmony_ci if (!entry) { 4518c2ecf20Sopenharmony_ci kernfs_put(target); 4528c2ecf20Sopenharmony_ci return -ENOENT; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!symlink_name) 4568c2ecf20Sopenharmony_ci symlink_name = target_name; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci link = kernfs_create_link(kobj->sd, symlink_name, entry); 4598c2ecf20Sopenharmony_ci if (PTR_ERR(link) == -EEXIST) 4608c2ecf20Sopenharmony_ci sysfs_warn_dup(kobj->sd, symlink_name); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci kernfs_put(entry); 4638c2ecf20Sopenharmony_ci kernfs_put(target); 4648c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(link); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn, 4698c2ecf20Sopenharmony_ci const struct attribute_group *grp, 4708c2ecf20Sopenharmony_ci struct iattr *newattrs) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct kernfs_node *kn; 4738c2ecf20Sopenharmony_ci int error; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (grp->attrs) { 4768c2ecf20Sopenharmony_ci struct attribute *const *attr; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci for (attr = grp->attrs; *attr; attr++) { 4798c2ecf20Sopenharmony_ci kn = kernfs_find_and_get(grp_kn, (*attr)->name); 4808c2ecf20Sopenharmony_ci if (!kn) 4818c2ecf20Sopenharmony_ci return -ENOENT; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci error = kernfs_setattr(kn, newattrs); 4848c2ecf20Sopenharmony_ci kernfs_put(kn); 4858c2ecf20Sopenharmony_ci if (error) 4868c2ecf20Sopenharmony_ci return error; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (grp->bin_attrs) { 4918c2ecf20Sopenharmony_ci struct bin_attribute *const *bin_attr; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) { 4948c2ecf20Sopenharmony_ci kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name); 4958c2ecf20Sopenharmony_ci if (!kn) 4968c2ecf20Sopenharmony_ci return -ENOENT; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci error = kernfs_setattr(kn, newattrs); 4998c2ecf20Sopenharmony_ci kernfs_put(kn); 5008c2ecf20Sopenharmony_ci if (error) 5018c2ecf20Sopenharmony_ci return error; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/** 5098c2ecf20Sopenharmony_ci * sysfs_group_change_owner - change owner of an attribute group. 5108c2ecf20Sopenharmony_ci * @kobj: The kobject containing the group. 5118c2ecf20Sopenharmony_ci * @grp: The attribute group. 5128c2ecf20Sopenharmony_ci * @kuid: new owner's kuid 5138c2ecf20Sopenharmony_ci * @kgid: new owner's kgid 5148c2ecf20Sopenharmony_ci * 5158c2ecf20Sopenharmony_ci * Returns 0 on success or error code on failure. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ciint sysfs_group_change_owner(struct kobject *kobj, 5188c2ecf20Sopenharmony_ci const struct attribute_group *grp, kuid_t kuid, 5198c2ecf20Sopenharmony_ci kgid_t kgid) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct kernfs_node *grp_kn; 5228c2ecf20Sopenharmony_ci int error; 5238c2ecf20Sopenharmony_ci struct iattr newattrs = { 5248c2ecf20Sopenharmony_ci .ia_valid = ATTR_UID | ATTR_GID, 5258c2ecf20Sopenharmony_ci .ia_uid = kuid, 5268c2ecf20Sopenharmony_ci .ia_gid = kgid, 5278c2ecf20Sopenharmony_ci }; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (!kobj->state_in_sysfs) 5308c2ecf20Sopenharmony_ci return -EINVAL; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (grp->name) { 5338c2ecf20Sopenharmony_ci grp_kn = kernfs_find_and_get(kobj->sd, grp->name); 5348c2ecf20Sopenharmony_ci } else { 5358c2ecf20Sopenharmony_ci kernfs_get(kobj->sd); 5368c2ecf20Sopenharmony_ci grp_kn = kobj->sd; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci if (!grp_kn) 5398c2ecf20Sopenharmony_ci return -ENOENT; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci error = kernfs_setattr(grp_kn, &newattrs); 5428c2ecf20Sopenharmony_ci if (!error) 5438c2ecf20Sopenharmony_ci error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci kernfs_put(grp_kn); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return error; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_group_change_owner); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci/** 5528c2ecf20Sopenharmony_ci * sysfs_groups_change_owner - change owner of a set of attribute groups. 5538c2ecf20Sopenharmony_ci * @kobj: The kobject containing the groups. 5548c2ecf20Sopenharmony_ci * @groups: The attribute groups. 5558c2ecf20Sopenharmony_ci * @kuid: new owner's kuid 5568c2ecf20Sopenharmony_ci * @kgid: new owner's kgid 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * Returns 0 on success or error code on failure. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ciint sysfs_groups_change_owner(struct kobject *kobj, 5618c2ecf20Sopenharmony_ci const struct attribute_group **groups, 5628c2ecf20Sopenharmony_ci kuid_t kuid, kgid_t kgid) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci int error = 0, i; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (!kobj->state_in_sysfs) 5678c2ecf20Sopenharmony_ci return -EINVAL; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!groups) 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci for (i = 0; groups[i]; i++) { 5738c2ecf20Sopenharmony_ci error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid); 5748c2ecf20Sopenharmony_ci if (error) 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return error; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sysfs_groups_change_owner); 581