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 19 static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, 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 sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); 28 if (grp->bin_attrs) 29 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) 30 sysfs_remove_bin_file(kobj, *bin_attr); 31 } 32 33 static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, 34 const struct attribute_group *grp, int update) 35 { 36 struct attribute *const* attr; 37 struct bin_attribute *const* bin_attr; 38 int error = 0, i; 39 40 if (grp->attrs) { 41 for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) { 42 umode_t mode = 0; 43 44 /* 45 * In update mode, we're changing the permissions or 46 * visibility. Do this by first removing then 47 * re-adding (if required) the file. 48 */ 49 if (update) 50 sysfs_hash_and_remove(dir_sd, NULL, 51 (*attr)->name); 52 if (grp->is_visible) { 53 mode = grp->is_visible(kobj, *attr, i); 54 if (!mode) 55 continue; 56 } 57 error = sysfs_add_file_mode(dir_sd, *attr, 58 SYSFS_KOBJ_ATTR, 59 (*attr)->mode | mode); 60 if (unlikely(error)) 61 break; 62 } 63 if (error) { 64 remove_files(dir_sd, kobj, grp); 65 goto exit; 66 } 67 } 68 69 if (grp->bin_attrs) { 70 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) { 71 if (update) 72 sysfs_remove_bin_file(kobj, *bin_attr); 73 error = sysfs_create_bin_file(kobj, *bin_attr); 74 if (error) 75 break; 76 } 77 if (error) 78 remove_files(dir_sd, kobj, grp); 79 } 80 exit: 81 return error; 82 } 83 84 85 static int internal_create_group(struct kobject *kobj, int update, 86 const struct attribute_group *grp) 87 { 88 struct sysfs_dirent *sd; 89 int error; 90 91 BUG_ON(!kobj || (!update && !kobj->sd)); 92 93 /* Updates may happen before the object has been instantiated */ 94 if (unlikely(update && !kobj->sd)) 95 return -EINVAL; 96 if (!grp->attrs && !grp->bin_attrs) { 97 WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n", 98 kobj->name, grp->name ? "" : grp->name); 99 return -EINVAL; 100 } 101 if (grp->name) { 102 error = sysfs_create_subdir(kobj, grp->name, &sd); 103 if (error) 104 return error; 105 } else 106 sd = kobj->sd; 107 sysfs_get(sd); 108 error = create_files(sd, kobj, grp, update); 109 if (error) { 110 if (grp->name) 111 sysfs_remove_subdir(sd); 112 } 113 sysfs_put(sd); 114 return error; 115 } 116 117 /** 118 * sysfs_create_group - given a directory kobject, create an attribute group 119 * @kobj: The kobject to create the group on 120 * @grp: The attribute group to create 121 * 122 * This function creates a group for the first time. It will explicitly 123 * warn and error if any of the attribute files being created already exist. 124 * 125 * Returns 0 on success or error. 126 */ 127 int sysfs_create_group(struct kobject *kobj, 128 const struct attribute_group *grp) 129 { 130 return internal_create_group(kobj, 0, grp); 131 } 132 133 /** 134 * sysfs_update_group - given a directory kobject, update an attribute group 135 * @kobj: The kobject to update the group on 136 * @grp: The attribute group to update 137 * 138 * This function updates an attribute group. Unlike 139 * sysfs_create_group(), it will explicitly not warn or error if any 140 * of the attribute files being created already exist. Furthermore, 141 * if the visibility of the files has changed through the is_visible() 142 * callback, it will update the permissions and add or remove the 143 * relevant files. 144 * 145 * The primary use for this function is to call it after making a change 146 * that affects group visibility. 147 * 148 * Returns 0 on success or error. 149 */ 150 int sysfs_update_group(struct kobject *kobj, 151 const struct attribute_group *grp) 152 { 153 return internal_create_group(kobj, 1, grp); 154 } 155 156 157 158 void sysfs_remove_group(struct kobject * kobj, 159 const struct attribute_group * grp) 160 { 161 struct sysfs_dirent *dir_sd = kobj->sd; 162 struct sysfs_dirent *sd; 163 164 if (grp->name) { 165 sd = sysfs_get_dirent(dir_sd, NULL, grp->name); 166 if (!sd) { 167 WARN(!sd, KERN_WARNING "sysfs group %p not found for " 168 "kobject '%s'\n", grp, kobject_name(kobj)); 169 return; 170 } 171 } else 172 sd = sysfs_get(dir_sd); 173 174 remove_files(sd, kobj, grp); 175 if (grp->name) 176 sysfs_remove_subdir(sd); 177 178 sysfs_put(sd); 179 } 180 181 /** 182 * sysfs_merge_group - merge files into a pre-existing attribute group. 183 * @kobj: The kobject containing the group. 184 * @grp: The files to create and the attribute group they belong to. 185 * 186 * This function returns an error if the group doesn't exist or any of the 187 * files already exist in that group, in which case none of the new files 188 * are created. 189 */ 190 int sysfs_merge_group(struct kobject *kobj, 191 const struct attribute_group *grp) 192 { 193 struct sysfs_dirent *dir_sd; 194 int error = 0; 195 struct attribute *const *attr; 196 int i; 197 198 dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); 199 if (!dir_sd) 200 return -ENOENT; 201 202 for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) 203 error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); 204 if (error) { 205 while (--i >= 0) 206 sysfs_hash_and_remove(dir_sd, NULL, (*--attr)->name); 207 } 208 sysfs_put(dir_sd); 209 210 return error; 211 } 212 EXPORT_SYMBOL_GPL(sysfs_merge_group); 213 214 /** 215 * sysfs_unmerge_group - remove files from a pre-existing attribute group. 216 * @kobj: The kobject containing the group. 217 * @grp: The files to remove and the attribute group they belong to. 218 */ 219 void sysfs_unmerge_group(struct kobject *kobj, 220 const struct attribute_group *grp) 221 { 222 struct sysfs_dirent *dir_sd; 223 struct attribute *const *attr; 224 225 dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); 226 if (dir_sd) { 227 for (attr = grp->attrs; *attr; ++attr) 228 sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); 229 sysfs_put(dir_sd); 230 } 231 } 232 EXPORT_SYMBOL_GPL(sysfs_unmerge_group); 233 234 /** 235 * sysfs_add_link_to_group - add a symlink to an attribute group. 236 * @kobj: The kobject containing the group. 237 * @group_name: The name of the group. 238 * @target: The target kobject of the symlink to create. 239 * @link_name: The name of the symlink to create. 240 */ 241 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, 242 struct kobject *target, const char *link_name) 243 { 244 struct sysfs_dirent *dir_sd; 245 int error = 0; 246 247 dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); 248 if (!dir_sd) 249 return -ENOENT; 250 251 error = sysfs_create_link_sd(dir_sd, target, link_name); 252 sysfs_put(dir_sd); 253 254 return error; 255 } 256 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group); 257 258 /** 259 * sysfs_remove_link_from_group - remove a symlink from an attribute group. 260 * @kobj: The kobject containing the group. 261 * @group_name: The name of the group. 262 * @link_name: The name of the symlink to remove. 263 */ 264 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, 265 const char *link_name) 266 { 267 struct sysfs_dirent *dir_sd; 268 269 dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); 270 if (dir_sd) { 271 sysfs_hash_and_remove(dir_sd, NULL, link_name); 272 sysfs_put(dir_sd); 273 } 274 } 275 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); 276 277 EXPORT_SYMBOL_GPL(sysfs_create_group); 278 EXPORT_SYMBOL_GPL(sysfs_update_group); 279 EXPORT_SYMBOL_GPL(sysfs_remove_group); 280