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 <linux/fs.h>
  17#include "sysfs.h"
  18
  19
  20static void remove_files(struct kernfs_node *parent,
  21                         const struct attribute_group *grp)
  22{
  23        struct attribute *const *attr;
  24        struct bin_attribute *const *bin_attr;
  25
  26        if (grp->attrs)
  27                for (attr = grp->attrs; *attr; attr++)
  28                        kernfs_remove_by_name(parent, (*attr)->name);
  29        if (grp->bin_attrs)
  30                for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
  31                        kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
  32}
  33
  34static int create_files(struct kernfs_node *parent, struct kobject *kobj,
  35                        kuid_t uid, kgid_t gid,
  36                        const struct attribute_group *grp, int update)
  37{
  38        struct attribute *const *attr;
  39        struct bin_attribute *const *bin_attr;
  40        int error = 0, i;
  41
  42        if (grp->attrs) {
  43                for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
  44                        umode_t mode = (*attr)->mode;
  45
  46                        /*
  47                         * In update mode, we're changing the permissions or
  48                         * visibility.  Do this by first removing then
  49                         * re-adding (if required) the file.
  50                         */
  51                        if (update)
  52                                kernfs_remove_by_name(parent, (*attr)->name);
  53                        if (grp->is_visible) {
  54                                mode = grp->is_visible(kobj, *attr, i);
  55                                if (!mode)
  56                                        continue;
  57                        }
  58
  59                        WARN(mode & ~(SYSFS_PREALLOC | 0664),
  60                             "Attribute %s: Invalid permissions 0%o\n",
  61                             (*attr)->name, mode);
  62
  63                        mode &= SYSFS_PREALLOC | 0664;
  64                        error = sysfs_add_file_mode_ns(parent, *attr, false,
  65                                                       mode, uid, gid, NULL);
  66                        if (unlikely(error))
  67                                break;
  68                }
  69                if (error) {
  70                        remove_files(parent, grp);
  71                        goto exit;
  72                }
  73        }
  74
  75        if (grp->bin_attrs) {
  76                for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
  77                        umode_t mode = (*bin_attr)->attr.mode;
  78
  79                        if (update)
  80                                kernfs_remove_by_name(parent,
  81                                                (*bin_attr)->attr.name);
  82                        if (grp->is_bin_visible) {
  83                                mode = grp->is_bin_visible(kobj, *bin_attr, i);
  84                                if (!mode)
  85                                        continue;
  86                        }
  87
  88                        WARN(mode & ~(SYSFS_PREALLOC | 0664),
  89                             "Attribute %s: Invalid permissions 0%o\n",
  90                             (*bin_attr)->attr.name, mode);
  91
  92                        mode &= SYSFS_PREALLOC | 0664;
  93                        error = sysfs_add_file_mode_ns(parent,
  94                                        &(*bin_attr)->attr, true,
  95                                        mode,
  96                                        uid, gid, NULL);
  97                        if (error)
  98                                break;
  99                }
 100                if (error)
 101                        remove_files(parent, grp);
 102        }
 103exit:
 104        return error;
 105}
 106
 107
 108static int internal_create_group(struct kobject *kobj, int update,
 109                                 const struct attribute_group *grp)
 110{
 111        struct kernfs_node *kn;
 112        kuid_t uid;
 113        kgid_t gid;
 114        int error;
 115
 116        if (WARN_ON(!kobj || (!update && !kobj->sd)))
 117                return -EINVAL;
 118
 119        /* Updates may happen before the object has been instantiated */
 120        if (unlikely(update && !kobj->sd))
 121                return -EINVAL;
 122        if (!grp->attrs && !grp->bin_attrs) {
 123                WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
 124                        kobj->name, grp->name ?: "");
 125                return -EINVAL;
 126        }
 127        kobject_get_ownership(kobj, &uid, &gid);
 128        if (grp->name) {
 129                if (update) {
 130                        kn = kernfs_find_and_get(kobj->sd, grp->name);
 131                        if (!kn) {
 132                                pr_warn("Can't update unknown attr grp name: %s/%s\n",
 133                                        kobj->name, grp->name);
 134                                return -EINVAL;
 135                        }
 136                } else {
 137                        kn = kernfs_create_dir_ns(kobj->sd, grp->name,
 138                                                  S_IRWXU | S_IRUGO | S_IXUGO,
 139                                                  uid, gid, kobj, NULL);
 140                        if (IS_ERR(kn)) {
 141                                if (PTR_ERR(kn) == -EEXIST)
 142                                        sysfs_warn_dup(kobj->sd, grp->name);
 143                                return PTR_ERR(kn);
 144                        }
 145                }
 146        } else
 147                kn = kobj->sd;
 148        kernfs_get(kn);
 149        error = create_files(kn, kobj, uid, gid, grp, update);
 150        if (error) {
 151                if (grp->name)
 152                        kernfs_remove(kn);
 153        }
 154        kernfs_put(kn);
 155
 156        if (grp->name && update)
 157                kernfs_put(kn);
 158
 159        return error;
 160}
 161
 162/**
 163 * sysfs_create_group - given a directory kobject, create an attribute group
 164 * @kobj:       The kobject to create the group on
 165 * @grp:        The attribute group to create
 166 *
 167 * This function creates a group for the first time.  It will explicitly
 168 * warn and error if any of the attribute files being created already exist.
 169 *
 170 * Returns 0 on success or error code on failure.
 171 */
 172int sysfs_create_group(struct kobject *kobj,
 173                       const struct attribute_group *grp)
 174{
 175        return internal_create_group(kobj, 0, grp);
 176}
 177EXPORT_SYMBOL_GPL(sysfs_create_group);
 178
 179static int internal_create_groups(struct kobject *kobj, int update,
 180                                  const struct attribute_group **groups)
 181{
 182        int error = 0;
 183        int i;
 184
 185        if (!groups)
 186                return 0;
 187
 188        for (i = 0; groups[i]; i++) {
 189                error = internal_create_group(kobj, update, groups[i]);
 190                if (error) {
 191                        while (--i >= 0)
 192                                sysfs_remove_group(kobj, groups[i]);
 193                        break;
 194                }
 195        }
 196        return error;
 197}
 198
 199/**
 200 * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
 201 * @kobj:       The kobject to create the group on
 202 * @groups:     The attribute groups to create, NULL terminated
 203 *
 204 * This function creates a bunch of attribute groups.  If an error occurs when
 205 * creating a group, all previously created groups will be removed, unwinding
 206 * everything back to the original state when this function was called.
 207 * It will explicitly warn and error if any of the attribute files being
 208 * created already exist.
 209 *
 210 * Returns 0 on success or error code from sysfs_create_group on failure.
 211 */
 212int sysfs_create_groups(struct kobject *kobj,
 213                        const struct attribute_group **groups)
 214{
 215        return internal_create_groups(kobj, 0, groups);
 216}
 217EXPORT_SYMBOL_GPL(sysfs_create_groups);
 218
 219/**
 220 * sysfs_update_groups - given a directory kobject, create a bunch of attribute groups
 221 * @kobj:       The kobject to update the group on
 222 * @groups:     The attribute groups to update, NULL terminated
 223 *
 224 * This function update a bunch of attribute groups.  If an error occurs when
 225 * updating a group, all previously updated groups will be removed together
 226 * with already existing (not updated) attributes.
 227 *
 228 * Returns 0 on success or error code from sysfs_update_group on failure.
 229 */
 230int sysfs_update_groups(struct kobject *kobj,
 231                        const struct attribute_group **groups)
 232{
 233        return internal_create_groups(kobj, 1, groups);
 234}
 235EXPORT_SYMBOL_GPL(sysfs_update_groups);
 236
 237/**
 238 * sysfs_update_group - given a directory kobject, update an attribute group
 239 * @kobj:       The kobject to update the group on
 240 * @grp:        The attribute group to update
 241 *
 242 * This function updates an attribute group.  Unlike
 243 * sysfs_create_group(), it will explicitly not warn or error if any
 244 * of the attribute files being created already exist.  Furthermore,
 245 * if the visibility of the files has changed through the is_visible()
 246 * callback, it will update the permissions and add or remove the
 247 * relevant files. Changing a group's name (subdirectory name under
 248 * kobj's directory in sysfs) is not allowed.
 249 *
 250 * The primary use for this function is to call it after making a change
 251 * that affects group visibility.
 252 *
 253 * Returns 0 on success or error code on failure.
 254 */
 255int sysfs_update_group(struct kobject *kobj,
 256                       const struct attribute_group *grp)
 257{
 258        return internal_create_group(kobj, 1, grp);
 259}
 260EXPORT_SYMBOL_GPL(sysfs_update_group);
 261
 262/**
 263 * sysfs_remove_group: remove a group from a kobject
 264 * @kobj:       kobject to remove the group from
 265 * @grp:        group to remove
 266 *
 267 * This function removes a group of attributes from a kobject.  The attributes
 268 * previously have to have been created for this group, otherwise it will fail.
 269 */
 270void sysfs_remove_group(struct kobject *kobj,
 271                        const struct attribute_group *grp)
 272{
 273        struct kernfs_node *parent = kobj->sd;
 274        struct kernfs_node *kn;
 275
 276        if (grp->name) {
 277                kn = kernfs_find_and_get(parent, grp->name);
 278                if (!kn) {
 279                        WARN(!kn, KERN_WARNING
 280                             "sysfs group '%s' not found for kobject '%s'\n",
 281                             grp->name, kobject_name(kobj));
 282                        return;
 283                }
 284        } else {
 285                kn = parent;
 286                kernfs_get(kn);
 287        }
 288
 289        remove_files(kn, grp);
 290        if (grp->name)
 291                kernfs_remove(kn);
 292
 293        kernfs_put(kn);
 294}
 295EXPORT_SYMBOL_GPL(sysfs_remove_group);
 296
 297/**
 298 * sysfs_remove_groups - remove a list of groups
 299 *
 300 * @kobj:       The kobject for the groups to be removed from
 301 * @groups:     NULL terminated list of groups to be removed
 302 *
 303 * If groups is not NULL, remove the specified groups from the kobject.
 304 */
 305void sysfs_remove_groups(struct kobject *kobj,
 306                         const struct attribute_group **groups)
 307{
 308        int i;
 309
 310        if (!groups)
 311                return;
 312        for (i = 0; groups[i]; i++)
 313                sysfs_remove_group(kobj, groups[i]);
 314}
 315EXPORT_SYMBOL_GPL(sysfs_remove_groups);
 316
 317/**
 318 * sysfs_merge_group - merge files into a pre-existing attribute group.
 319 * @kobj:       The kobject containing the group.
 320 * @grp:        The files to create and the attribute group they belong to.
 321 *
 322 * This function returns an error if the group doesn't exist or any of the
 323 * files already exist in that group, in which case none of the new files
 324 * are created.
 325 */
 326int sysfs_merge_group(struct kobject *kobj,
 327                       const struct attribute_group *grp)
 328{
 329        struct kernfs_node *parent;
 330        kuid_t uid;
 331        kgid_t gid;
 332        int error = 0;
 333        struct attribute *const *attr;
 334        int i;
 335
 336        parent = kernfs_find_and_get(kobj->sd, grp->name);
 337        if (!parent)
 338                return -ENOENT;
 339
 340        kobject_get_ownership(kobj, &uid, &gid);
 341
 342        for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
 343                error = sysfs_add_file_mode_ns(parent, *attr, false,
 344                                               (*attr)->mode, uid, gid, NULL);
 345        if (error) {
 346                while (--i >= 0)
 347                        kernfs_remove_by_name(parent, (*--attr)->name);
 348        }
 349        kernfs_put(parent);
 350
 351        return error;
 352}
 353EXPORT_SYMBOL_GPL(sysfs_merge_group);
 354
 355/**
 356 * sysfs_unmerge_group - remove files from a pre-existing attribute group.
 357 * @kobj:       The kobject containing the group.
 358 * @grp:        The files to remove and the attribute group they belong to.
 359 */
 360void sysfs_unmerge_group(struct kobject *kobj,
 361                       const struct attribute_group *grp)
 362{
 363        struct kernfs_node *parent;
 364        struct attribute *const *attr;
 365
 366        parent = kernfs_find_and_get(kobj->sd, grp->name);
 367        if (parent) {
 368                for (attr = grp->attrs; *attr; ++attr)
 369                        kernfs_remove_by_name(parent, (*attr)->name);
 370                kernfs_put(parent);
 371        }
 372}
 373EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
 374
 375/**
 376 * sysfs_add_link_to_group - add a symlink to an attribute group.
 377 * @kobj:       The kobject containing the group.
 378 * @group_name: The name of the group.
 379 * @target:     The target kobject of the symlink to create.
 380 * @link_name:  The name of the symlink to create.
 381 */
 382int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
 383                            struct kobject *target, const char *link_name)
 384{
 385        struct kernfs_node *parent;
 386        int error = 0;
 387
 388        parent = kernfs_find_and_get(kobj->sd, group_name);
 389        if (!parent)
 390                return -ENOENT;
 391
 392        error = sysfs_create_link_sd(parent, target, link_name);
 393        kernfs_put(parent);
 394
 395        return error;
 396}
 397EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
 398
 399/**
 400 * sysfs_remove_link_from_group - remove a symlink from an attribute group.
 401 * @kobj:       The kobject containing the group.
 402 * @group_name: The name of the group.
 403 * @link_name:  The name of the symlink to remove.
 404 */
 405void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
 406                                  const char *link_name)
 407{
 408        struct kernfs_node *parent;
 409
 410        parent = kernfs_find_and_get(kobj->sd, group_name);
 411        if (parent) {
 412                kernfs_remove_by_name(parent, link_name);
 413                kernfs_put(parent);
 414        }
 415}
 416EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
 417
 418/**
 419 * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
 420 * to a group or an attribute
 421 * @kobj:               The kobject containing the group.
 422 * @target_kobj:        The target kobject.
 423 * @target_name:        The name of the target group or attribute.
 424 * @symlink_name:       The name of the symlink file (target_name will be
 425 *                      considered if symlink_name is NULL).
 426 */
 427int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
 428                                         struct kobject *target_kobj,
 429                                         const char *target_name,
 430                                         const char *symlink_name)
 431{
 432        struct kernfs_node *target;
 433        struct kernfs_node *entry;
 434        struct kernfs_node *link;
 435
 436        /*
 437         * We don't own @target_kobj and it may be removed at any time.
 438         * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()
 439         * for details.
 440         */
 441        spin_lock(&sysfs_symlink_target_lock);
 442        target = target_kobj->sd;
 443        if (target)
 444                kernfs_get(target);
 445        spin_unlock(&sysfs_symlink_target_lock);
 446        if (!target)
 447                return -ENOENT;
 448
 449        entry = kernfs_find_and_get(target, target_name);
 450        if (!entry) {
 451                kernfs_put(target);
 452                return -ENOENT;
 453        }
 454
 455        if (!symlink_name)
 456                symlink_name = target_name;
 457
 458        link = kernfs_create_link(kobj->sd, symlink_name, entry);
 459        if (PTR_ERR(link) == -EEXIST)
 460                sysfs_warn_dup(kobj->sd, symlink_name);
 461
 462        kernfs_put(entry);
 463        kernfs_put(target);
 464        return PTR_ERR_OR_ZERO(link);
 465}
 466EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj);
 467
 468static int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn,
 469                                          const struct attribute_group *grp,
 470                                          struct iattr *newattrs)
 471{
 472        struct kernfs_node *kn;
 473        int error;
 474
 475        if (grp->attrs) {
 476                struct attribute *const *attr;
 477
 478                for (attr = grp->attrs; *attr; attr++) {
 479                        kn = kernfs_find_and_get(grp_kn, (*attr)->name);
 480                        if (!kn)
 481                                return -ENOENT;
 482
 483                        error = kernfs_setattr(kn, newattrs);
 484                        kernfs_put(kn);
 485                        if (error)
 486                                return error;
 487                }
 488        }
 489
 490        if (grp->bin_attrs) {
 491                struct bin_attribute *const *bin_attr;
 492
 493                for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
 494                        kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);
 495                        if (!kn)
 496                                return -ENOENT;
 497
 498                        error = kernfs_setattr(kn, newattrs);
 499                        kernfs_put(kn);
 500                        if (error)
 501                                return error;
 502                }
 503        }
 504
 505        return 0;
 506}
 507
 508/**
 509 * sysfs_group_change_owner - change owner of an attribute group.
 510 * @kobj:       The kobject containing the group.
 511 * @grp:        The attribute group.
 512 * @kuid:       new owner's kuid
 513 * @kgid:       new owner's kgid
 514 *
 515 * Returns 0 on success or error code on failure.
 516 */
 517int sysfs_group_change_owner(struct kobject *kobj,
 518                             const struct attribute_group *grp, kuid_t kuid,
 519                             kgid_t kgid)
 520{
 521        struct kernfs_node *grp_kn;
 522        int error;
 523        struct iattr newattrs = {
 524                .ia_valid = ATTR_UID | ATTR_GID,
 525                .ia_uid = kuid,
 526                .ia_gid = kgid,
 527        };
 528
 529        if (!kobj->state_in_sysfs)
 530                return -EINVAL;
 531
 532        if (grp->name) {
 533                grp_kn = kernfs_find_and_get(kobj->sd, grp->name);
 534        } else {
 535                kernfs_get(kobj->sd);
 536                grp_kn = kobj->sd;
 537        }
 538        if (!grp_kn)
 539                return -ENOENT;
 540
 541        error = kernfs_setattr(grp_kn, &newattrs);
 542        if (!error)
 543                error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs);
 544
 545        kernfs_put(grp_kn);
 546
 547        return error;
 548}
 549EXPORT_SYMBOL_GPL(sysfs_group_change_owner);
 550
 551/**
 552 * sysfs_groups_change_owner - change owner of a set of attribute groups.
 553 * @kobj:       The kobject containing the groups.
 554 * @groups:     The attribute groups.
 555 * @kuid:       new owner's kuid
 556 * @kgid:       new owner's kgid
 557 *
 558 * Returns 0 on success or error code on failure.
 559 */
 560int sysfs_groups_change_owner(struct kobject *kobj,
 561                              const struct attribute_group **groups,
 562                              kuid_t kuid, kgid_t kgid)
 563{
 564        int error = 0, i;
 565
 566        if (!kobj->state_in_sysfs)
 567                return -EINVAL;
 568
 569        if (!groups)
 570                return 0;
 571
 572        for (i = 0; groups[i]; i++) {
 573                error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);
 574                if (error)
 575                        break;
 576        }
 577
 578        return error;
 579}
 580EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);
 581