0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/module.h>
0013 #include <linux/kobject.h>
0014 #include <linux/slab.h>
0015 #include <linux/list.h>
0016 #include <linux/mutex.h>
0017 #include <linux/seq_file.h>
0018 #include <linux/mm.h>
0019
0020 #include "sysfs.h"
0021
0022
0023
0024
0025
0026 static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
0027 {
0028 struct kobject *kobj = kn->parent->priv;
0029
0030 if (kn->flags & KERNFS_LOCKDEP)
0031 lockdep_assert_held(kn);
0032 return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
0033 }
0034
0035
0036
0037
0038
0039
0040 static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
0041 {
0042 struct kernfs_open_file *of = sf->private;
0043 struct kobject *kobj = of->kn->parent->priv;
0044 const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
0045 ssize_t count;
0046 char *buf;
0047
0048 if (WARN_ON_ONCE(!ops->show))
0049 return -EINVAL;
0050
0051
0052 count = seq_get_buf(sf, &buf);
0053 if (count < PAGE_SIZE) {
0054 seq_commit(sf, -1);
0055 return 0;
0056 }
0057 memset(buf, 0, PAGE_SIZE);
0058
0059 count = ops->show(kobj, of->kn->priv, buf);
0060 if (count < 0)
0061 return count;
0062
0063
0064
0065
0066
0067 if (count >= (ssize_t)PAGE_SIZE) {
0068 printk("fill_read_buffer: %pS returned bad count\n",
0069 ops->show);
0070
0071 count = PAGE_SIZE - 1;
0072 }
0073 seq_commit(sf, count);
0074 return 0;
0075 }
0076
0077 static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf,
0078 size_t count, loff_t pos)
0079 {
0080 struct bin_attribute *battr = of->kn->priv;
0081 struct kobject *kobj = of->kn->parent->priv;
0082 loff_t size = file_inode(of->file)->i_size;
0083
0084 if (!count)
0085 return 0;
0086
0087 if (size) {
0088 if (pos >= size)
0089 return 0;
0090 if (pos + count > size)
0091 count = size - pos;
0092 }
0093
0094 if (!battr->read)
0095 return -EIO;
0096
0097 return battr->read(of->file, kobj, battr, buf, pos, count);
0098 }
0099
0100
0101 static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
0102 size_t count, loff_t pos)
0103 {
0104 const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
0105 struct kobject *kobj = of->kn->parent->priv;
0106 ssize_t len;
0107
0108
0109
0110
0111
0112 if (WARN_ON_ONCE(buf != of->prealloc_buf))
0113 return 0;
0114 len = ops->show(kobj, of->kn->priv, buf);
0115 if (len < 0)
0116 return len;
0117 if (pos) {
0118 if (len <= pos)
0119 return 0;
0120 len -= pos;
0121 memmove(buf, buf + pos, len);
0122 }
0123 return min_t(ssize_t, count, len);
0124 }
0125
0126
0127 static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
0128 size_t count, loff_t pos)
0129 {
0130 const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
0131 struct kobject *kobj = of->kn->parent->priv;
0132
0133 if (!count)
0134 return 0;
0135
0136 return ops->store(kobj, of->kn->priv, buf, count);
0137 }
0138
0139
0140 static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf,
0141 size_t count, loff_t pos)
0142 {
0143 struct bin_attribute *battr = of->kn->priv;
0144 struct kobject *kobj = of->kn->parent->priv;
0145 loff_t size = file_inode(of->file)->i_size;
0146
0147 if (size) {
0148 if (size <= pos)
0149 return -EFBIG;
0150 count = min_t(ssize_t, count, size - pos);
0151 }
0152 if (!count)
0153 return 0;
0154
0155 if (!battr->write)
0156 return -EIO;
0157
0158 return battr->write(of->file, kobj, battr, buf, pos, count);
0159 }
0160
0161 static int sysfs_kf_bin_mmap(struct kernfs_open_file *of,
0162 struct vm_area_struct *vma)
0163 {
0164 struct bin_attribute *battr = of->kn->priv;
0165 struct kobject *kobj = of->kn->parent->priv;
0166
0167 return battr->mmap(of->file, kobj, battr, vma);
0168 }
0169
0170 static int sysfs_kf_bin_open(struct kernfs_open_file *of)
0171 {
0172 struct bin_attribute *battr = of->kn->priv;
0173
0174 if (battr->f_mapping)
0175 of->file->f_mapping = battr->f_mapping();
0176
0177 return 0;
0178 }
0179
0180 void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr)
0181 {
0182 struct kernfs_node *kn = kobj->sd, *tmp;
0183
0184 if (kn && dir)
0185 kn = kernfs_find_and_get(kn, dir);
0186 else
0187 kernfs_get(kn);
0188
0189 if (kn && attr) {
0190 tmp = kernfs_find_and_get(kn, attr);
0191 kernfs_put(kn);
0192 kn = tmp;
0193 }
0194
0195 if (kn) {
0196 kernfs_notify(kn);
0197 kernfs_put(kn);
0198 }
0199 }
0200 EXPORT_SYMBOL_GPL(sysfs_notify);
0201
0202 static const struct kernfs_ops sysfs_file_kfops_empty = {
0203 };
0204
0205 static const struct kernfs_ops sysfs_file_kfops_ro = {
0206 .seq_show = sysfs_kf_seq_show,
0207 };
0208
0209 static const struct kernfs_ops sysfs_file_kfops_wo = {
0210 .write = sysfs_kf_write,
0211 };
0212
0213 static const struct kernfs_ops sysfs_file_kfops_rw = {
0214 .seq_show = sysfs_kf_seq_show,
0215 .write = sysfs_kf_write,
0216 };
0217
0218 static const struct kernfs_ops sysfs_prealloc_kfops_ro = {
0219 .read = sysfs_kf_read,
0220 .prealloc = true,
0221 };
0222
0223 static const struct kernfs_ops sysfs_prealloc_kfops_wo = {
0224 .write = sysfs_kf_write,
0225 .prealloc = true,
0226 };
0227
0228 static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
0229 .read = sysfs_kf_read,
0230 .write = sysfs_kf_write,
0231 .prealloc = true,
0232 };
0233
0234 static const struct kernfs_ops sysfs_bin_kfops_ro = {
0235 .read = sysfs_kf_bin_read,
0236 };
0237
0238 static const struct kernfs_ops sysfs_bin_kfops_wo = {
0239 .write = sysfs_kf_bin_write,
0240 };
0241
0242 static const struct kernfs_ops sysfs_bin_kfops_rw = {
0243 .read = sysfs_kf_bin_read,
0244 .write = sysfs_kf_bin_write,
0245 };
0246
0247 static const struct kernfs_ops sysfs_bin_kfops_mmap = {
0248 .read = sysfs_kf_bin_read,
0249 .write = sysfs_kf_bin_write,
0250 .mmap = sysfs_kf_bin_mmap,
0251 .open = sysfs_kf_bin_open,
0252 };
0253
0254 int sysfs_add_file_mode_ns(struct kernfs_node *parent,
0255 const struct attribute *attr, umode_t mode, kuid_t uid,
0256 kgid_t gid, const void *ns)
0257 {
0258 struct kobject *kobj = parent->priv;
0259 const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
0260 struct lock_class_key *key = NULL;
0261 const struct kernfs_ops *ops = NULL;
0262 struct kernfs_node *kn;
0263
0264
0265 if (WARN(!sysfs_ops, KERN_ERR
0266 "missing sysfs attribute operations for kobject: %s\n",
0267 kobject_name(kobj)))
0268 return -EINVAL;
0269
0270 if (mode & SYSFS_PREALLOC) {
0271 if (sysfs_ops->show && sysfs_ops->store)
0272 ops = &sysfs_prealloc_kfops_rw;
0273 else if (sysfs_ops->show)
0274 ops = &sysfs_prealloc_kfops_ro;
0275 else if (sysfs_ops->store)
0276 ops = &sysfs_prealloc_kfops_wo;
0277 } else {
0278 if (sysfs_ops->show && sysfs_ops->store)
0279 ops = &sysfs_file_kfops_rw;
0280 else if (sysfs_ops->show)
0281 ops = &sysfs_file_kfops_ro;
0282 else if (sysfs_ops->store)
0283 ops = &sysfs_file_kfops_wo;
0284 }
0285
0286 if (!ops)
0287 ops = &sysfs_file_kfops_empty;
0288
0289 #ifdef CONFIG_DEBUG_LOCK_ALLOC
0290 if (!attr->ignore_lockdep)
0291 key = attr->key ?: (struct lock_class_key *)&attr->skey;
0292 #endif
0293
0294 kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
0295 PAGE_SIZE, ops, (void *)attr, ns, key);
0296 if (IS_ERR(kn)) {
0297 if (PTR_ERR(kn) == -EEXIST)
0298 sysfs_warn_dup(parent, attr->name);
0299 return PTR_ERR(kn);
0300 }
0301 return 0;
0302 }
0303
0304 int sysfs_add_bin_file_mode_ns(struct kernfs_node *parent,
0305 const struct bin_attribute *battr, umode_t mode,
0306 kuid_t uid, kgid_t gid, const void *ns)
0307 {
0308 const struct attribute *attr = &battr->attr;
0309 struct lock_class_key *key = NULL;
0310 const struct kernfs_ops *ops;
0311 struct kernfs_node *kn;
0312
0313 if (battr->mmap)
0314 ops = &sysfs_bin_kfops_mmap;
0315 else if (battr->read && battr->write)
0316 ops = &sysfs_bin_kfops_rw;
0317 else if (battr->read)
0318 ops = &sysfs_bin_kfops_ro;
0319 else if (battr->write)
0320 ops = &sysfs_bin_kfops_wo;
0321 else
0322 ops = &sysfs_file_kfops_empty;
0323
0324 #ifdef CONFIG_DEBUG_LOCK_ALLOC
0325 if (!attr->ignore_lockdep)
0326 key = attr->key ?: (struct lock_class_key *)&attr->skey;
0327 #endif
0328
0329 kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
0330 battr->size, ops, (void *)attr, ns, key);
0331 if (IS_ERR(kn)) {
0332 if (PTR_ERR(kn) == -EEXIST)
0333 sysfs_warn_dup(parent, attr->name);
0334 return PTR_ERR(kn);
0335 }
0336 return 0;
0337 }
0338
0339
0340
0341
0342
0343
0344
0345 int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
0346 const void *ns)
0347 {
0348 kuid_t uid;
0349 kgid_t gid;
0350
0351 if (WARN_ON(!kobj || !kobj->sd || !attr))
0352 return -EINVAL;
0353
0354 kobject_get_ownership(kobj, &uid, &gid);
0355 return sysfs_add_file_mode_ns(kobj->sd, attr, attr->mode, uid, gid, ns);
0356 }
0357 EXPORT_SYMBOL_GPL(sysfs_create_file_ns);
0358
0359 int sysfs_create_files(struct kobject *kobj, const struct attribute * const *ptr)
0360 {
0361 int err = 0;
0362 int i;
0363
0364 for (i = 0; ptr[i] && !err; i++)
0365 err = sysfs_create_file(kobj, ptr[i]);
0366 if (err)
0367 while (--i >= 0)
0368 sysfs_remove_file(kobj, ptr[i]);
0369 return err;
0370 }
0371 EXPORT_SYMBOL_GPL(sysfs_create_files);
0372
0373
0374
0375
0376
0377
0378
0379 int sysfs_add_file_to_group(struct kobject *kobj,
0380 const struct attribute *attr, const char *group)
0381 {
0382 struct kernfs_node *parent;
0383 kuid_t uid;
0384 kgid_t gid;
0385 int error;
0386
0387 if (group) {
0388 parent = kernfs_find_and_get(kobj->sd, group);
0389 } else {
0390 parent = kobj->sd;
0391 kernfs_get(parent);
0392 }
0393
0394 if (!parent)
0395 return -ENOENT;
0396
0397 kobject_get_ownership(kobj, &uid, &gid);
0398 error = sysfs_add_file_mode_ns(parent, attr, attr->mode, uid, gid,
0399 NULL);
0400 kernfs_put(parent);
0401
0402 return error;
0403 }
0404 EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
0405
0406
0407
0408
0409
0410
0411
0412
0413 int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr,
0414 umode_t mode)
0415 {
0416 struct kernfs_node *kn;
0417 struct iattr newattrs;
0418 int rc;
0419
0420 kn = kernfs_find_and_get(kobj->sd, attr->name);
0421 if (!kn)
0422 return -ENOENT;
0423
0424 newattrs.ia_mode = (mode & S_IALLUGO) | (kn->mode & ~S_IALLUGO);
0425 newattrs.ia_valid = ATTR_MODE;
0426
0427 rc = kernfs_setattr(kn, &newattrs);
0428
0429 kernfs_put(kn);
0430 return rc;
0431 }
0432 EXPORT_SYMBOL_GPL(sysfs_chmod_file);
0433
0434
0435
0436
0437
0438
0439
0440
0441
0442
0443
0444 struct kernfs_node *sysfs_break_active_protection(struct kobject *kobj,
0445 const struct attribute *attr)
0446 {
0447 struct kernfs_node *kn;
0448
0449 kobject_get(kobj);
0450 kn = kernfs_find_and_get(kobj->sd, attr->name);
0451 if (kn)
0452 kernfs_break_active_protection(kn);
0453 return kn;
0454 }
0455 EXPORT_SYMBOL_GPL(sysfs_break_active_protection);
0456
0457
0458
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468 void sysfs_unbreak_active_protection(struct kernfs_node *kn)
0469 {
0470 struct kobject *kobj = kn->parent->priv;
0471
0472 kernfs_unbreak_active_protection(kn);
0473 kernfs_put(kn);
0474 kobject_put(kobj);
0475 }
0476 EXPORT_SYMBOL_GPL(sysfs_unbreak_active_protection);
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486 void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
0487 const void *ns)
0488 {
0489 struct kernfs_node *parent = kobj->sd;
0490
0491 kernfs_remove_by_name_ns(parent, attr->name, ns);
0492 }
0493 EXPORT_SYMBOL_GPL(sysfs_remove_file_ns);
0494
0495
0496
0497
0498
0499
0500
0501
0502 bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr)
0503 {
0504 struct kernfs_node *parent = kobj->sd;
0505 struct kernfs_node *kn;
0506 bool ret;
0507
0508 kn = kernfs_find_and_get(parent, attr->name);
0509 if (WARN_ON_ONCE(!kn))
0510 return false;
0511
0512 ret = kernfs_remove_self(kn);
0513
0514 kernfs_put(kn);
0515 return ret;
0516 }
0517 EXPORT_SYMBOL_GPL(sysfs_remove_file_self);
0518
0519 void sysfs_remove_files(struct kobject *kobj, const struct attribute * const *ptr)
0520 {
0521 int i;
0522
0523 for (i = 0; ptr[i]; i++)
0524 sysfs_remove_file(kobj, ptr[i]);
0525 }
0526 EXPORT_SYMBOL_GPL(sysfs_remove_files);
0527
0528
0529
0530
0531
0532
0533
0534 void sysfs_remove_file_from_group(struct kobject *kobj,
0535 const struct attribute *attr, const char *group)
0536 {
0537 struct kernfs_node *parent;
0538
0539 if (group) {
0540 parent = kernfs_find_and_get(kobj->sd, group);
0541 } else {
0542 parent = kobj->sd;
0543 kernfs_get(parent);
0544 }
0545
0546 if (parent) {
0547 kernfs_remove_by_name(parent, attr->name);
0548 kernfs_put(parent);
0549 }
0550 }
0551 EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
0552
0553
0554
0555
0556
0557
0558 int sysfs_create_bin_file(struct kobject *kobj,
0559 const struct bin_attribute *attr)
0560 {
0561 kuid_t uid;
0562 kgid_t gid;
0563
0564 if (WARN_ON(!kobj || !kobj->sd || !attr))
0565 return -EINVAL;
0566
0567 kobject_get_ownership(kobj, &uid, &gid);
0568 return sysfs_add_bin_file_mode_ns(kobj->sd, attr, attr->attr.mode, uid,
0569 gid, NULL);
0570 }
0571 EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
0572
0573
0574
0575
0576
0577
0578 void sysfs_remove_bin_file(struct kobject *kobj,
0579 const struct bin_attribute *attr)
0580 {
0581 kernfs_remove_by_name(kobj->sd, attr->attr.name);
0582 }
0583 EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
0584
0585 static int internal_change_owner(struct kernfs_node *kn, kuid_t kuid,
0586 kgid_t kgid)
0587 {
0588 struct iattr newattrs = {
0589 .ia_valid = ATTR_UID | ATTR_GID,
0590 .ia_uid = kuid,
0591 .ia_gid = kgid,
0592 };
0593 return kernfs_setattr(kn, &newattrs);
0594 }
0595
0596
0597
0598
0599
0600
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610 int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ,
0611 const char *name, kuid_t kuid, kgid_t kgid)
0612 {
0613 struct kernfs_node *kn = NULL;
0614 int error;
0615
0616 if (!name || !kobj->state_in_sysfs || !targ->state_in_sysfs)
0617 return -EINVAL;
0618
0619 error = -ENOENT;
0620 kn = kernfs_find_and_get_ns(kobj->sd, name, targ->sd->ns);
0621 if (!kn)
0622 goto out;
0623
0624 error = -EINVAL;
0625 if (kernfs_type(kn) != KERNFS_LINK)
0626 goto out;
0627 if (kn->symlink.target_kn->priv != targ)
0628 goto out;
0629
0630 error = internal_change_owner(kn, kuid, kgid);
0631
0632 out:
0633 kernfs_put(kn);
0634 return error;
0635 }
0636
0637
0638
0639
0640
0641
0642
0643
0644
0645
0646
0647
0648
0649 int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid,
0650 kgid_t kgid)
0651 {
0652 struct kernfs_node *kn;
0653 int error;
0654
0655 if (!name)
0656 return -EINVAL;
0657
0658 if (!kobj->state_in_sysfs)
0659 return -EINVAL;
0660
0661 kn = kernfs_find_and_get(kobj->sd, name);
0662 if (!kn)
0663 return -ENOENT;
0664
0665 error = internal_change_owner(kn, kuid, kgid);
0666
0667 kernfs_put(kn);
0668
0669 return error;
0670 }
0671 EXPORT_SYMBOL_GPL(sysfs_file_change_owner);
0672
0673
0674
0675
0676
0677
0678
0679
0680
0681
0682
0683
0684
0685
0686
0687
0688
0689
0690
0691 int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid)
0692 {
0693 int error;
0694 const struct kobj_type *ktype;
0695
0696 if (!kobj->state_in_sysfs)
0697 return -EINVAL;
0698
0699
0700 error = internal_change_owner(kobj->sd, kuid, kgid);
0701 if (error)
0702 return error;
0703
0704 ktype = get_ktype(kobj);
0705 if (ktype) {
0706
0707
0708
0709
0710 error = sysfs_groups_change_owner(kobj, ktype->default_groups,
0711 kuid, kgid);
0712 if (error)
0713 return error;
0714 }
0715
0716 return 0;
0717 }
0718 EXPORT_SYMBOL_GPL(sysfs_change_owner);
0719
0720
0721
0722
0723
0724
0725
0726
0727
0728
0729 int sysfs_emit(char *buf, const char *fmt, ...)
0730 {
0731 va_list args;
0732 int len;
0733
0734 if (WARN(!buf || offset_in_page(buf),
0735 "invalid sysfs_emit: buf:%p\n", buf))
0736 return 0;
0737
0738 va_start(args, fmt);
0739 len = vscnprintf(buf, PAGE_SIZE, fmt, args);
0740 va_end(args);
0741
0742 return len;
0743 }
0744 EXPORT_SYMBOL_GPL(sysfs_emit);
0745
0746
0747
0748
0749
0750
0751
0752
0753
0754
0755
0756
0757 int sysfs_emit_at(char *buf, int at, const char *fmt, ...)
0758 {
0759 va_list args;
0760 int len;
0761
0762 if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE,
0763 "invalid sysfs_emit_at: buf:%p at:%d\n", buf, at))
0764 return 0;
0765
0766 va_start(args, fmt);
0767 len = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args);
0768 va_end(args);
0769
0770 return len;
0771 }
0772 EXPORT_SYMBOL_GPL(sysfs_emit_at);