linux/fs/sysfs/group.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
   4 *
   5 * Copyright (c) 2003 Patrick Mochel
   6 * Copyright (c) 2003 Open Source Development Lab
   7 * Copyright (c) 2013 Greg Kroah-Hartman
   8 * Copyright (c) 2013 The Linux Foundation
   9 */
  10
  11#include <linux/kobject.h>
  12#include <linux/module.h>
  13#include <linux/dcache.h>
  14#include <linux/namei.h>
  15#include <linux/err.h>
  16#include "sysfs.h"
  17
  18
  19static void remove_files(struct kernfs_node *parent,
  20                         const struct attribute_group *grp)
  21{
  22        struct attribute *const *attr;
  23        struct bin_attribute *const *bin_attr;
  24
  25        if (grp->attrs)
  26                for (attr = grp->attrs; *attr; attr++)
  27                        kernfs_remove_by_name(parent, (*attr)->name);
  28        if (grp->bin_attrs)
  29                for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
  30                        kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
  31}
  32
  33static int create_files(struct kernfs_node *parent, struct kobject *kobj,
  34                        kuid_t uid, kgid_t gid,
  35                        const struct attribute_group *grp, int update)
  36{
  37        struct attribute *const *attr;
  38        struct bin_attribute *const *bin_attr;
  39        int error = 0, i;
  40
  41        if (grp->attrs) {
  42                for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
  43                        umode_t mode = (*attr)->mode;
  44
  45                        /*
  46                         * In update mode, we're changing the permissions or
  47                         * visibility.  Do this by first removing then
  48                         * re-adding (if required) the file.
  49                         */
  50                        if (update)
  51                                kernfs_remove_by_name(parent, (*attr)->name);
  52                        if (grp->is_visible) {
  53                                mode = grp->is_visible(kobj, *attr, i);
  54                                if (!mode)
  55                                        continue;
  56                        }
  57
  58                        WARN(mode & ~(SYSFS_PREALLOC | 0664),
  59                             "Attribute %s: Invalid permissions 0%o\n",
  60                             (*attr)->name, mode);
  61
  62                        mode &= SYSFS_PREALLOC | 0664;
  63                        error = sysfs_add_file_mode_ns(parent, *attr, false,
  64                                                       mode, uid, gid, NULL);
  65                        if (unlikely(error))
  66                                break;
  67                }
  68                if (error) {
  69                        remove_files(parent, grp);
  70                        goto exit;
  71                }
  72        }
  73
  74        if (grp->bin_attrs) {
  75                for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
  76                        umode_t mode = (*bin_attr)->attr.mode;
  77
  78                        if (update)
  79                                kernfs_remove_by_name(parent,
  80                                                (*bin_attr)->attr.name);
  81                        if (grp->is_bin_visible) {
  82                                mode = grp->is_bin_visible(kobj, *bin_attr, i);
  83                                if (!mode)
  84                                        continue;
  85                        }
  86
  87                        WARN(mode & ~(SYSFS_PREALLOC | 0664),
  88                             "Attribute %s: Invalid permissions 0%o\n",
  89                             (*bin_attr)->attr.name, mode);
  90
  91                        mode &= SYSFS_PREALLOC | 0664;
  92                        error = sysfs_add_file_mode_ns(parent,
  93                                        &(*bin_attr)->attr, true,
  94                                        mode,
  95                                        uid, gid, NULL);
  96                        if (error)
  97                                break;
  98                }
  99                if (error)
 100                        remove_files(parent, grp);
 101        }
 102exit:
 103        return error;
 104}
 105
 106
 107static int internal_create_group(struct kobject *kobj, int update,
 108                                 const struct attribute_group *grp)
 109{
 110        struct kernfs_node *kn;
 111        kuid_t uid;
 112        kgid_t gid;
 113        int error;
 114
 115        if (WARN_ON(!kobj || (!update && !kobj->sd)))
 116                return -EINVAL;
 117
 118        /* Updates may happen before the object has been instantiated */
 119        if (unlikely(update && !kobj->sd))
 120                return -EINVAL;
 121        if (!grp->attrs && !grp->bin_attrs) {
 122                WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
 123                        kobj->name, grp->name ?: "");
 124                return -EINVAL;
 125        }
 126        kobject_get_ownership(kobj, &uid, &gid);
 127        if (grp->name) {
 128                if (update) {
 129                        kn = kernfs_find_and_get(kobj->sd, grp->name);
 130                        if (!kn) {
 131                                pr_warn("Can't update unknown attr grp name: %s/%s\n",
 132                                        kobj->name, grp->name);
 133                                return -EINVAL;
 134                        }
 135                } else {
 136                        kn = kernfs_create_dir_ns(kobj->sd, grp->name,
 137                                                  S_IRWXU | S_IRUGO | S_IXUGO,
 138                                                  uid, gid, kobj, NULL);
 139                        if (IS_ERR(kn)) {
 140                                if (PTR_ERR(kn) == -EEXIST)
 141                                        sysfs_warn_dup(kobj->sd, grp->name);
 142                                return PTR_ERR(kn);
 143                        }
 144                }
 145        } else
 146                kn = kobj->sd;
 147        kernfs_get(kn);
 148        error = create_files(kn, kobj, uid, gid, grp, update);
 149        if (error) {
 150                if (grp->name)
 151                        kernfs_remove(kn);
 152        }
 153        kernfs_put(kn);
 154
 155        if (grp->name && update)
 156                kernfs_put(kn);
 157
 158        return error;
 159}
 160
 161/**
 162 * sysfs_create_group - given a directory kobject, create an attribute group
 163 * @kobj:       The kobject to create the group on
 164 * @grp:        The attribute group to create
 165 *
 166 * This function creates a group for the first time.  It will explicitly
 167 * warn and error if any of the attribute files being created already exist.
 168 *
 169 * Returns 0 on success or error code on failure.
 170 */
 171int sysfs_create_group(struct kobject *kobj,
 172                       const struct attribute_group *grp)
 173{
 174        return internal_create_group(kobj, 0, grp);
 175}
 176EXPORT_SYMBOL_GPL(sysfs_create_group);
 177
 178/**
 179 * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
 180 * @kobj:       The kobject to create the group on
 181 * @groups:     The attribute groups to create, NULL terminated
 182 *
 183 * This function creates a bunch of attribute groups.  If an error occurs when
 184 * creating a group, all previously created groups will be removed, unwinding
 185 * everything back to the original state when this function was called.
 186 * It will explicitly warn and error if any of the attribute files being
 187 * created already exist.
 188 *
 189 * Returns 0 on success or error code from sysfs_create_group on failure.
 190 */
 191int sysfs_create_groups(struct kobject *kobj,
 192                        const struct attribute_group **groups)
 193{
 194        int error = 0;
 195        int i;
 196
 197        if (!groups)
 198                return 0;
 199
 200        for (i = 0; groups[i]; i++) {
 201                error = sysfs_create_group(kobj, groups[i]);
 202                if (error) {
 203                        while (--i >= 0)
 204                                sysfs_remove_group(kobj, groups[i]);
 205                        break;
 206                }
 207        }
 208        return error;
 209}
 210EXPORT_SYMBOL_GPL(sysfs_create_groups);
 211
 212/**
 213 * sysfs_update_group - given a directory kobject, update an attribute group
 214 * @kobj:       The kobject to update the group on
 215 * @grp:        The attribute group to update
 216 *
 217 * This function updates an attribute group.  Unlike
 218 * sysfs_create_group(), it will explicitly not warn or error if any
 219 * of the attribute files being created already exist.  Furthermore,
 220 * if the visibility of the files has changed through the is_visible()
 221 * callback, it will update the permissions and add or remove the
 222 * relevant files. Changing a group's name (subdirectory name under
 223 * kobj's directory in sysfs) is not allowed.
 224 *
 225 * The primary use for this function is to call it after making a change
 226 * that affects group visibility.
 227 *
 228 * Returns 0 on success or error code on failure.
 229 */
 230int sysfs_update_group(struct kobject *kobj,
 231                       const struct attribute_group *grp)
 232{
 233        return internal_create_group(kobj, 1, grp);
 234}
 235EXPORT_SYMBOL_GPL(sysfs_update_group);
 236
 237/**
 238 * sysfs_remove_group: remove a group from a kobject
 239 * @kobj:       kobject to remove the group from
 240 * @grp:        group to remove
 241 *
 242 * This function removes a group of attributes from a kobject.  The attributes
 243 * previously have to have been created for this group, otherwise it will fail.
 244 */
 245void sysfs_remove_group(struct kobject *kobj,
 246                        const struct attribute_group *grp)
 247{
 248        struct kernfs_node *parent = kobj->sd;
 249        struct kernfs_node *kn;
 250
 251        if (grp->name) {
 252                kn = kernfs_find_and_get(parent, grp->name);
 253                if (!kn) {
 254                        WARN(!kn, KERN_WARNING
 255                             "sysfs group '%s' not found for kobject '%s'\n",
 256                             grp->name, kobject_name(kobj));
 257                        return;
 258                }
 259        } else {
 260                kn = parent;
 261                kernfs_get(kn);
 262        }
 263
 264        remove_files(kn, grp);
 265        if (grp->name)
 266                kernfs_remove(kn);
 267
 268        kernfs_put(kn);
 269}
 270EXPORT_SYMBOL_GPL(sysfs_remove_group);
 271
 272/**
 273 * sysfs_remove_groups - remove a list of groups
 274 *
 275 * @kobj:       The kobject for the groups to be removed from
 276 * @groups:     NULL terminated list of groups to be removed
 277 *
 278 * If groups is not NULL, remove the specified groups from the kobject.
 279 */
 280void sysfs_remove_groups(struct kobject *kobj,
 281                         const struct attribute_group **groups)
 282{
 283        int i;
 284
 285        if (!groups)
 286                return;
 287        for (i = 0; groups[i]; i++)
 288                sysfs_remove_group(kobj, groups[i]);
 289}
 290EXPORT_SYMBOL_GPL(sysfs_remove_groups);
 291
 292/**
 293 * sysfs_merge_group - merge files into a pre-existing attribute group.
 294 * @kobj:       The kobject containing the group.
 295 * @grp:        The files to create and the attribute group they belong to.
 296 *
 297 * This function returns an error if the group doesn't exist or any of the
 298 * files already exist in that group, in which case none of the new files
 299 * are created.
 300 */
 301int sysfs_merge_group(struct kobject *kobj,
 302                       const struct attribute_group *grp)
 303{
 304        struct kernfs_node *parent;
 305        kuid_t uid;
 306        kgid_t gid;
 307        int error = 0;
 308        struct attribute *const *attr;
 309        int i;
 310
 311        parent = kernfs_find_and_get(kobj->sd, grp->name);
 312        if (!parent)
 313                return -ENOENT;
 314
 315        kobject_get_ownership(kobj, &uid, &gid);
 316
 317        for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
 318                error = sysfs_add_file_mode_ns(parent, *attr, false,
 319                                               (*attr)->mode, uid, gid, NULL);
 320        if (error) {
 321                while (--i >= 0)
 322                        kernfs_remove_by_name(parent, (*--attr)->name);
 323        }
 324        kernfs_put(parent);
 325
 326        return error;
 327}
 328EXPORT_SYMBOL_GPL(sysfs_merge_group);
 329
 330/**
 331 * sysfs_unmerge_group - remove files from a pre-existing attribute group.
 332 * @kobj:       The kobject containing the group.
 333 * @grp:        The files to remove and the attribute group they belong to.
 334 */
 335void sysfs_unmerge_group(struct kobject *kobj,
 336                       const struct attribute_group *grp)
 337{
 338        struct kernfs_node *parent;
 339        struct attribute *const *attr;
 340
 341        parent = kernfs_find_and_get(kobj->sd, grp->name);
 342        if (parent) {
 343                for (attr = grp->attrs; *attr; ++attr)
 344                        kernfs_remove_by_name(parent, (*attr)->name);
 345                kernfs_put(parent);
 346        }
 347}
 348EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
 349
 350/**
 351 * sysfs_add_link_to_group - add a symlink to an attribute group.
 352 * @kobj:       The kobject containing the group.
 353 * @group_name: The name of the group.
 354 * @target:     The target kobject of the symlink to create.
 355 * @link_name:  The name of the symlink to create.
 356 */
 357int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
 358                            struct kobject *target, const char *link_name)
 359{
 360        struct kernfs_node *parent;
 361        int error = 0;
 362
 363        parent = kernfs_find_and_get(kobj->sd, group_name);
 364        if (!parent)
 365                return -ENOENT;
 366
 367        error = sysfs_create_link_sd(parent, target, link_name);
 368        kernfs_put(parent);
 369
 370        return error;
 371}
 372EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
 373
 374/**
 375 * sysfs_remove_link_from_group - remove a symlink from an attribute group.
 376 * @kobj:       The kobject containing the group.
 377 * @group_name: The name of the group.
 378 * @link_name:  The name of the symlink to remove.
 379 */
 380void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
 381                                  const char *link_name)
 382{
 383        struct kernfs_node *parent;
 384
 385        parent = kernfs_find_and_get(kobj->sd, group_name);
 386        if (parent) {
 387                kernfs_remove_by_name(parent, link_name);
 388                kernfs_put(parent);
 389        }
 390}
 391EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
 392
 393/**
 394 * __compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
 395 * to a group or an attribute
 396 * @kobj:               The kobject containing the group.
 397 * @target_kobj:        The target kobject.
 398 * @target_name:        The name of the target group or attribute.
 399 */
 400int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
 401                                      struct kobject *target_kobj,
 402                                      const char *target_name)
 403{
 404        struct kernfs_node *target;
 405        struct kernfs_node *entry;
 406        struct kernfs_node *link;
 407
 408        /*
 409         * We don't own @target_kobj and it may be removed at any time.
 410         * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()
 411         * for details.
 412         */
 413        spin_lock(&sysfs_symlink_target_lock);
 414        target = target_kobj->sd;
 415        if (target)
 416                kernfs_get(target);
 417        spin_unlock(&sysfs_symlink_target_lock);
 418        if (!target)
 419                return -ENOENT;
 420
 421        entry = kernfs_find_and_get(target_kobj->sd, target_name);
 422        if (!entry) {
 423                kernfs_put(target);
 424                return -ENOENT;
 425        }
 426
 427        link = kernfs_create_link(kobj->sd, target_name, entry);
 428        if (IS_ERR(link) && PTR_ERR(link) == -EEXIST)
 429                sysfs_warn_dup(kobj->sd, target_name);
 430
 431        kernfs_put(entry);
 432        kernfs_put(target);
 433        return PTR_ERR_OR_ZERO(link);
 434}
 435EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj);
 436