162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C), 2008-2021, OPPO Mobile Comm Corp., Ltd. 462306a36Sopenharmony_ci * https://www.oppo.com/ 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/sysfs.h> 762306a36Sopenharmony_ci#include <linux/kobject.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "internal.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cienum { 1262306a36Sopenharmony_ci attr_feature, 1362306a36Sopenharmony_ci attr_pointer_ui, 1462306a36Sopenharmony_ci attr_pointer_bool, 1562306a36Sopenharmony_ci}; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cienum { 1862306a36Sopenharmony_ci struct_erofs_sb_info, 1962306a36Sopenharmony_ci struct_erofs_mount_opts, 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct erofs_attr { 2362306a36Sopenharmony_ci struct attribute attr; 2462306a36Sopenharmony_ci short attr_id; 2562306a36Sopenharmony_ci int struct_type, offset; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define EROFS_ATTR(_name, _mode, _id) \ 2962306a36Sopenharmony_cistatic struct erofs_attr erofs_attr_##_name = { \ 3062306a36Sopenharmony_ci .attr = {.name = __stringify(_name), .mode = _mode }, \ 3162306a36Sopenharmony_ci .attr_id = attr_##_id, \ 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci#define EROFS_ATTR_FUNC(_name, _mode) EROFS_ATTR(_name, _mode, _name) 3462306a36Sopenharmony_ci#define EROFS_ATTR_FEATURE(_name) EROFS_ATTR(_name, 0444, feature) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define EROFS_ATTR_OFFSET(_name, _mode, _id, _struct) \ 3762306a36Sopenharmony_cistatic struct erofs_attr erofs_attr_##_name = { \ 3862306a36Sopenharmony_ci .attr = {.name = __stringify(_name), .mode = _mode }, \ 3962306a36Sopenharmony_ci .attr_id = attr_##_id, \ 4062306a36Sopenharmony_ci .struct_type = struct_##_struct, \ 4162306a36Sopenharmony_ci .offset = offsetof(struct _struct, _name),\ 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define EROFS_ATTR_RW(_name, _id, _struct) \ 4562306a36Sopenharmony_ci EROFS_ATTR_OFFSET(_name, 0644, _id, _struct) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define EROFS_RO_ATTR(_name, _id, _struct) \ 4862306a36Sopenharmony_ci EROFS_ATTR_OFFSET(_name, 0444, _id, _struct) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define EROFS_ATTR_RW_UI(_name, _struct) \ 5162306a36Sopenharmony_ci EROFS_ATTR_RW(_name, pointer_ui, _struct) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define EROFS_ATTR_RW_BOOL(_name, _struct) \ 5462306a36Sopenharmony_ci EROFS_ATTR_RW(_name, pointer_bool, _struct) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define ATTR_LIST(name) (&erofs_attr_##name.attr) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 5962306a36Sopenharmony_ciEROFS_ATTR_RW_UI(sync_decompress, erofs_mount_opts); 6062306a36Sopenharmony_ci#endif 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic struct attribute *erofs_attrs[] = { 6362306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 6462306a36Sopenharmony_ci ATTR_LIST(sync_decompress), 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci NULL, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ciATTRIBUTE_GROUPS(erofs); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Features this copy of erofs supports */ 7162306a36Sopenharmony_ciEROFS_ATTR_FEATURE(zero_padding); 7262306a36Sopenharmony_ciEROFS_ATTR_FEATURE(compr_cfgs); 7362306a36Sopenharmony_ciEROFS_ATTR_FEATURE(big_pcluster); 7462306a36Sopenharmony_ciEROFS_ATTR_FEATURE(chunked_file); 7562306a36Sopenharmony_ciEROFS_ATTR_FEATURE(device_table); 7662306a36Sopenharmony_ciEROFS_ATTR_FEATURE(compr_head2); 7762306a36Sopenharmony_ciEROFS_ATTR_FEATURE(sb_chksum); 7862306a36Sopenharmony_ciEROFS_ATTR_FEATURE(ztailpacking); 7962306a36Sopenharmony_ciEROFS_ATTR_FEATURE(fragments); 8062306a36Sopenharmony_ciEROFS_ATTR_FEATURE(dedupe); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct attribute *erofs_feat_attrs[] = { 8362306a36Sopenharmony_ci ATTR_LIST(zero_padding), 8462306a36Sopenharmony_ci ATTR_LIST(compr_cfgs), 8562306a36Sopenharmony_ci ATTR_LIST(big_pcluster), 8662306a36Sopenharmony_ci ATTR_LIST(chunked_file), 8762306a36Sopenharmony_ci ATTR_LIST(device_table), 8862306a36Sopenharmony_ci ATTR_LIST(compr_head2), 8962306a36Sopenharmony_ci ATTR_LIST(sb_chksum), 9062306a36Sopenharmony_ci ATTR_LIST(ztailpacking), 9162306a36Sopenharmony_ci ATTR_LIST(fragments), 9262306a36Sopenharmony_ci ATTR_LIST(dedupe), 9362306a36Sopenharmony_ci NULL, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ciATTRIBUTE_GROUPS(erofs_feat); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic unsigned char *__struct_ptr(struct erofs_sb_info *sbi, 9862306a36Sopenharmony_ci int struct_type, int offset) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (struct_type == struct_erofs_sb_info) 10162306a36Sopenharmony_ci return (unsigned char *)sbi + offset; 10262306a36Sopenharmony_ci if (struct_type == struct_erofs_mount_opts) 10362306a36Sopenharmony_ci return (unsigned char *)&sbi->opt + offset; 10462306a36Sopenharmony_ci return NULL; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic ssize_t erofs_attr_show(struct kobject *kobj, 10862306a36Sopenharmony_ci struct attribute *attr, char *buf) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 11162306a36Sopenharmony_ci s_kobj); 11262306a36Sopenharmony_ci struct erofs_attr *a = container_of(attr, struct erofs_attr, attr); 11362306a36Sopenharmony_ci unsigned char *ptr = __struct_ptr(sbi, a->struct_type, a->offset); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci switch (a->attr_id) { 11662306a36Sopenharmony_ci case attr_feature: 11762306a36Sopenharmony_ci return sysfs_emit(buf, "supported\n"); 11862306a36Sopenharmony_ci case attr_pointer_ui: 11962306a36Sopenharmony_ci if (!ptr) 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", *(unsigned int *)ptr); 12262306a36Sopenharmony_ci case attr_pointer_bool: 12362306a36Sopenharmony_ci if (!ptr) 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", *(bool *)ptr); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic ssize_t erofs_attr_store(struct kobject *kobj, struct attribute *attr, 13162306a36Sopenharmony_ci const char *buf, size_t len) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 13462306a36Sopenharmony_ci s_kobj); 13562306a36Sopenharmony_ci struct erofs_attr *a = container_of(attr, struct erofs_attr, attr); 13662306a36Sopenharmony_ci unsigned char *ptr = __struct_ptr(sbi, a->struct_type, a->offset); 13762306a36Sopenharmony_ci unsigned long t; 13862306a36Sopenharmony_ci int ret; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci switch (a->attr_id) { 14162306a36Sopenharmony_ci case attr_pointer_ui: 14262306a36Sopenharmony_ci if (!ptr) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci ret = kstrtoul(skip_spaces(buf), 0, &t); 14562306a36Sopenharmony_ci if (ret) 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci if (t != (unsigned int)t) 14862306a36Sopenharmony_ci return -ERANGE; 14962306a36Sopenharmony_ci#ifdef CONFIG_EROFS_FS_ZIP 15062306a36Sopenharmony_ci if (!strcmp(a->attr.name, "sync_decompress") && 15162306a36Sopenharmony_ci (t > EROFS_SYNC_DECOMPRESS_FORCE_OFF)) 15262306a36Sopenharmony_ci return -EINVAL; 15362306a36Sopenharmony_ci#endif 15462306a36Sopenharmony_ci *(unsigned int *)ptr = t; 15562306a36Sopenharmony_ci return len; 15662306a36Sopenharmony_ci case attr_pointer_bool: 15762306a36Sopenharmony_ci if (!ptr) 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci ret = kstrtoul(skip_spaces(buf), 0, &t); 16062306a36Sopenharmony_ci if (ret) 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci if (t != 0 && t != 1) 16362306a36Sopenharmony_ci return -EINVAL; 16462306a36Sopenharmony_ci *(bool *)ptr = !!t; 16562306a36Sopenharmony_ci return len; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void erofs_sb_release(struct kobject *kobj) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 17362306a36Sopenharmony_ci s_kobj); 17462306a36Sopenharmony_ci complete(&sbi->s_kobj_unregister); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic const struct sysfs_ops erofs_attr_ops = { 17862306a36Sopenharmony_ci .show = erofs_attr_show, 17962306a36Sopenharmony_ci .store = erofs_attr_store, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct kobj_type erofs_sb_ktype = { 18362306a36Sopenharmony_ci .default_groups = erofs_groups, 18462306a36Sopenharmony_ci .sysfs_ops = &erofs_attr_ops, 18562306a36Sopenharmony_ci .release = erofs_sb_release, 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic const struct kobj_type erofs_ktype = { 18962306a36Sopenharmony_ci .sysfs_ops = &erofs_attr_ops, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic struct kset erofs_root = { 19362306a36Sopenharmony_ci .kobj = {.ktype = &erofs_ktype}, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic const struct kobj_type erofs_feat_ktype = { 19762306a36Sopenharmony_ci .default_groups = erofs_feat_groups, 19862306a36Sopenharmony_ci .sysfs_ops = &erofs_attr_ops, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic struct kobject erofs_feat = { 20262306a36Sopenharmony_ci .kset = &erofs_root, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciint erofs_register_sysfs(struct super_block *sb) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct erofs_sb_info *sbi = EROFS_SB(sb); 20862306a36Sopenharmony_ci char *name; 20962306a36Sopenharmony_ci char *str = NULL; 21062306a36Sopenharmony_ci int err; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (erofs_is_fscache_mode(sb)) { 21362306a36Sopenharmony_ci if (sbi->domain_id) { 21462306a36Sopenharmony_ci str = kasprintf(GFP_KERNEL, "%s,%s", sbi->domain_id, 21562306a36Sopenharmony_ci sbi->fsid); 21662306a36Sopenharmony_ci if (!str) 21762306a36Sopenharmony_ci return -ENOMEM; 21862306a36Sopenharmony_ci name = str; 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci name = sbi->fsid; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci name = sb->s_id; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci sbi->s_kobj.kset = &erofs_root; 22662306a36Sopenharmony_ci init_completion(&sbi->s_kobj_unregister); 22762306a36Sopenharmony_ci err = kobject_init_and_add(&sbi->s_kobj, &erofs_sb_ktype, NULL, "%s", name); 22862306a36Sopenharmony_ci kfree(str); 22962306a36Sopenharmony_ci if (err) 23062306a36Sopenharmony_ci goto put_sb_kobj; 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciput_sb_kobj: 23462306a36Sopenharmony_ci kobject_put(&sbi->s_kobj); 23562306a36Sopenharmony_ci wait_for_completion(&sbi->s_kobj_unregister); 23662306a36Sopenharmony_ci return err; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_civoid erofs_unregister_sysfs(struct super_block *sb) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct erofs_sb_info *sbi = EROFS_SB(sb); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (sbi->s_kobj.state_in_sysfs) { 24462306a36Sopenharmony_ci kobject_del(&sbi->s_kobj); 24562306a36Sopenharmony_ci kobject_put(&sbi->s_kobj); 24662306a36Sopenharmony_ci wait_for_completion(&sbi->s_kobj_unregister); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciint __init erofs_init_sysfs(void) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci int ret; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci kobject_set_name(&erofs_root.kobj, "erofs"); 25562306a36Sopenharmony_ci erofs_root.kobj.parent = fs_kobj; 25662306a36Sopenharmony_ci ret = kset_register(&erofs_root); 25762306a36Sopenharmony_ci if (ret) 25862306a36Sopenharmony_ci goto root_err; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ret = kobject_init_and_add(&erofs_feat, &erofs_feat_ktype, 26162306a36Sopenharmony_ci NULL, "features"); 26262306a36Sopenharmony_ci if (ret) 26362306a36Sopenharmony_ci goto feat_err; 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cifeat_err: 26762306a36Sopenharmony_ci kobject_put(&erofs_feat); 26862306a36Sopenharmony_ci kset_unregister(&erofs_root); 26962306a36Sopenharmony_ciroot_err: 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_civoid erofs_exit_sysfs(void) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci kobject_put(&erofs_feat); 27662306a36Sopenharmony_ci kset_unregister(&erofs_root); 27762306a36Sopenharmony_ci} 278