0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/kobject.h>
0012 #include <linux/module.h>
0013 #include <linux/dcache.h>
0014 #include <linux/namei.h>
0015 #include <linux/err.h>
0016 #include <linux/fs.h>
0017 #include "sysfs.h"
0018
0019
0020 static void remove_files(struct kernfs_node *parent,
0021 const struct attribute_group *grp)
0022 {
0023 struct attribute *const *attr;
0024 struct bin_attribute *const *bin_attr;
0025
0026 if (grp->attrs)
0027 for (attr = grp->attrs; *attr; attr++)
0028 kernfs_remove_by_name(parent, (*attr)->name);
0029 if (grp->bin_attrs)
0030 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++)
0031 kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
0032 }
0033
0034 static int create_files(struct kernfs_node *parent, struct kobject *kobj,
0035 kuid_t uid, kgid_t gid,
0036 const struct attribute_group *grp, int update)
0037 {
0038 struct attribute *const *attr;
0039 struct bin_attribute *const *bin_attr;
0040 int error = 0, i;
0041
0042 if (grp->attrs) {
0043 for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
0044 umode_t mode = (*attr)->mode;
0045
0046
0047
0048
0049
0050
0051 if (update)
0052 kernfs_remove_by_name(parent, (*attr)->name);
0053 if (grp->is_visible) {
0054 mode = grp->is_visible(kobj, *attr, i);
0055 if (!mode)
0056 continue;
0057 }
0058
0059 WARN(mode & ~(SYSFS_PREALLOC | 0664),
0060 "Attribute %s: Invalid permissions 0%o\n",
0061 (*attr)->name, mode);
0062
0063 mode &= SYSFS_PREALLOC | 0664;
0064 error = sysfs_add_file_mode_ns(parent, *attr, mode, uid,
0065 gid, NULL);
0066 if (unlikely(error))
0067 break;
0068 }
0069 if (error) {
0070 remove_files(parent, grp);
0071 goto exit;
0072 }
0073 }
0074
0075 if (grp->bin_attrs) {
0076 for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
0077 umode_t mode = (*bin_attr)->attr.mode;
0078
0079 if (update)
0080 kernfs_remove_by_name(parent,
0081 (*bin_attr)->attr.name);
0082 if (grp->is_bin_visible) {
0083 mode = grp->is_bin_visible(kobj, *bin_attr, i);
0084 if (!mode)
0085 continue;
0086 }
0087
0088 WARN(mode & ~(SYSFS_PREALLOC | 0664),
0089 "Attribute %s: Invalid permissions 0%o\n",
0090 (*bin_attr)->attr.name, mode);
0091
0092 mode &= SYSFS_PREALLOC | 0664;
0093 error = sysfs_add_bin_file_mode_ns(parent, *bin_attr,
0094 mode, uid, gid,
0095 NULL);
0096 if (error)
0097 break;
0098 }
0099 if (error)
0100 remove_files(parent, grp);
0101 }
0102 exit:
0103 return error;
0104 }
0105
0106
0107 static int internal_create_group(struct kobject *kobj, int update,
0108 const struct attribute_group *grp)
0109 {
0110 struct kernfs_node *kn;
0111 kuid_t uid;
0112 kgid_t gid;
0113 int error;
0114
0115 if (WARN_ON(!kobj || (!update && !kobj->sd)))
0116 return -EINVAL;
0117
0118
0119 if (unlikely(update && !kobj->sd))
0120 return -EINVAL;
0121 if (!grp->attrs && !grp->bin_attrs) {
0122 WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",
0123 kobj->name, grp->name ?: "");
0124 return -EINVAL;
0125 }
0126 kobject_get_ownership(kobj, &uid, &gid);
0127 if (grp->name) {
0128 if (update) {
0129 kn = kernfs_find_and_get(kobj->sd, grp->name);
0130 if (!kn) {
0131 pr_warn("Can't update unknown attr grp name: %s/%s\n",
0132 kobj->name, grp->name);
0133 return -EINVAL;
0134 }
0135 } else {
0136 kn = kernfs_create_dir_ns(kobj->sd, grp->name,
0137 S_IRWXU | S_IRUGO | S_IXUGO,
0138 uid, gid, kobj, NULL);
0139 if (IS_ERR(kn)) {
0140 if (PTR_ERR(kn) == -EEXIST)
0141 sysfs_warn_dup(kobj->sd, grp->name);
0142 return PTR_ERR(kn);
0143 }
0144 }
0145 } else
0146 kn = kobj->sd;
0147 kernfs_get(kn);
0148 error = create_files(kn, kobj, uid, gid, grp, update);
0149 if (error) {
0150 if (grp->name)
0151 kernfs_remove(kn);
0152 }
0153 kernfs_put(kn);
0154
0155 if (grp->name && update)
0156 kernfs_put(kn);
0157
0158 return error;
0159 }
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171 int sysfs_create_group(struct kobject *kobj,
0172 const struct attribute_group *grp)
0173 {
0174 return internal_create_group(kobj, 0, grp);
0175 }
0176 EXPORT_SYMBOL_GPL(sysfs_create_group);
0177
0178 static int internal_create_groups(struct kobject *kobj, int update,
0179 const struct attribute_group **groups)
0180 {
0181 int error = 0;
0182 int i;
0183
0184 if (!groups)
0185 return 0;
0186
0187 for (i = 0; groups[i]; i++) {
0188 error = internal_create_group(kobj, update, groups[i]);
0189 if (error) {
0190 while (--i >= 0)
0191 sysfs_remove_group(kobj, groups[i]);
0192 break;
0193 }
0194 }
0195 return error;
0196 }
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211 int sysfs_create_groups(struct kobject *kobj,
0212 const struct attribute_group **groups)
0213 {
0214 return internal_create_groups(kobj, 0, groups);
0215 }
0216 EXPORT_SYMBOL_GPL(sysfs_create_groups);
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229 int sysfs_update_groups(struct kobject *kobj,
0230 const struct attribute_group **groups)
0231 {
0232 return internal_create_groups(kobj, 1, groups);
0233 }
0234 EXPORT_SYMBOL_GPL(sysfs_update_groups);
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254 int sysfs_update_group(struct kobject *kobj,
0255 const struct attribute_group *grp)
0256 {
0257 return internal_create_group(kobj, 1, grp);
0258 }
0259 EXPORT_SYMBOL_GPL(sysfs_update_group);
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269 void sysfs_remove_group(struct kobject *kobj,
0270 const struct attribute_group *grp)
0271 {
0272 struct kernfs_node *parent = kobj->sd;
0273 struct kernfs_node *kn;
0274
0275 if (grp->name) {
0276 kn = kernfs_find_and_get(parent, grp->name);
0277 if (!kn) {
0278 WARN(!kn, KERN_WARNING
0279 "sysfs group '%s' not found for kobject '%s'\n",
0280 grp->name, kobject_name(kobj));
0281 return;
0282 }
0283 } else {
0284 kn = parent;
0285 kernfs_get(kn);
0286 }
0287
0288 remove_files(kn, grp);
0289 if (grp->name)
0290 kernfs_remove(kn);
0291
0292 kernfs_put(kn);
0293 }
0294 EXPORT_SYMBOL_GPL(sysfs_remove_group);
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304 void sysfs_remove_groups(struct kobject *kobj,
0305 const struct attribute_group **groups)
0306 {
0307 int i;
0308
0309 if (!groups)
0310 return;
0311 for (i = 0; groups[i]; i++)
0312 sysfs_remove_group(kobj, groups[i]);
0313 }
0314 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325 int sysfs_merge_group(struct kobject *kobj,
0326 const struct attribute_group *grp)
0327 {
0328 struct kernfs_node *parent;
0329 kuid_t uid;
0330 kgid_t gid;
0331 int error = 0;
0332 struct attribute *const *attr;
0333 int i;
0334
0335 parent = kernfs_find_and_get(kobj->sd, grp->name);
0336 if (!parent)
0337 return -ENOENT;
0338
0339 kobject_get_ownership(kobj, &uid, &gid);
0340
0341 for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
0342 error = sysfs_add_file_mode_ns(parent, *attr, (*attr)->mode,
0343 uid, gid, NULL);
0344 if (error) {
0345 while (--i >= 0)
0346 kernfs_remove_by_name(parent, (*--attr)->name);
0347 }
0348 kernfs_put(parent);
0349
0350 return error;
0351 }
0352 EXPORT_SYMBOL_GPL(sysfs_merge_group);
0353
0354
0355
0356
0357
0358
0359 void sysfs_unmerge_group(struct kobject *kobj,
0360 const struct attribute_group *grp)
0361 {
0362 struct kernfs_node *parent;
0363 struct attribute *const *attr;
0364
0365 parent = kernfs_find_and_get(kobj->sd, grp->name);
0366 if (parent) {
0367 for (attr = grp->attrs; *attr; ++attr)
0368 kernfs_remove_by_name(parent, (*attr)->name);
0369 kernfs_put(parent);
0370 }
0371 }
0372 EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
0373
0374
0375
0376
0377
0378
0379
0380
0381 int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name,
0382 struct kobject *target, const char *link_name)
0383 {
0384 struct kernfs_node *parent;
0385 int error = 0;
0386
0387 parent = kernfs_find_and_get(kobj->sd, group_name);
0388 if (!parent)
0389 return -ENOENT;
0390
0391 error = sysfs_create_link_sd(parent, target, link_name);
0392 kernfs_put(parent);
0393
0394 return error;
0395 }
0396 EXPORT_SYMBOL_GPL(sysfs_add_link_to_group);
0397
0398
0399
0400
0401
0402
0403
0404 void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name,
0405 const char *link_name)
0406 {
0407 struct kernfs_node *parent;
0408
0409 parent = kernfs_find_and_get(kobj->sd, group_name);
0410 if (parent) {
0411 kernfs_remove_by_name(parent, link_name);
0412 kernfs_put(parent);
0413 }
0414 }
0415 EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group);
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426 int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj,
0427 struct kobject *target_kobj,
0428 const char *target_name,
0429 const char *symlink_name)
0430 {
0431 struct kernfs_node *target;
0432 struct kernfs_node *entry;
0433 struct kernfs_node *link;
0434
0435
0436
0437
0438
0439
0440 spin_lock(&sysfs_symlink_target_lock);
0441 target = target_kobj->sd;
0442 if (target)
0443 kernfs_get(target);
0444 spin_unlock(&sysfs_symlink_target_lock);
0445 if (!target)
0446 return -ENOENT;
0447
0448 entry = kernfs_find_and_get(target, target_name);
0449 if (!entry) {
0450 kernfs_put(target);
0451 return -ENOENT;
0452 }
0453
0454 if (!symlink_name)
0455 symlink_name = target_name;
0456
0457 link = kernfs_create_link(kobj->sd, symlink_name, entry);
0458 if (PTR_ERR(link) == -EEXIST)
0459 sysfs_warn_dup(kobj->sd, symlink_name);
0460
0461 kernfs_put(entry);
0462 kernfs_put(target);
0463 return PTR_ERR_OR_ZERO(link);
0464 }
0465 EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj);
0466
0467 static int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn,
0468 const struct attribute_group *grp,
0469 struct iattr *newattrs)
0470 {
0471 struct kernfs_node *kn;
0472 int error;
0473
0474 if (grp->attrs) {
0475 struct attribute *const *attr;
0476
0477 for (attr = grp->attrs; *attr; attr++) {
0478 kn = kernfs_find_and_get(grp_kn, (*attr)->name);
0479 if (!kn)
0480 return -ENOENT;
0481
0482 error = kernfs_setattr(kn, newattrs);
0483 kernfs_put(kn);
0484 if (error)
0485 return error;
0486 }
0487 }
0488
0489 if (grp->bin_attrs) {
0490 struct bin_attribute *const *bin_attr;
0491
0492 for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) {
0493 kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name);
0494 if (!kn)
0495 return -ENOENT;
0496
0497 error = kernfs_setattr(kn, newattrs);
0498 kernfs_put(kn);
0499 if (error)
0500 return error;
0501 }
0502 }
0503
0504 return 0;
0505 }
0506
0507
0508
0509
0510
0511
0512
0513
0514
0515
0516 int sysfs_group_change_owner(struct kobject *kobj,
0517 const struct attribute_group *grp, kuid_t kuid,
0518 kgid_t kgid)
0519 {
0520 struct kernfs_node *grp_kn;
0521 int error;
0522 struct iattr newattrs = {
0523 .ia_valid = ATTR_UID | ATTR_GID,
0524 .ia_uid = kuid,
0525 .ia_gid = kgid,
0526 };
0527
0528 if (!kobj->state_in_sysfs)
0529 return -EINVAL;
0530
0531 if (grp->name) {
0532 grp_kn = kernfs_find_and_get(kobj->sd, grp->name);
0533 } else {
0534 kernfs_get(kobj->sd);
0535 grp_kn = kobj->sd;
0536 }
0537 if (!grp_kn)
0538 return -ENOENT;
0539
0540 error = kernfs_setattr(grp_kn, &newattrs);
0541 if (!error)
0542 error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs);
0543
0544 kernfs_put(grp_kn);
0545
0546 return error;
0547 }
0548 EXPORT_SYMBOL_GPL(sysfs_group_change_owner);
0549
0550
0551
0552
0553
0554
0555
0556
0557
0558
0559 int sysfs_groups_change_owner(struct kobject *kobj,
0560 const struct attribute_group **groups,
0561 kuid_t kuid, kgid_t kgid)
0562 {
0563 int error = 0, i;
0564
0565 if (!kobj->state_in_sysfs)
0566 return -EINVAL;
0567
0568 if (!groups)
0569 return 0;
0570
0571 for (i = 0; groups[i]; i++) {
0572 error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid);
0573 if (error)
0574 break;
0575 }
0576
0577 return error;
0578 }
0579 EXPORT_SYMBOL_GPL(sysfs_groups_change_owner);