LCOV - code coverage report
Current view: top level - fs/sysfs - group.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 102 167 61.1 %
Date: 2022-04-01 14:17:54 Functions: 9 14 64.3 %
Branches: 68 118 57.6 %

           Branch data     Line data    Source code
       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                 :      13574 : 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                 :      13574 :         struct attribute *const *attr;
      38                 :      13574 :         struct bin_attribute *const *bin_attr;
      39                 :      13574 :         int error = 0, i;
      40                 :            : 
      41         [ +  + ]:      13574 :         if (grp->attrs) {
      42   [ +  +  +  - ]:     134530 :                 for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
      43                 :     120989 :                         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         [ +  + ]:     120989 :                         if (update)
      51                 :         99 :                                 kernfs_remove_by_name(parent, (*attr)->name);
      52         [ +  + ]:     120989 :                         if (grp->is_visible) {
      53                 :      10032 :                                 mode = grp->is_visible(kobj, *attr, i);
      54         [ +  + ]:      10032 :                                 if (!mode)
      55                 :       2321 :                                         continue;
      56                 :            :                         }
      57                 :            : 
      58         [ -  + ]:     118668 :                         WARN(mode & ~(SYSFS_PREALLOC | 0664),
      59                 :            :                              "Attribute %s: Invalid permissions 0%o\n",
      60                 :            :                              (*attr)->name, mode);
      61                 :            : 
      62                 :     118668 :                         mode &= SYSFS_PREALLOC | 0664;
      63                 :     118668 :                         error = sysfs_add_file_mode_ns(parent, *attr, false,
      64                 :            :                                                        mode, uid, gid, NULL);
      65         [ +  - ]:     118668 :                         if (unlikely(error))
      66                 :            :                                 break;
      67                 :            :                 }
      68         [ -  + ]:      13541 :                 if (error) {
      69                 :          0 :                         remove_files(parent, grp);
      70                 :          0 :                         goto exit;
      71                 :            :                 }
      72                 :            :         }
      73                 :            : 
      74         [ +  + ]:      13574 :         if (grp->bin_attrs) {
      75         [ +  + ]:        308 :                 for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
      76                 :        220 :                         umode_t mode = (*bin_attr)->attr.mode;
      77                 :            : 
      78         [ -  + ]:        220 :                         if (update)
      79                 :          0 :                                 kernfs_remove_by_name(parent,
      80                 :            :                                                 (*bin_attr)->attr.name);
      81         [ +  + ]:        220 :                         if (grp->is_bin_visible) {
      82                 :        165 :                                 mode = grp->is_bin_visible(kobj, *bin_attr, i);
      83         [ +  + ]:        165 :                                 if (!mode)
      84                 :         22 :                                         continue;
      85                 :            :                         }
      86                 :            : 
      87         [ -  + ]:        198 :                         WARN(mode & ~(SYSFS_PREALLOC | 0664),
      88                 :            :                              "Attribute %s: Invalid permissions 0%o\n",
      89                 :            :                              (*bin_attr)->attr.name, mode);
      90                 :            : 
      91                 :        198 :                         mode &= SYSFS_PREALLOC | 0664;
      92                 :        198 :                         error = sysfs_add_file_mode_ns(parent,
      93                 :        198 :                                         &(*bin_attr)->attr, true,
      94                 :            :                                         mode,
      95                 :            :                                         uid, gid, NULL);
      96         [ +  - ]:        198 :                         if (error)
      97                 :            :                                 break;
      98                 :            :                 }
      99         [ +  - ]:         88 :                 if (error)
     100                 :          0 :                         remove_files(parent, grp);
     101                 :            :         }
     102                 :      13574 : exit:
     103                 :      13574 :         return error;
     104                 :            : }
     105                 :            : 
     106                 :            : 
     107                 :      13574 : static int internal_create_group(struct kobject *kobj, int update,
     108                 :            :                                  const struct attribute_group *grp)
     109                 :            : {
     110                 :      13574 :         struct kernfs_node *kn;
     111                 :      13574 :         kuid_t uid;
     112                 :      13574 :         kgid_t gid;
     113                 :      13574 :         int error;
     114                 :            : 
     115   [ +  -  +  +  :      27148 :         if (WARN_ON(!kobj || (!update && !kobj->sd)))
          +  -  -  +  +  
                      - ]
     116                 :            :                 return -EINVAL;
     117                 :            : 
     118                 :            :         /* Updates may happen before the object has been instantiated */
     119   [ +  +  +  - ]:      13574 :         if (unlikely(update && !kobj->sd))
     120                 :            :                 return -EINVAL;
     121   [ +  +  -  + ]:      13574 :         if (!grp->attrs && !grp->bin_attrs) {
     122         [ #  # ]:          0 :                 WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
     123                 :            :                         kobj->name, grp->name ?: "");
     124                 :          0 :                 return -EINVAL;
     125                 :            :         }
     126                 :      13574 :         kobject_get_ownership(kobj, &uid, &gid);
     127         [ +  + ]:      13574 :         if (grp->name) {
     128         [ +  + ]:       8173 :                 if (update) {
     129                 :         77 :                         kn = kernfs_find_and_get(kobj->sd, grp->name);
     130         [ -  + ]:         77 :                         if (!kn) {
     131                 :          0 :                                 pr_warn("Can't update unknown attr grp name: %s/%s\n",
     132                 :            :                                         kobj->name, grp->name);
     133                 :          0 :                                 return -EINVAL;
     134                 :            :                         }
     135                 :            :                 } else {
     136                 :       8096 :                         kn = kernfs_create_dir_ns(kobj->sd, grp->name,
     137                 :            :                                                   S_IRWXU | S_IRUGO | S_IXUGO,
     138                 :            :                                                   uid, gid, kobj, NULL);
     139         [ -  + ]:       8096 :                         if (IS_ERR(kn)) {
     140         [ #  # ]:          0 :                                 if (PTR_ERR(kn) == -EEXIST)
     141                 :          0 :                                         sysfs_warn_dup(kobj->sd, grp->name);
     142                 :          0 :                                 return PTR_ERR(kn);
     143                 :            :                         }
     144                 :            :                 }
     145                 :            :         } else
     146                 :       5401 :                 kn = kobj->sd;
     147                 :      13574 :         kernfs_get(kn);
     148                 :      13574 :         error = create_files(kn, kobj, uid, gid, grp, update);
     149         [ -  + ]:      13574 :         if (error) {
     150         [ #  # ]:          0 :                 if (grp->name)
     151                 :          0 :                         kernfs_remove(kn);
     152                 :            :         }
     153                 :      13574 :         kernfs_put(kn);
     154                 :            : 
     155   [ +  +  +  + ]:      13574 :         if (grp->name && update)
     156                 :         77 :                 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                 :      10571 : int sysfs_create_group(struct kobject *kobj,
     172                 :            :                        const struct attribute_group *grp)
     173                 :            : {
     174                 :      10571 :         return internal_create_group(kobj, 0, grp);
     175                 :            : }
     176                 :            : EXPORT_SYMBOL_GPL(sysfs_create_group);
     177                 :            : 
     178                 :      23265 : static int internal_create_groups(struct kobject *kobj, int update,
     179                 :            :                                   const struct attribute_group **groups)
     180                 :            : {
     181                 :      23265 :         int error = 0;
     182                 :      23265 :         int i;
     183                 :            : 
     184         [ +  + ]:      23265 :         if (!groups)
     185                 :            :                 return 0;
     186                 :            : 
     187         [ +  + ]:       5346 :         for (i = 0; groups[i]; i++) {
     188                 :       3003 :                 error = internal_create_group(kobj, update, groups[i]);
     189         [ -  + ]:       3003 :                 if (error) {
     190         [ #  # ]:          0 :                         while (--i >= 0)
     191                 :          0 :                                 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                 :      23254 : int sysfs_create_groups(struct kobject *kobj,
     212                 :            :                         const struct attribute_group **groups)
     213                 :            : {
     214                 :      23254 :         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                 :         11 : int sysfs_update_groups(struct kobject *kobj,
     230                 :            :                         const struct attribute_group **groups)
     231                 :            : {
     232                 :         11 :         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                 :          0 : int sysfs_update_group(struct kobject *kobj,
     255                 :            :                        const struct attribute_group *grp)
     256                 :            : {
     257                 :          0 :         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                 :       3234 : void sysfs_remove_group(struct kobject *kobj,
     270                 :            :                         const struct attribute_group *grp)
     271                 :            : {
     272                 :       3234 :         struct kernfs_node *parent = kobj->sd;
     273                 :       3234 :         struct kernfs_node *kn;
     274                 :            : 
     275         [ +  + ]:       3234 :         if (grp->name) {
     276                 :       3212 :                 kn = kernfs_find_and_get(parent, grp->name);
     277         [ -  + ]:       3212 :                 if (!kn) {
     278                 :          0 :                         WARN(!kn, KERN_WARNING
     279                 :            :                              "sysfs group '%s' not found for kobject '%s'\n",
     280                 :            :                              grp->name, kobject_name(kobj));
     281                 :          0 :                         return;
     282                 :            :                 }
     283                 :            :         } else {
     284                 :         22 :                 kn = parent;
     285                 :         22 :                 kernfs_get(kn);
     286                 :            :         }
     287                 :            : 
     288                 :       3234 :         remove_files(kn, grp);
     289         [ +  + ]:       3234 :         if (grp->name)
     290                 :       3212 :                 kernfs_remove(kn);
     291                 :            : 
     292                 :       3234 :         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                 :        143 : void sysfs_remove_groups(struct kobject *kobj,
     305                 :            :                          const struct attribute_group **groups)
     306                 :            : {
     307                 :        143 :         int i;
     308                 :            : 
     309         [ +  + ]:        143 :         if (!groups)
     310                 :            :                 return;
     311         [ +  + ]:         44 :         for (i = 0; groups[i]; i++)
     312                 :         22 :                 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                 :       3300 : int sysfs_merge_group(struct kobject *kobj,
     326                 :            :                        const struct attribute_group *grp)
     327                 :            : {
     328                 :       3300 :         struct kernfs_node *parent;
     329                 :       3300 :         kuid_t uid;
     330                 :       3300 :         kgid_t gid;
     331                 :       3300 :         int error = 0;
     332                 :       3300 :         struct attribute *const *attr;
     333                 :       3300 :         int i;
     334                 :            : 
     335                 :       3300 :         parent = kernfs_find_and_get(kobj->sd, grp->name);
     336         [ +  - ]:       3300 :         if (!parent)
     337                 :            :                 return -ENOENT;
     338                 :            : 
     339                 :       3300 :         kobject_get_ownership(kobj, &uid, &gid);
     340                 :            : 
     341   [ +  +  +  - ]:      20152 :         for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
     342                 :      16852 :                 error = sysfs_add_file_mode_ns(parent, *attr, false,
     343                 :      16852 :                                                (*attr)->mode, uid, gid, NULL);
     344         [ -  + ]:       3300 :         if (error) {
     345         [ #  # ]:          0 :                 while (--i >= 0)
     346                 :          0 :                         kernfs_remove_by_name(parent, (*--attr)->name);
     347                 :            :         }
     348                 :       3300 :         kernfs_put(parent);
     349                 :            : 
     350                 :       3300 :         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                 :          0 : void sysfs_unmerge_group(struct kobject *kobj,
     360                 :            :                        const struct attribute_group *grp)
     361                 :            : {
     362                 :          0 :         struct kernfs_node *parent;
     363                 :          0 :         struct attribute *const *attr;
     364                 :            : 
     365                 :          0 :         parent = kernfs_find_and_get(kobj->sd, grp->name);
     366         [ #  # ]:          0 :         if (parent) {
     367         [ #  # ]:          0 :                 for (attr = grp->attrs; *attr; ++attr)
     368                 :          0 :                         kernfs_remove_by_name(parent, (*attr)->name);
     369                 :          0 :                 kernfs_put(parent);
     370                 :            :         }
     371                 :          0 : }
     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                 :          0 : int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
     382                 :            :                             struct kobject *target, const char *link_name)
     383                 :            : {
     384                 :          0 :         struct kernfs_node *parent;
     385                 :          0 :         int error = 0;
     386                 :            : 
     387                 :          0 :         parent = kernfs_find_and_get(kobj->sd, group_name);
     388         [ #  # ]:          0 :         if (!parent)
     389                 :            :                 return -ENOENT;
     390                 :            : 
     391                 :          0 :         error = sysfs_create_link_sd(parent, target, link_name);
     392                 :          0 :         kernfs_put(parent);
     393                 :            : 
     394                 :          0 :         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                 :          0 : void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
     405                 :            :                                   const char *link_name)
     406                 :            : {
     407                 :          0 :         struct kernfs_node *parent;
     408                 :            : 
     409                 :          0 :         parent = kernfs_find_and_get(kobj->sd, group_name);
     410         [ #  # ]:          0 :         if (parent) {
     411                 :          0 :                 kernfs_remove_by_name(parent, link_name);
     412                 :          0 :                 kernfs_put(parent);
     413                 :            :         }
     414                 :          0 : }
     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                 :          0 : int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
     425                 :            :                                       struct kobject *target_kobj,
     426                 :            :                                       const char *target_name)
     427                 :            : {
     428                 :          0 :         struct kernfs_node *target;
     429                 :          0 :         struct kernfs_node *entry;
     430                 :          0 :         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                 :          0 :         spin_lock(&sysfs_symlink_target_lock);
     438                 :          0 :         target = target_kobj->sd;
     439         [ #  # ]:          0 :         if (target)
     440                 :          0 :                 kernfs_get(target);
     441                 :          0 :         spin_unlock(&sysfs_symlink_target_lock);
     442         [ #  # ]:          0 :         if (!target)
     443                 :            :                 return -ENOENT;
     444                 :            : 
     445                 :          0 :         entry = kernfs_find_and_get(target_kobj->sd, target_name);
     446         [ #  # ]:          0 :         if (!entry) {
     447                 :          0 :                 kernfs_put(target);
     448                 :          0 :                 return -ENOENT;
     449                 :            :         }
     450                 :            : 
     451                 :          0 :         link = kernfs_create_link(kobj->sd, target_name, entry);
     452         [ #  # ]:          0 :         if (PTR_ERR(link) == -EEXIST)
     453                 :          0 :                 sysfs_warn_dup(kobj->sd, target_name);
     454                 :            : 
     455                 :          0 :         kernfs_put(entry);
     456                 :          0 :         kernfs_put(target);
     457         [ #  # ]:          0 :         return PTR_ERR_OR_ZERO(link);
     458                 :            : }
     459                 :            : EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj);

Generated by: LCOV version 1.14