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