Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
0004  *
0005  * Copyright (c) 2003 Patrick Mochel
0006  * Copyright (c) 2003 Open Source Development Lab
0007  * Copyright (c) 2013 Greg Kroah-Hartman
0008  * Copyright (c) 2013 The Linux Foundation
0009  */
0010 
0011 #include <linux/kobject.h>
0012 #include <linux/module.h>
0013 #include <linux/dcache.h>
0014 #include <linux/namei.h>
0015 #include <linux/err.h>
0016 #include <linux/fs.h>
0017 #include "sysfs.h"
0018 
0019 
0020 static void remove_files(struct kernfs_node *parent,
0021              const struct attribute_group *grp)
0022 {
0023     struct attribute *const *attr;
0024     struct bin_attribute *const *bin_attr;
0025 
0026     if (grp->attrs)
0027         for (attr = grp->attrs; *attr; attr++)
0028             kernfs_remove_by_name(parent, (*attr)->name);
0029     if (grp->bin_attrs)
0030         for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
0031             kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
0032 }
0033 
0034 static int create_files(struct kernfs_node *parent, struct kobject *kobj,
0035             kuid_t uid, kgid_t gid,
0036             const struct attribute_group *grp, int update)
0037 {
0038     struct attribute *const *attr;
0039     struct bin_attribute *const *bin_attr;
0040     int error = 0, i;
0041 
0042     if (grp->attrs) {
0043         for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
0044             umode_t mode = (*attr)->mode;
0045 
0046             /*
0047              * In update mode, we're changing the permissions or
0048              * visibility.  Do this by first removing then
0049              * re-adding (if required) the file.
0050              */
0051             if (update)
0052                 kernfs_remove_by_name(parent, (*attr)->name);
0053             if (grp->is_visible) {
0054                 mode = grp->is_visible(kobj, *attr, i);
0055                 if (!mode)
0056                     continue;
0057             }
0058 
0059             WARN(mode & ~(SYSFS_PREALLOC | 0664),
0060                  "Attribute %s: Invalid permissions 0%o\n",
0061                  (*attr)->name, mode);
0062 
0063             mode &= SYSFS_PREALLOC | 0664;
0064             error = sysfs_add_file_mode_ns(parent, *attr, mode, uid,
0065                                gid, NULL);
0066             if (unlikely(error))
0067                 break;
0068         }
0069         if (error) {
0070             remove_files(parent, grp);
0071             goto exit;
0072         }
0073     }
0074 
0075     if (grp->bin_attrs) {
0076         for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
0077             umode_t mode = (*bin_attr)->attr.mode;
0078 
0079             if (update)
0080                 kernfs_remove_by_name(parent,
0081                         (*bin_attr)->attr.name);
0082             if (grp->is_bin_visible) {
0083                 mode = grp->is_bin_visible(kobj, *bin_attr, i);
0084                 if (!mode)
0085                     continue;
0086             }
0087 
0088             WARN(mode & ~(SYSFS_PREALLOC | 0664),
0089                  "Attribute %s: Invalid permissions 0%o\n",
0090                  (*bin_attr)->attr.name, mode);
0091 
0092             mode &= SYSFS_PREALLOC | 0664;
0093             error = sysfs_add_bin_file_mode_ns(parent, *bin_attr,
0094                                mode, uid, gid,
0095                                NULL);
0096             if (error)
0097                 break;
0098         }
0099         if (error)
0100             remove_files(parent, grp);
0101     }
0102 exit:
0103     return error;
0104 }
0105 
0106 
0107 static int internal_create_group(struct kobject *kobj, int update,
0108                  const struct attribute_group *grp)
0109 {
0110     struct kernfs_node *kn;
0111     kuid_t uid;
0112     kgid_t gid;
0113     int error;
0114 
0115     if (WARN_ON(!kobj || (!update && !kobj->sd)))
0116         return -EINVAL;
0117 
0118     /* Updates may happen before the object has been instantiated */
0119     if (unlikely(update && !kobj->sd))
0120         return -EINVAL;
0121     if (!grp->attrs && !grp->bin_attrs) {
0122         WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
0123             kobj->name, grp->name ?: "");
0124         return -EINVAL;
0125     }
0126     kobject_get_ownership(kobj, &uid, &gid);
0127     if (grp->name) {
0128         if (update) {
0129             kn = kernfs_find_and_get(kobj->sd, grp->name);
0130             if (!kn) {
0131                 pr_warn("Can't update unknown attr grp name: %s/%s\n",
0132                     kobj->name, grp->name);
0133                 return -EINVAL;
0134             }
0135         } else {
0136             kn = kernfs_create_dir_ns(kobj->sd, grp->name,
0137                           S_IRWXU | S_IRUGO | S_IXUGO,
0138                           uid, gid, kobj, NULL);
0139             if (IS_ERR(kn)) {
0140                 if (PTR_ERR(kn) == -EEXIST)
0141                     sysfs_warn_dup(kobj->sd, grp->name);
0142                 return PTR_ERR(kn);
0143             }
0144         }
0145     } else
0146         kn = kobj->sd;
0147     kernfs_get(kn);
0148     error = create_files(kn, kobj, uid, gid, grp, update);
0149     if (error) {
0150         if (grp->name)
0151             kernfs_remove(kn);
0152     }
0153     kernfs_put(kn);
0154 
0155     if (grp->name && update)
0156         kernfs_put(kn);
0157 
0158     return error;
0159 }
0160 
0161 /**
0162  * sysfs_create_group - given a directory kobject, create an attribute group
0163  * @kobj:   The kobject to create the group on
0164  * @grp:    The attribute group to create
0165  *
0166  * This function creates a group for the first time.  It will explicitly
0167  * warn and error if any of the attribute files being created already exist.
0168  *
0169  * Returns 0 on success or error code on failure.
0170  */
0171 int sysfs_create_group(struct kobject *kobj,
0172                const struct attribute_group *grp)
0173 {
0174     return internal_create_group(kobj, 0, grp);
0175 }
0176 EXPORT_SYMBOL_GPL(sysfs_create_group);
0177 
0178 static int internal_create_groups(struct kobject *kobj, int update,
0179                   const struct attribute_group **groups)
0180 {
0181     int error = 0;
0182     int i;
0183 
0184     if (!groups)
0185         return 0;
0186 
0187     for (i = 0; groups[i]; i++) {
0188         error = internal_create_group(kobj, update, groups[i]);
0189         if (error) {
0190             while (--i >= 0)
0191                 sysfs_remove_group(kobj, groups[i]);
0192             break;
0193         }
0194     }
0195     return error;
0196 }
0197 
0198 /**
0199  * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups
0200  * @kobj:   The kobject to create the group on
0201  * @groups: The attribute groups to create, NULL terminated
0202  *
0203  * This function creates a bunch of attribute groups.  If an error occurs when
0204  * creating a group, all previously created groups will be removed, unwinding
0205  * everything back to the original state when this function was called.
0206  * It will explicitly warn and error if any of the attribute files being
0207  * created already exist.
0208  *
0209  * Returns 0 on success or error code from sysfs_create_group on failure.
0210  */
0211 int sysfs_create_groups(struct kobject *kobj,
0212             const struct attribute_group **groups)
0213 {
0214     return internal_create_groups(kobj, 0, groups);
0215 }
0216 EXPORT_SYMBOL_GPL(sysfs_create_groups);
0217 
0218 /**
0219  * sysfs_update_groups - given a directory kobject, create a bunch of attribute groups
0220  * @kobj:   The kobject to update the group on
0221  * @groups: The attribute groups to update, NULL terminated
0222  *
0223  * This function update a bunch of attribute groups.  If an error occurs when
0224  * updating a group, all previously updated groups will be removed together
0225  * with already existing (not updated) attributes.
0226  *
0227  * Returns 0 on success or error code from sysfs_update_group on failure.
0228  */
0229 int sysfs_update_groups(struct kobject *kobj,
0230             const struct attribute_group **groups)
0231 {
0232     return internal_create_groups(kobj, 1, groups);
0233 }
0234 EXPORT_SYMBOL_GPL(sysfs_update_groups);
0235 
0236 /**
0237  * sysfs_update_group - given a directory kobject, update an attribute group
0238  * @kobj:   The kobject to update the group on
0239  * @grp:    The attribute group to update
0240  *
0241  * This function updates an attribute group.  Unlike
0242  * sysfs_create_group(), it will explicitly not warn or error if any
0243  * of the attribute files being created already exist.  Furthermore,
0244  * if the visibility of the files has changed through the is_visible()
0245  * callback, it will update the permissions and add or remove the
0246  * relevant files. Changing a group's name (subdirectory name under
0247  * kobj's directory in sysfs) is not allowed.
0248  *
0249  * The primary use for this function is to call it after making a change
0250  * that affects group visibility.
0251  *
0252  * Returns 0 on success or error code on failure.
0253  */
0254 int sysfs_update_group(struct kobject *kobj,
0255                const struct attribute_group *grp)
0256 {
0257     return internal_create_group(kobj, 1, grp);
0258 }
0259 EXPORT_SYMBOL_GPL(sysfs_update_group);
0260 
0261 /**
0262  * sysfs_remove_group: remove a group from a kobject
0263  * @kobj:   kobject to remove the group from
0264  * @grp:    group to remove
0265  *
0266  * This function removes a group of attributes from a kobject.  The attributes
0267  * previously have to have been created for this group, otherwise it will fail.
0268  */
0269 void sysfs_remove_group(struct kobject *kobj,
0270             const struct attribute_group *grp)
0271 {
0272     struct kernfs_node *parent = kobj->sd;
0273     struct kernfs_node *kn;
0274 
0275     if (grp->name) {
0276         kn = kernfs_find_and_get(parent, grp->name);
0277         if (!kn) {
0278             WARN(!kn, KERN_WARNING
0279                  "sysfs group '%s' not found for kobject '%s'\n",
0280                  grp->name, kobject_name(kobj));
0281             return;
0282         }
0283     } else {
0284         kn = parent;
0285         kernfs_get(kn);
0286     }
0287 
0288     remove_files(kn, grp);
0289     if (grp->name)
0290         kernfs_remove(kn);
0291 
0292     kernfs_put(kn);
0293 }
0294 EXPORT_SYMBOL_GPL(sysfs_remove_group);
0295 
0296 /**
0297  * sysfs_remove_groups - remove a list of groups
0298  *
0299  * @kobj:   The kobject for the groups to be removed from
0300  * @groups: NULL terminated list of groups to be removed
0301  *
0302  * If groups is not NULL, remove the specified groups from the kobject.
0303  */
0304 void sysfs_remove_groups(struct kobject *kobj,
0305              const struct attribute_group **groups)
0306 {
0307     int i;
0308 
0309     if (!groups)
0310         return;
0311     for (i = 0; groups[i]; i++)
0312         sysfs_remove_group(kobj, groups[i]);
0313 }
0314 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
0315 
0316 /**
0317  * sysfs_merge_group - merge files into a pre-existing attribute group.
0318  * @kobj:   The kobject containing the group.
0319  * @grp:    The files to create and the attribute group they belong to.
0320  *
0321  * This function returns an error if the group doesn't exist or any of the
0322  * files already exist in that group, in which case none of the new files
0323  * are created.
0324  */
0325 int sysfs_merge_group(struct kobject *kobj,
0326                const struct attribute_group *grp)
0327 {
0328     struct kernfs_node *parent;
0329     kuid_t uid;
0330     kgid_t gid;
0331     int error = 0;
0332     struct attribute *const *attr;
0333     int i;
0334 
0335     parent = kernfs_find_and_get(kobj->sd, grp->name);
0336     if (!parent)
0337         return -ENOENT;
0338 
0339     kobject_get_ownership(kobj, &uid, &gid);
0340 
0341     for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
0342         error = sysfs_add_file_mode_ns(parent, *attr, (*attr)->mode,
0343                            uid, gid, NULL);
0344     if (error) {
0345         while (--i >= 0)
0346             kernfs_remove_by_name(parent, (*--attr)->name);
0347     }
0348     kernfs_put(parent);
0349 
0350     return error;
0351 }
0352 EXPORT_SYMBOL_GPL(sysfs_merge_group);
0353 
0354 /**
0355  * sysfs_unmerge_group - remove files from a pre-existing attribute group.
0356  * @kobj:   The kobject containing the group.
0357  * @grp:    The files to remove and the attribute group they belong to.
0358  */
0359 void sysfs_unmerge_group(struct kobject *kobj,
0360                const struct attribute_group *grp)
0361 {
0362     struct kernfs_node *parent;
0363     struct attribute *const *attr;
0364 
0365     parent = kernfs_find_and_get(kobj->sd, grp->name);
0366     if (parent) {
0367         for (attr = grp->attrs; *attr; ++attr)
0368             kernfs_remove_by_name(parent, (*attr)->name);
0369         kernfs_put(parent);
0370     }
0371 }
0372 EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
0373 
0374 /**
0375  * sysfs_add_link_to_group - add a symlink to an attribute group.
0376  * @kobj:   The kobject containing the group.
0377  * @group_name: The name of the group.
0378  * @target: The target kobject of the symlink to create.
0379  * @link_name:  The name of the symlink to create.
0380  */
0381 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
0382                 struct kobject *target, const char *link_name)
0383 {
0384     struct kernfs_node *parent;
0385     int error = 0;
0386 
0387     parent = kernfs_find_and_get(kobj->sd, group_name);
0388     if (!parent)
0389         return -ENOENT;
0390 
0391     error = sysfs_create_link_sd(parent, target, link_name);
0392     kernfs_put(parent);
0393 
0394     return error;
0395 }
0396 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
0397 
0398 /**
0399  * sysfs_remove_link_from_group - remove a symlink from an attribute group.
0400  * @kobj:   The kobject containing the group.
0401  * @group_name: The name of the group.
0402  * @link_name:  The name of the symlink to remove.
0403  */
0404 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
0405                   const char *link_name)
0406 {
0407     struct kernfs_node *parent;
0408 
0409     parent = kernfs_find_and_get(kobj->sd, group_name);
0410     if (parent) {
0411         kernfs_remove_by_name(parent, link_name);
0412         kernfs_put(parent);
0413     }
0414 }
0415 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
0416 
0417 /**
0418  * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing
0419  * to a group or an attribute
0420  * @kobj:       The kobject containing the group.
0421  * @target_kobj:    The target kobject.
0422  * @target_name:    The name of the target group or attribute.
0423  * @symlink_name:   The name of the symlink file (target_name will be
0424  *          considered if symlink_name is NULL).
0425  */
0426 int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
0427                      struct kobject *target_kobj,
0428                      const char *target_name,
0429                      const char *symlink_name)
0430 {
0431     struct kernfs_node *target;
0432     struct kernfs_node *entry;
0433     struct kernfs_node *link;
0434 
0435     /*
0436      * We don't own @target_kobj and it may be removed at any time.
0437      * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir()
0438      * for details.
0439      */
0440     spin_lock(&sysfs_symlink_target_lock);
0441     target = target_kobj->sd;
0442     if (target)
0443         kernfs_get(target);
0444     spin_unlock(&sysfs_symlink_target_lock);
0445     if (!target)
0446         return -ENOENT;
0447 
0448     entry = kernfs_find_and_get(target, target_name);
0449     if (!entry) {
0450         kernfs_put(target);
0451         return -ENOENT;
0452     }
0453 
0454     if (!symlink_name)
0455         symlink_name = target_name;
0456 
0457     link = kernfs_create_link(kobj->sd, symlink_name, entry);
0458     if (PTR_ERR(link) == -EEXIST)
0459         sysfs_warn_dup(kobj->sd, symlink_name);
0460 
0461     kernfs_put(entry);
0462     kernfs_put(target);
0463     return PTR_ERR_OR_ZERO(link);
0464 }
0465 EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj);
0466 
0467 static int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn,
0468                       const struct attribute_group *grp,
0469                       struct iattr *newattrs)
0470 {
0471     struct kernfs_node *kn;
0472     int error;
0473 
0474     if (grp->attrs) {
0475         struct attribute *const *attr;
0476 
0477         for (attr = grp->attrs; *attr; attr++) {
0478             kn = kernfs_find_and_get(grp_kn, (*attr)->name);
0479             if (!kn)
0480                 return -ENOENT;
0481 
0482             error = kernfs_setattr(kn, newattrs);
0483             kernfs_put(kn);
0484             if (error)
0485                 return error;
0486         }
0487     }
0488 
0489     if (grp->bin_attrs) {
0490         struct bin_attribute *const *bin_attr;
0491 
0492         for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
0493             kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);
0494             if (!kn)
0495                 return -ENOENT;
0496 
0497             error = kernfs_setattr(kn, newattrs);
0498             kernfs_put(kn);
0499             if (error)
0500                 return error;
0501         }
0502     }
0503 
0504     return 0;
0505 }
0506 
0507 /**
0508  * sysfs_group_change_owner - change owner of an attribute group.
0509  * @kobj:   The kobject containing the group.
0510  * @grp:    The attribute group.
0511  * @kuid:   new owner's kuid
0512  * @kgid:   new owner's kgid
0513  *
0514  * Returns 0 on success or error code on failure.
0515  */
0516 int sysfs_group_change_owner(struct kobject *kobj,
0517                  const struct attribute_group *grp, kuid_t kuid,
0518                  kgid_t kgid)
0519 {
0520     struct kernfs_node *grp_kn;
0521     int error;
0522     struct iattr newattrs = {
0523         .ia_valid = ATTR_UID | ATTR_GID,
0524         .ia_uid = kuid,
0525         .ia_gid = kgid,
0526     };
0527 
0528     if (!kobj->state_in_sysfs)
0529         return -EINVAL;
0530 
0531     if (grp->name) {
0532         grp_kn = kernfs_find_and_get(kobj->sd, grp->name);
0533     } else {
0534         kernfs_get(kobj->sd);
0535         grp_kn = kobj->sd;
0536     }
0537     if (!grp_kn)
0538         return -ENOENT;
0539 
0540     error = kernfs_setattr(grp_kn, &newattrs);
0541     if (!error)
0542         error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs);
0543 
0544     kernfs_put(grp_kn);
0545 
0546     return error;
0547 }
0548 EXPORT_SYMBOL_GPL(sysfs_group_change_owner);
0549 
0550 /**
0551  * sysfs_groups_change_owner - change owner of a set of attribute groups.
0552  * @kobj:   The kobject containing the groups.
0553  * @groups: The attribute groups.
0554  * @kuid:   new owner's kuid
0555  * @kgid:   new owner's kgid
0556  *
0557  * Returns 0 on success or error code on failure.
0558  */
0559 int sysfs_groups_change_owner(struct kobject *kobj,
0560                   const struct attribute_group **groups,
0561                   kuid_t kuid, kgid_t kgid)
0562 {
0563     int error = 0, i;
0564 
0565     if (!kobj->state_in_sysfs)
0566         return -EINVAL;
0567 
0568     if (!groups)
0569         return 0;
0570 
0571     for (i = 0; groups[i]; i++) {
0572         error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);
0573         if (error)
0574             break;
0575     }
0576 
0577     return error;
0578 }
0579 EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);