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 <linux/fs.h> 17 #include "sysfs.h" 18 19 20 static void remove_files(struct kernfs_node *parent, 21 const struct attribute_group *grp) 22 { 23 struct attribute *const *attr; 24 struct bin_attribute *const *bin_attr; 25 26 if (grp->attrs) 27 for (attr = grp->attrs; *attr; attr++) 28 kernfs_remove_by_name(parent, (*attr)->name); 29 if (grp->bin_attrs) 30 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) 31 kernfs_remove_by_name(parent, (*bin_attr)->attr.name); 32 } 33 34 static int create_files(struct kernfs_node *parent, struct kobject *kobj, 35 kuid_t uid, kgid_t gid, 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 = (*attr)->mode; 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 kernfs_remove_by_name(parent, (*attr)->name); 53 if (grp->is_visible) { 54 mode = grp->is_visible(kobj, *attr, i); 55 if (!mode) 56 continue; 57 } 58 59 WARN(mode & ~(SYSFS_PREALLOC | 0664), 60 "Attribute %s: Invalid permissions 0%o\n", 61 (*attr)->name, mode); 62 63 mode &= SYSFS_PREALLOC | 0664; 64 error = sysfs_add_file_mode_ns(parent, *attr, false, 65 mode, uid, gid, NULL); 66 if (unlikely(error)) 67 break; 68 } 69 if (error) { 70 remove_files(parent, grp); 71 goto exit; 72 } 73 } 74 75 if (grp->bin_attrs) { 76 for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) { 77 umode_t mode = (*bin_attr)->attr.mode; 78 79 if (update) 80 kernfs_remove_by_name(parent, 81 (*bin_attr)->attr.name); 82 if (grp->is_bin_visible) { 83 mode = grp->is_bin_visible(kobj, *bin_attr, i); 84 if (!mode) 85 continue; 86 } 87 88 WARN(mode & ~(SYSFS_PREALLOC | 0664), 89 "Attribute %s: Invalid permissions 0%o\n", 90 (*bin_attr)->attr.name, mode); 91 92 mode &= SYSFS_PREALLOC | 0664; 93 error = sysfs_add_file_mode_ns(parent, 94 &(*bin_attr)->attr, true, 95 mode, 96 uid, gid, NULL); 97 if (error) 98 break; 99 } 100 if (error) 101 remove_files(parent, grp); 102 } 103 exit: 104 return error; 105 } 106 107 108 static int internal_create_group(struct kobject *kobj, int update, 109 const struct attribute_group *grp) 110 { 111 struct kernfs_node *kn; 112 kuid_t uid; 113 kgid_t gid; 114 int error; 115 116 if (WARN_ON(!kobj || (!update && !kobj->sd))) 117 return -EINVAL; 118 119 /* Updates may happen before the object has been instantiated */ 120 if (unlikely(update && !kobj->sd)) 121 return -EINVAL; 122 if (!grp->attrs && !grp->bin_attrs) { 123 WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n", 124 kobj->name, grp->name ?: ""); 125 return -EINVAL; 126 } 127 kobject_get_ownership(kobj, &uid, &gid); 128 if (grp->name) { 129 if (update) { 130 kn = kernfs_find_and_get(kobj->sd, grp->name); 131 if (!kn) { 132 pr_warn("Can't update unknown attr grp name: %s/%s\n", 133 kobj->name, grp->name); 134 return -EINVAL; 135 } 136 } else { 137 kn = kernfs_create_dir_ns(kobj->sd, grp->name, 138 S_IRWXU | S_IRUGO | S_IXUGO, 139 uid, gid, kobj, NULL); 140 if (IS_ERR(kn)) { 141 if (PTR_ERR(kn) == -EEXIST) 142 sysfs_warn_dup(kobj->sd, grp->name); 143 return PTR_ERR(kn); 144 } 145 } 146 } else 147 kn = kobj->sd; 148 kernfs_get(kn); 149 error = create_files(kn, kobj, uid, gid, grp, update); 150 if (error) { 151 if (grp->name) 152 kernfs_remove(kn); 153 } 154 kernfs_put(kn); 155 156 if (grp->name && update) 157 kernfs_put(kn); 158 159 return error; 160 } 161 162 /** 163 * sysfs_create_group - given a directory kobject, create an attribute group 164 * @kobj: The kobject to create the group on 165 * @grp: The attribute group to create 166 * 167 * This function creates a group for the first time. It will explicitly 168 * warn and error if any of the attribute files being created already exist. 169 * 170 * Returns 0 on success or error code on failure. 171 */ 172 int sysfs_create_group(struct kobject *kobj, 173 const struct attribute_group *grp) 174 { 175 return internal_create_group(kobj, 0, grp); 176 } 177 EXPORT_SYMBOL_GPL(sysfs_create_group); 178 179 static int internal_create_groups(struct kobject *kobj, int update, 180 const struct attribute_group **groups) 181 { 182 int error = 0; 183 int i; 184 185 if (!groups) 186 return 0; 187 188 for (i = 0; groups[i]; i++) { 189 error = internal_create_group(kobj, update, groups[i]); 190 if (error) { 191 while (--i >= 0) 192 sysfs_remove_group(kobj, groups[i]); 193 break; 194 } 195 } 196 return error; 197 } 198 199 /** 200 * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups 201 * @kobj: The kobject to create the group on 202 * @groups: The attribute groups to create, NULL terminated 203 * 204 * This function creates a bunch of attribute groups. If an error occurs when 205 * creating a group, all previously created groups will be removed, unwinding 206 * everything back to the original state when this function was called. 207 * It will explicitly warn and error if any of the attribute files being 208 * created already exist. 209 * 210 * Returns 0 on success or error code from sysfs_create_group on failure. 211 */ 212 int sysfs_create_groups(struct kobject *kobj, 213 const struct attribute_group **groups) 214 { 215 return internal_create_groups(kobj, 0, groups); 216 } 217 EXPORT_SYMBOL_GPL(sysfs_create_groups); 218 219 /** 220 * sysfs_update_groups - given a directory kobject, create a bunch of attribute groups 221 * @kobj: The kobject to update the group on 222 * @groups: The attribute groups to update, NULL terminated 223 * 224 * This function update a bunch of attribute groups. If an error occurs when 225 * updating a group, all previously updated groups will be removed together 226 * with already existing (not updated) attributes. 227 * 228 * Returns 0 on success or error code from sysfs_update_group on failure. 229 */ 230 int sysfs_update_groups(struct kobject *kobj, 231 const struct attribute_group **groups) 232 { 233 return internal_create_groups(kobj, 1, groups); 234 } 235 EXPORT_SYMBOL_GPL(sysfs_update_groups); 236 237 /** 238 * sysfs_update_group - given a directory kobject, update an attribute group 239 * @kobj: The kobject to update the group on 240 * @grp: The attribute group to update 241 * 242 * This function updates an attribute group. Unlike 243 * sysfs_create_group(), it will explicitly not warn or error if any 244 * of the attribute files being created already exist. Furthermore, 245 * if the visibility of the files has changed through the is_visible() 246 * callback, it will update the permissions and add or remove the 247 * relevant files. Changing a group's name (subdirectory name under 248 * kobj's directory in sysfs) is not allowed. 249 * 250 * The primary use for this function is to call it after making a change 251 * that affects group visibility. 252 * 253 * Returns 0 on success or error code on failure. 254 */ 255 int sysfs_update_group(struct kobject *kobj, 256 const struct attribute_group *grp) 257 { 258 return internal_create_group(kobj, 1, grp); 259 } 260 EXPORT_SYMBOL_GPL(sysfs_update_group); 261 262 /** 263 * sysfs_remove_group: remove a group from a kobject 264 * @kobj: kobject to remove the group from 265 * @grp: group to remove 266 * 267 * This function removes a group of attributes from a kobject. The attributes 268 * previously have to have been created for this group, otherwise it will fail. 269 */ 270 void sysfs_remove_group(struct kobject *kobj, 271 const struct attribute_group *grp) 272 { 273 struct kernfs_node *parent = kobj->sd; 274 struct kernfs_node *kn; 275 276 if (grp->name) { 277 kn = kernfs_find_and_get(parent, grp->name); 278 if (!kn) { 279 WARN(!kn, KERN_WARNING 280 "sysfs group '%s' not found for kobject '%s'\n", 281 grp->name, kobject_name(kobj)); 282 return; 283 } 284 } else { 285 kn = parent; 286 kernfs_get(kn); 287 } 288 289 remove_files(kn, grp); 290 if (grp->name) 291 kernfs_remove(kn); 292 293 kernfs_put(kn); 294 } 295 EXPORT_SYMBOL_GPL(sysfs_remove_group); 296 297 /** 298 * sysfs_remove_groups - remove a list of groups 299 * 300 * @kobj: The kobject for the groups to be removed from 301 * @groups: NULL terminated list of groups to be removed 302 * 303 * If groups is not NULL, remove the specified groups from the kobject. 304 */ 305 void sysfs_remove_groups(struct kobject *kobj, 306 const struct attribute_group **groups) 307 { 308 int i; 309 310 if (!groups) 311 return; 312 for (i = 0; groups[i]; i++) 313 sysfs_remove_group(kobj, groups[i]); 314 } 315 EXPORT_SYMBOL_GPL(sysfs_remove_groups); 316 317 /** 318 * sysfs_merge_group - merge files into a pre-existing attribute group. 319 * @kobj: The kobject containing the group. 320 * @grp: The files to create and the attribute group they belong to. 321 * 322 * This function returns an error if the group doesn't exist or any of the 323 * files already exist in that group, in which case none of the new files 324 * are created. 325 */ 326 int sysfs_merge_group(struct kobject *kobj, 327 const struct attribute_group *grp) 328 { 329 struct kernfs_node *parent; 330 kuid_t uid; 331 kgid_t gid; 332 int error = 0; 333 struct attribute *const *attr; 334 int i; 335 336 parent = kernfs_find_and_get(kobj->sd, grp->name); 337 if (!parent) 338 return -ENOENT; 339 340 kobject_get_ownership(kobj, &uid, &gid); 341 342 for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) 343 error = sysfs_add_file_mode_ns(parent, *attr, false, 344 (*attr)->mode, uid, gid, NULL); 345 if (error) { 346 while (--i >= 0) 347 kernfs_remove_by_name(parent, (*--attr)->name); 348 } 349 kernfs_put(parent); 350 351 return error; 352 } 353 EXPORT_SYMBOL_GPL(sysfs_merge_group); 354 355 /** 356 * sysfs_unmerge_group - remove files from a pre-existing attribute group. 357 * @kobj: The kobject containing the group. 358 * @grp: The files to remove and the attribute group they belong to. 359 */ 360 void sysfs_unmerge_group(struct kobject *kobj, 361 const struct attribute_group *grp) 362 { 363 struct kernfs_node *parent; 364 struct attribute *const *attr; 365 366 parent = kernfs_find_and_get(kobj->sd, grp->name); 367 if (parent) { 368 for (attr = grp->attrs; *attr; ++attr) 369 kernfs_remove_by_name(parent, (*attr)->name); 370 kernfs_put(parent); 371 } 372 } 373 EXPORT_SYMBOL_GPL(sysfs_unmerge_group); 374 375 /** 376 * sysfs_add_link_to_group - add a symlink to an attribute group. 377 * @kobj: The kobject containing the group. 378 * @group_name: The name of the group. 379 * @target: The target kobject of the symlink to create. 380 * @link_name: The name of the symlink to create. 381 */ 382 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, 383 struct kobject *target, const char *link_name) 384 { 385 struct kernfs_node *parent; 386 int error = 0; 387 388 parent = kernfs_find_and_get(kobj->sd, group_name); 389 if (!parent) 390 return -ENOENT; 391 392 error = sysfs_create_link_sd(parent, target, link_name); 393 kernfs_put(parent); 394 395 return error; 396 } 397 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group); 398 399 /** 400 * sysfs_remove_link_from_group - remove a symlink from an attribute group. 401 * @kobj: The kobject containing the group. 402 * @group_name: The name of the group. 403 * @link_name: The name of the symlink to remove. 404 */ 405 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, 406 const char *link_name) 407 { 408 struct kernfs_node *parent; 409 410 parent = kernfs_find_and_get(kobj->sd, group_name); 411 if (parent) { 412 kernfs_remove_by_name(parent, link_name); 413 kernfs_put(parent); 414 } 415 } 416 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); 417 418 /** 419 * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing 420 * to a group or an attribute 421 * @kobj: The kobject containing the group. 422 * @target_kobj: The target kobject. 423 * @target_name: The name of the target group or attribute. 424 * @symlink_name: The name of the symlink file (target_name will be 425 * considered if symlink_name is NULL). 426 */ 427 int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, 428 struct kobject *target_kobj, 429 const char *target_name, 430 const char *symlink_name) 431 { 432 struct kernfs_node *target; 433 struct kernfs_node *entry; 434 struct kernfs_node *link; 435 436 /* 437 * We don't own @target_kobj and it may be removed at any time. 438 * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir() 439 * for details. 440 */ 441 spin_lock(&sysfs_symlink_target_lock); 442 target = target_kobj->sd; 443 if (target) 444 kernfs_get(target); 445 spin_unlock(&sysfs_symlink_target_lock); 446 if (!target) 447 return -ENOENT; 448 449 entry = kernfs_find_and_get(target, target_name); 450 if (!entry) { 451 kernfs_put(target); 452 return -ENOENT; 453 } 454 455 if (!symlink_name) 456 symlink_name = target_name; 457 458 link = kernfs_create_link(kobj->sd, symlink_name, entry); 459 if (PTR_ERR(link) == -EEXIST) 460 sysfs_warn_dup(kobj->sd, symlink_name); 461 462 kernfs_put(entry); 463 kernfs_put(target); 464 return PTR_ERR_OR_ZERO(link); 465 } 466 EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj); 467 468 static int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn, 469 const struct attribute_group *grp, 470 struct iattr *newattrs) 471 { 472 struct kernfs_node *kn; 473 int error; 474 475 if (grp->attrs) { 476 struct attribute *const *attr; 477 478 for (attr = grp->attrs; *attr; attr++) { 479 kn = kernfs_find_and_get(grp_kn, (*attr)->name); 480 if (!kn) 481 return -ENOENT; 482 483 error = kernfs_setattr(kn, newattrs); 484 kernfs_put(kn); 485 if (error) 486 return error; 487 } 488 } 489 490 if (grp->bin_attrs) { 491 struct bin_attribute *const *bin_attr; 492 493 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) { 494 kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name); 495 if (!kn) 496 return -ENOENT; 497 498 error = kernfs_setattr(kn, newattrs); 499 kernfs_put(kn); 500 if (error) 501 return error; 502 } 503 } 504 505 return 0; 506 } 507 508 /** 509 * sysfs_group_change_owner - change owner of an attribute group. 510 * @kobj: The kobject containing the group. 511 * @grp: The attribute group. 512 * @kuid: new owner's kuid 513 * @kgid: new owner's kgid 514 * 515 * Returns 0 on success or error code on failure. 516 */ 517 int sysfs_group_change_owner(struct kobject *kobj, 518 const struct attribute_group *grp, kuid_t kuid, 519 kgid_t kgid) 520 { 521 struct kernfs_node *grp_kn; 522 int error; 523 struct iattr newattrs = { 524 .ia_valid = ATTR_UID | ATTR_GID, 525 .ia_uid = kuid, 526 .ia_gid = kgid, 527 }; 528 529 if (!kobj->state_in_sysfs) 530 return -EINVAL; 531 532 if (grp->name) { 533 grp_kn = kernfs_find_and_get(kobj->sd, grp->name); 534 } else { 535 kernfs_get(kobj->sd); 536 grp_kn = kobj->sd; 537 } 538 if (!grp_kn) 539 return -ENOENT; 540 541 error = kernfs_setattr(grp_kn, &newattrs); 542 if (!error) 543 error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs); 544 545 kernfs_put(grp_kn); 546 547 return error; 548 } 549 EXPORT_SYMBOL_GPL(sysfs_group_change_owner); 550 551 /** 552 * sysfs_groups_change_owner - change owner of a set of attribute groups. 553 * @kobj: The kobject containing the groups. 554 * @groups: The attribute groups. 555 * @kuid: new owner's kuid 556 * @kgid: new owner's kgid 557 * 558 * Returns 0 on success or error code on failure. 559 */ 560 int sysfs_groups_change_owner(struct kobject *kobj, 561 const struct attribute_group **groups, 562 kuid_t kuid, kgid_t kgid) 563 { 564 int error = 0, i; 565 566 if (!kobj->state_in_sysfs) 567 return -EINVAL; 568 569 if (!groups) 570 return 0; 571 572 for (i = 0; groups[i]; i++) { 573 error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid); 574 if (error) 575 break; 576 } 577 578 return error; 579 } 580 EXPORT_SYMBOL_GPL(sysfs_groups_change_owner); 581