root/fs/sysfs/group.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. remove_files
  2. create_files
  3. internal_create_group
  4. sysfs_create_group
  5. internal_create_groups
  6. sysfs_create_groups
  7. sysfs_update_groups
  8. sysfs_update_group
  9. sysfs_remove_group
  10. sysfs_remove_groups
  11. sysfs_merge_group
  12. sysfs_unmerge_group
  13. sysfs_add_link_to_group
  14. sysfs_remove_link_from_group
  15. __compat_only_sysfs_link_entry_to_kobj

   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 
  19 static 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 
  33 static 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         }
 102 exit:
 103         return error;
 104 }
 105 
 106 
 107 static 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  */
 171 int sysfs_create_group(struct kobject *kobj,
 172                        const struct attribute_group *grp)
 173 {
 174         return internal_create_group(kobj, 0, grp);
 175 }
 176 EXPORT_SYMBOL_GPL(sysfs_create_group);
 177 
 178 static int internal_create_groups(struct kobject *kobj, int update,
 179                                   const struct attribute_group **groups)
 180 {
 181         int error = 0;
 182         int i;
 183 
 184         if (!groups)
 185                 return 0;
 186 
 187         for (i = 0; groups[i]; i++) {
 188                 error = internal_create_group(kobj, update, groups[i]);
 189                 if (error) {
 190                         while (--i >= 0)
 191                                 sysfs_remove_group(kobj, groups[i]);
 192                         break;
 193                 }
 194         }
 195         return error;
 196 }
 197 
 198 /**
 199  * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
 200  * @kobj:       The kobject to create the group on
 201  * @groups:     The attribute groups to create, NULL terminated
 202  *
 203  * This function creates a bunch of attribute groups.  If an error occurs when
 204  * creating a group, all previously created groups will be removed, unwinding
 205  * everything back to the original state when this function was called.
 206  * It will explicitly warn and error if any of the attribute files being
 207  * created already exist.
 208  *
 209  * Returns 0 on success or error code from sysfs_create_group on failure.
 210  */
 211 int sysfs_create_groups(struct kobject *kobj,
 212                         const struct attribute_group **groups)
 213 {
 214         return internal_create_groups(kobj, 0, groups);
 215 }
 216 EXPORT_SYMBOL_GPL(sysfs_create_groups);
 217 
 218 /**
 219  * sysfs_update_groups - given a directory kobject, create a bunch of attribute groups
 220  * @kobj:       The kobject to update the group on
 221  * @groups:     The attribute groups to update, NULL terminated
 222  *
 223  * This function update a bunch of attribute groups.  If an error occurs when
 224  * updating a group, all previously updated groups will be removed together
 225  * with already existing (not updated) attributes.
 226  *
 227  * Returns 0 on success or error code from sysfs_update_group on failure.
 228  */
 229 int sysfs_update_groups(struct kobject *kobj,
 230                         const struct attribute_group **groups)
 231 {
 232         return internal_create_groups(kobj, 1, groups);
 233 }
 234 EXPORT_SYMBOL_GPL(sysfs_update_groups);
 235 
 236 /**
 237  * sysfs_update_group - given a directory kobject, update an attribute group
 238  * @kobj:       The kobject to update the group on
 239  * @grp:        The attribute group to update
 240  *
 241  * This function updates an attribute group.  Unlike
 242  * sysfs_create_group(), it will explicitly not warn or error if any
 243  * of the attribute files being created already exist.  Furthermore,
 244  * if the visibility of the files has changed through the is_visible()
 245  * callback, it will update the permissions and add or remove the
 246  * relevant files. Changing a group's name (subdirectory name under
 247  * kobj's directory in sysfs) is not allowed.
 248  *
 249  * The primary use for this function is to call it after making a change
 250  * that affects group visibility.
 251  *
 252  * Returns 0 on success or error code on failure.
 253  */
 254 int sysfs_update_group(struct kobject *kobj,
 255                        const struct attribute_group *grp)
 256 {
 257         return internal_create_group(kobj, 1, grp);
 258 }
 259 EXPORT_SYMBOL_GPL(sysfs_update_group);
 260 
 261 /**
 262  * sysfs_remove_group: remove a group from a kobject
 263  * @kobj:       kobject to remove the group from
 264  * @grp:        group to remove
 265  *
 266  * This function removes a group of attributes from a kobject.  The attributes
 267  * previously have to have been created for this group, otherwise it will fail.
 268  */
 269 void sysfs_remove_group(struct kobject *kobj,
 270                         const struct attribute_group *grp)
 271 {
 272         struct kernfs_node *parent = kobj->sd;
 273         struct kernfs_node *kn;
 274 
 275         if (grp->name) {
 276                 kn = kernfs_find_and_get(parent, grp->name);
 277                 if (!kn) {
 278                         WARN(!kn, KERN_WARNING
 279                              "sysfs group '%s' not found for kobject '%s'\n",
 280                              grp->name, kobject_name(kobj));
 281                         return;
 282                 }
 283         } else {
 284                 kn = parent;
 285                 kernfs_get(kn);
 286         }
 287 
 288         remove_files(kn, grp);
 289         if (grp->name)
 290                 kernfs_remove(kn);
 291 
 292         kernfs_put(kn);
 293 }
 294 EXPORT_SYMBOL_GPL(sysfs_remove_group);
 295 
 296 /**
 297  * sysfs_remove_groups - remove a list of groups
 298  *
 299  * @kobj:       The kobject for the groups to be removed from
 300  * @groups:     NULL terminated list of groups to be removed
 301  *
 302  * If groups is not NULL, remove the specified groups from the kobject.
 303  */
 304 void sysfs_remove_groups(struct kobject *kobj,
 305                          const struct attribute_group **groups)
 306 {
 307         int i;
 308 
 309         if (!groups)
 310                 return;
 311         for (i = 0; groups[i]; i++)
 312                 sysfs_remove_group(kobj, groups[i]);
 313 }
 314 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
 315 
 316 /**
 317  * sysfs_merge_group - merge files into a pre-existing attribute group.
 318  * @kobj:       The kobject containing the group.
 319  * @grp:        The files to create and the attribute group they belong to.
 320  *
 321  * This function returns an error if the group doesn't exist or any of the
 322  * files already exist in that group, in which case none of the new files
 323  * are created.
 324  */
 325 int sysfs_merge_group(struct kobject *kobj,
 326                        const struct attribute_group *grp)
 327 {
 328         struct kernfs_node *parent;
 329         kuid_t uid;
 330         kgid_t gid;
 331         int error = 0;
 332         struct attribute *const *attr;
 333         int i;
 334 
 335         parent = kernfs_find_and_get(kobj->sd, grp->name);
 336         if (!parent)
 337                 return -ENOENT;
 338 
 339         kobject_get_ownership(kobj, &uid, &gid);
 340 
 341         for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
 342                 error = sysfs_add_file_mode_ns(parent, *attr, false,
 343                                                (*attr)->mode, uid, gid, NULL);
 344         if (error) {
 345                 while (--i >= 0)
 346                         kernfs_remove_by_name(parent, (*--attr)->name);
 347         }
 348         kernfs_put(parent);
 349 
 350         return error;
 351 }
 352 EXPORT_SYMBOL_GPL(sysfs_merge_group);
 353 
 354 /**
 355  * sysfs_unmerge_group - remove files from a pre-existing attribute group.
 356  * @kobj:       The kobject containing the group.
 357  * @grp:        The files to remove and the attribute group they belong to.
 358  */
 359 void sysfs_unmerge_group(struct kobject *kobj,
 360                        const struct attribute_group *grp)
 361 {
 362         struct kernfs_node *parent;
 363         struct attribute *const *attr;
 364 
 365         parent = kernfs_find_and_get(kobj->sd, grp->name);
 366         if (parent) {
 367                 for (attr = grp->attrs; *attr; ++attr)
 368                         kernfs_remove_by_name(parent, (*attr)->name);
 369                 kernfs_put(parent);
 370         }
 371 }
 372 EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
 373 
 374 /**
 375  * sysfs_add_link_to_group - add a symlink to an attribute group.
 376  * @kobj:       The kobject containing the group.
 377  * @group_name: The name of the group.
 378  * @target:     The target kobject of the symlink to create.
 379  * @link_name:  The name of the symlink to create.
 380  */
 381 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
 382                             struct kobject *target, const char *link_name)
 383 {
 384         struct kernfs_node *parent;
 385         int error = 0;
 386 
 387         parent = kernfs_find_and_get(kobj->sd, group_name);
 388         if (!parent)
 389                 return -ENOENT;
 390 
 391         error = sysfs_create_link_sd(parent, target, link_name);
 392         kernfs_put(parent);
 393 
 394         return error;
 395 }
 396 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
 397 
 398 /**
 399  * sysfs_remove_link_from_group - remove a symlink from an attribute group.
 400  * @kobj:       The kobject containing the group.
 401  * @group_name: The name of the group.
 402  * @link_name:  The name of the symlink to remove.
 403  */
 404 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
 405                                   const char *link_name)
 406 {
 407         struct kernfs_node *parent;
 408 
 409         parent = kernfs_find_and_get(kobj->sd, group_name);
 410         if (parent) {
 411                 kernfs_remove_by_name(parent, link_name);
 412                 kernfs_put(parent);
 413         }
 414 }
 415 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
 416 
 417 /**
 418  * __compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
 419  * to a group or an attribute
 420  * @kobj:               The kobject containing the group.
 421  * @target_kobj:        The target kobject.
 422  * @target_name:        The name of the target group or attribute.
 423  */
 424 int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
 425                                       struct kobject *target_kobj,
 426                                       const char *target_name)
 427 {
 428         struct kernfs_node *target;
 429         struct kernfs_node *entry;
 430         struct kernfs_node *link;
 431 
 432         /*
 433          * We don't own @target_kobj and it may be removed at any time.
 434          * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()
 435          * for details.
 436          */
 437         spin_lock(&sysfs_symlink_target_lock);
 438         target = target_kobj->sd;
 439         if (target)
 440                 kernfs_get(target);
 441         spin_unlock(&sysfs_symlink_target_lock);
 442         if (!target)
 443                 return -ENOENT;
 444 
 445         entry = kernfs_find_and_get(target_kobj->sd, target_name);
 446         if (!entry) {
 447                 kernfs_put(target);
 448                 return -ENOENT;
 449         }
 450 
 451         link = kernfs_create_link(kobj->sd, target_name, entry);
 452         if (IS_ERR(link) && PTR_ERR(link) == -EEXIST)
 453                 sysfs_warn_dup(kobj->sd, target_name);
 454 
 455         kernfs_put(entry);
 456         kernfs_put(target);
 457         return PTR_ERR_OR_ZERO(link);
 458 }
 459 EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj);

/* [<][>][^][v][top][bottom][index][help] */