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 * Copyright (c) 2013 Greg Kroah-Hartman 7 * Copyright (c) 2013 The Linux Foundation 8 * 9 * This file is released undert the GPL v2. 10 * 11 */ 12 13 #include <linux/kobject.h> 14 #include <linux/module.h> 15 #include <linux/dcache.h> 16 #include <linux/namei.h> 17 #include <linux/err.h> 18 #include "sysfs.h" 19 20 21 static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, 22 const struct attribute_group *grp) 23 { 24 struct attribute *const *attr; 25 struct bin_attribute *const *bin_attr; 26 27 if (grp->attrs) 28 for (attr = grp->attrs; *attr; attr++) 29 sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); 30 if (grp->bin_attrs) 31 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) 32 sysfs_remove_bin_file(kobj, *bin_attr); 33 } 34 35 static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, 36 const struct attribute_group *grp, int update) 37 { 38 struct attribute *const *attr; 39 struct bin_attribute *const *bin_attr; 40 int error = 0, i; 41 42 if (grp->attrs) { 43 for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) { 44 umode_t mode = 0; 45 46 /* 47 * In update mode, we're changing the permissions or 48 * visibility. Do this by first removing then 49 * re-adding (if required) the file. 50 */ 51 if (update) 52 sysfs_hash_and_remove(dir_sd, NULL, 53 (*attr)->name); 54 if (grp->is_visible) { 55 mode = grp->is_visible(kobj, *attr, i); 56 if (!mode) 57 continue; 58 } 59 error = sysfs_add_file_mode(dir_sd, *attr, 60 SYSFS_KOBJ_ATTR, 61 (*attr)->mode | mode); 62 if (unlikely(error)) 63 break; 64 } 65 if (error) { 66 remove_files(dir_sd, kobj, grp); 67 goto exit; 68 } 69 } 70 71 if (grp->bin_attrs) { 72 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) { 73 if (update) 74 sysfs_remove_bin_file(kobj, *bin_attr); 75 error = sysfs_create_bin_file(kobj, *bin_attr); 76 if (error) 77 break; 78 } 79 if (error) 80 remove_files(dir_sd, kobj, grp); 81 } 82 exit: 83 return error; 84 } 85 86 87 static int internal_create_group(struct kobject *kobj, int update, 88 const struct attribute_group *grp) 89 { 90 struct sysfs_dirent *sd; 91 int error; 92 93 BUG_ON(!kobj || (!update && !kobj->sd)); 94 95 /* Updates may happen before the object has been instantiated */ 96 if (unlikely(update && !kobj->sd)) 97 return -EINVAL; 98 if (!grp->attrs && !grp->bin_attrs) { 99 WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n", 100 kobj->name, grp->name ? "" : grp->name); 101 return -EINVAL; 102 } 103 if (grp->name) { 104 error = sysfs_create_subdir(kobj, grp->name, &sd); 105 if (error) 106 return error; 107 } else 108 sd = kobj->sd; 109 sysfs_get(sd); 110 error = create_files(sd, kobj, grp, update); 111 if (error) { 112 if (grp->name) 113 sysfs_remove_subdir(sd); 114 } 115 sysfs_put(sd); 116 return error; 117 } 118 119 /** 120 * sysfs_create_group - given a directory kobject, create an attribute group 121 * @kobj: The kobject to create the group on 122 * @grp: The attribute group to create 123 * 124 * This function creates a group for the first time. It will explicitly 125 * warn and error if any of the attribute files being created already exist. 126 * 127 * Returns 0 on success or error. 128 */ 129 int sysfs_create_group(struct kobject *kobj, 130 const struct attribute_group *grp) 131 { 132 return internal_create_group(kobj, 0, grp); 133 } 134 EXPORT_SYMBOL_GPL(sysfs_create_group); 135 136 /** 137 * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups 138 * @kobj: The kobject to create the group on 139 * @groups: The attribute groups to create, NULL terminated 140 * 141 * This function creates a bunch of attribute groups. If an error occurs when 142 * creating a group, all previously created groups will be removed, unwinding 143 * everything back to the original state when this function was called. 144 * It will explicitly warn and error if any of the attribute files being 145 * created already exist. 146 * 147 * Returns 0 on success or error code from sysfs_create_group on error. 148 */ 149 int sysfs_create_groups(struct kobject *kobj, 150 const struct attribute_group **groups) 151 { 152 int error = 0; 153 int i; 154 155 if (!groups) 156 return 0; 157 158 for (i = 0; groups[i]; i++) { 159 error = sysfs_create_group(kobj, groups[i]); 160 if (error) { 161 while (--i >= 0) 162 sysfs_remove_group(kobj, groups[i]); 163 break; 164 } 165 } 166 return error; 167 } 168 EXPORT_SYMBOL_GPL(sysfs_create_groups); 169 170 /** 171 * sysfs_update_group - given a directory kobject, update an attribute group 172 * @kobj: The kobject to update the group on 173 * @grp: The attribute group to update 174 * 175 * This function updates an attribute group. Unlike 176 * sysfs_create_group(), it will explicitly not warn or error if any 177 * of the attribute files being created already exist. Furthermore, 178 * if the visibility of the files has changed through the is_visible() 179 * callback, it will update the permissions and add or remove the 180 * relevant files. 181 * 182 * The primary use for this function is to call it after making a change 183 * that affects group visibility. 184 * 185 * Returns 0 on success or error. 186 */ 187 int sysfs_update_group(struct kobject *kobj, 188 const struct attribute_group *grp) 189 { 190 return internal_create_group(kobj, 1, grp); 191 } 192 EXPORT_SYMBOL_GPL(sysfs_update_group); 193 194 /** 195 * sysfs_remove_group: remove a group from a kobject 196 * @kobj: kobject to remove the group from 197 * @grp: group to remove 198 * 199 * This function removes a group of attributes from a kobject. The attributes 200 * previously have to have been created for this group, otherwise it will fail. 201 */ 202 void sysfs_remove_group(struct kobject *kobj, 203 const struct attribute_group *grp) 204 { 205 struct sysfs_dirent *dir_sd = kobj->sd; 206 struct sysfs_dirent *sd; 207 208 if (grp->name) { 209 sd = sysfs_get_dirent(dir_sd, NULL, grp->name); 210 if (!sd) { 211 WARN(!sd, KERN_WARNING 212 "sysfs group %p not found for kobject '%s'\n", 213 grp, kobject_name(kobj)); 214 return; 215 } 216 } else 217 sd = sysfs_get(dir_sd); 218 219 remove_files(sd, kobj, grp); 220 if (grp->name) 221 sysfs_remove_subdir(sd); 222 223 sysfs_put(sd); 224 } 225 EXPORT_SYMBOL_GPL(sysfs_remove_group); 226 227 /** 228 * sysfs_remove_groups - remove a list of groups 229 * 230 * @kobj: The kobject for the groups to be removed from 231 * @groups: NULL terminated list of groups to be removed 232 * 233 * If groups is not NULL, remove the specified groups from the kobject. 234 */ 235 void sysfs_remove_groups(struct kobject *kobj, 236 const struct attribute_group **groups) 237 { 238 int i; 239 240 if (!groups) 241 return; 242 for (i = 0; groups[i]; i++) 243 sysfs_remove_group(kobj, groups[i]); 244 } 245 EXPORT_SYMBOL_GPL(sysfs_remove_groups); 246 247 /** 248 * sysfs_merge_group - merge files into a pre-existing attribute group. 249 * @kobj: The kobject containing the group. 250 * @grp: The files to create and the attribute group they belong to. 251 * 252 * This function returns an error if the group doesn't exist or any of the 253 * files already exist in that group, in which case none of the new files 254 * are created. 255 */ 256 int sysfs_merge_group(struct kobject *kobj, 257 const struct attribute_group *grp) 258 { 259 struct sysfs_dirent *dir_sd; 260 int error = 0; 261 struct attribute *const *attr; 262 int i; 263 264 dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); 265 if (!dir_sd) 266 return -ENOENT; 267 268 for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) 269 error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); 270 if (error) { 271 while (--i >= 0) 272 sysfs_hash_and_remove(dir_sd, NULL, (*--attr)->name); 273 } 274 sysfs_put(dir_sd); 275 276 return error; 277 } 278 EXPORT_SYMBOL_GPL(sysfs_merge_group); 279 280 /** 281 * sysfs_unmerge_group - remove files from a pre-existing attribute group. 282 * @kobj: The kobject containing the group. 283 * @grp: The files to remove and the attribute group they belong to. 284 */ 285 void sysfs_unmerge_group(struct kobject *kobj, 286 const struct attribute_group *grp) 287 { 288 struct sysfs_dirent *dir_sd; 289 struct attribute *const *attr; 290 291 dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); 292 if (dir_sd) { 293 for (attr = grp->attrs; *attr; ++attr) 294 sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); 295 sysfs_put(dir_sd); 296 } 297 } 298 EXPORT_SYMBOL_GPL(sysfs_unmerge_group); 299 300 /** 301 * sysfs_add_link_to_group - add a symlink to an attribute group. 302 * @kobj: The kobject containing the group. 303 * @group_name: The name of the group. 304 * @target: The target kobject of the symlink to create. 305 * @link_name: The name of the symlink to create. 306 */ 307 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, 308 struct kobject *target, const char *link_name) 309 { 310 struct sysfs_dirent *dir_sd; 311 int error = 0; 312 313 dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); 314 if (!dir_sd) 315 return -ENOENT; 316 317 error = sysfs_create_link_sd(dir_sd, target, link_name); 318 sysfs_put(dir_sd); 319 320 return error; 321 } 322 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group); 323 324 /** 325 * sysfs_remove_link_from_group - remove a symlink from an attribute group. 326 * @kobj: The kobject containing the group. 327 * @group_name: The name of the group. 328 * @link_name: The name of the symlink to remove. 329 */ 330 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, 331 const char *link_name) 332 { 333 struct sysfs_dirent *dir_sd; 334 335 dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); 336 if (dir_sd) { 337 sysfs_hash_and_remove(dir_sd, NULL, link_name); 338 sysfs_put(dir_sd); 339 } 340 } 341 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); 342