linux/fs/sysfs/group.c
<<
>>
Prefs
   1/*
   2 * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
   3 *
   4 * Copyright (c) 2003 Patrick Mochel
   5 * Copyright (c) 2003 Open Source Development Lab
   6 *
   7 * This file is released undert the GPL v2. 
   8 *
   9 */
  10
  11#include <linux/kobject.h>
  12#include <linux/module.h>
  13#include <linux/dcache.h>
  14#include <linux/namei.h>
  15#include <linux/err.h>
  16#include "sysfs.h"
  17
  18
  19static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
  20                         const struct attribute_group *grp)
  21{
  22        struct attribute *const* attr;
  23        int i;
  24
  25        for (i = 0, attr = grp->attrs; *attr; i++, attr++)
  26                sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name);
  27}
  28
  29static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
  30                        const struct attribute_group *grp, int update)
  31{
  32        struct attribute *const* attr;
  33        int error = 0, i;
  34
  35        for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
  36                mode_t mode = 0;
  37
  38                /* in update mode, we're changing the permissions or
  39                 * visibility.  Do this by first removing then
  40                 * re-adding (if required) the file */
  41                if (update)
  42                        sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name);
  43                if (grp->is_visible) {
  44                        mode = grp->is_visible(kobj, *attr, i);
  45                        if (!mode)
  46                                continue;
  47                }
  48                error = sysfs_add_file_mode(dir_sd, *attr, SYSFS_KOBJ_ATTR,
  49                                            (*attr)->mode | mode);
  50                if (unlikely(error))
  51                        break;
  52        }
  53        if (error)
  54                remove_files(dir_sd, kobj, grp);
  55        return error;
  56}
  57
  58
  59static int internal_create_group(struct kobject *kobj, int update,
  60                                 const struct attribute_group *grp)
  61{
  62        struct sysfs_dirent *sd;
  63        int error;
  64
  65        BUG_ON(!kobj || (!update && !kobj->sd));
  66
  67        /* Updates may happen before the object has been instantiated */
  68        if (unlikely(update && !kobj->sd))
  69                return -EINVAL;
  70
  71        if (grp->name) {
  72                error = sysfs_create_subdir(kobj, grp->name, &sd);
  73                if (error)
  74                        return error;
  75        } else
  76                sd = kobj->sd;
  77        sysfs_get(sd);
  78        error = create_files(sd, kobj, grp, update);
  79        if (error) {
  80                if (grp->name)
  81                        sysfs_remove_subdir(sd);
  82        }
  83        sysfs_put(sd);
  84        return error;
  85}
  86
  87/**
  88 * sysfs_create_group - given a directory kobject, create an attribute group
  89 * @kobj:       The kobject to create the group on
  90 * @grp:        The attribute group to create
  91 *
  92 * This function creates a group for the first time.  It will explicitly
  93 * warn and error if any of the attribute files being created already exist.
  94 *
  95 * Returns 0 on success or error.
  96 */
  97int sysfs_create_group(struct kobject *kobj,
  98                       const struct attribute_group *grp)
  99{
 100        return internal_create_group(kobj, 0, grp);
 101}
 102
 103/**
 104 * sysfs_update_group - given a directory kobject, update an attribute group
 105 * @kobj:       The kobject to update the group on
 106 * @grp:        The attribute group to update
 107 *
 108 * This function updates an attribute group.  Unlike
 109 * sysfs_create_group(), it will explicitly not warn or error if any
 110 * of the attribute files being created already exist.  Furthermore,
 111 * if the visibility of the files has changed through the is_visible()
 112 * callback, it will update the permissions and add or remove the
 113 * relevant files.
 114 *
 115 * The primary use for this function is to call it after making a change
 116 * that affects group visibility.
 117 *
 118 * Returns 0 on success or error.
 119 */
 120int sysfs_update_group(struct kobject *kobj,
 121                       const struct attribute_group *grp)
 122{
 123        return internal_create_group(kobj, 1, grp);
 124}
 125
 126
 127
 128void sysfs_remove_group(struct kobject * kobj, 
 129                        const struct attribute_group * grp)
 130{
 131        struct sysfs_dirent *dir_sd = kobj->sd;
 132        struct sysfs_dirent *sd;
 133
 134        if (grp->name) {
 135                sd = sysfs_get_dirent(dir_sd, NULL, grp->name);
 136                if (!sd) {
 137                        WARN(!sd, KERN_WARNING "sysfs group %p not found for "
 138                                "kobject '%s'\n", grp, kobject_name(kobj));
 139                        return;
 140                }
 141        } else
 142                sd = sysfs_get(dir_sd);
 143
 144        remove_files(sd, kobj, grp);
 145        if (grp->name)
 146                sysfs_remove_subdir(sd);
 147
 148        sysfs_put(sd);
 149}
 150
 151/**
 152 * sysfs_merge_group - merge files into a pre-existing attribute group.
 153 * @kobj:       The kobject containing the group.
 154 * @grp:        The files to create and the attribute group they belong to.
 155 *
 156 * This function returns an error if the group doesn't exist or any of the
 157 * files already exist in that group, in which case none of the new files
 158 * are created.
 159 */
 160int sysfs_merge_group(struct kobject *kobj,
 161                       const struct attribute_group *grp)
 162{
 163        struct sysfs_dirent *dir_sd;
 164        int error = 0;
 165        struct attribute *const *attr;
 166        int i;
 167
 168        dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name);
 169        if (!dir_sd)
 170                return -ENOENT;
 171
 172        for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
 173                error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR);
 174        if (error) {
 175                while (--i >= 0)
 176                        sysfs_hash_and_remove(dir_sd, NULL, (*--attr)->name);
 177        }
 178        sysfs_put(dir_sd);
 179
 180        return error;
 181}
 182EXPORT_SYMBOL_GPL(sysfs_merge_group);
 183
 184/**
 185 * sysfs_unmerge_group - remove files from a pre-existing attribute group.
 186 * @kobj:       The kobject containing the group.
 187 * @grp:        The files to remove and the attribute group they belong to.
 188 */
 189void sysfs_unmerge_group(struct kobject *kobj,
 190                       const struct attribute_group *grp)
 191{
 192        struct sysfs_dirent *dir_sd;
 193        struct attribute *const *attr;
 194
 195        dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name);
 196        if (dir_sd) {
 197                for (attr = grp->attrs; *attr; ++attr)
 198                        sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name);
 199                sysfs_put(dir_sd);
 200        }
 201}
 202EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
 203
 204
 205EXPORT_SYMBOL_GPL(sysfs_create_group);
 206EXPORT_SYMBOL_GPL(sysfs_update_group);
 207EXPORT_SYMBOL_GPL(sysfs_remove_group);
 208