1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C), 2008-2021, OPPO Mobile Comm Corp., Ltd. 4 * https://www.oppo.com/ 5 */ 6 #include <linux/sysfs.h> 7 #include <linux/kobject.h> 8 9 #include "internal.h" 10 11 enum { 12 attr_feature, 13 attr_drop_caches, 14 attr_pointer_ui, 15 attr_pointer_bool, 16 }; 17 18 enum { 19 struct_erofs_sb_info, 20 struct_erofs_mount_opts, 21 }; 22 23 struct erofs_attr { 24 struct attribute attr; 25 short attr_id; 26 int struct_type, offset; 27 }; 28 29 #define EROFS_ATTR(_name, _mode, _id) \ 30 static struct erofs_attr erofs_attr_##_name = { \ 31 .attr = {.name = __stringify(_name), .mode = _mode }, \ 32 .attr_id = attr_##_id, \ 33 } 34 #define EROFS_ATTR_FUNC(_name, _mode) EROFS_ATTR(_name, _mode, _name) 35 #define EROFS_ATTR_FEATURE(_name) EROFS_ATTR(_name, 0444, feature) 36 37 #define EROFS_ATTR_OFFSET(_name, _mode, _id, _struct) \ 38 static struct erofs_attr erofs_attr_##_name = { \ 39 .attr = {.name = __stringify(_name), .mode = _mode }, \ 40 .attr_id = attr_##_id, \ 41 .struct_type = struct_##_struct, \ 42 .offset = offsetof(struct _struct, _name),\ 43 } 44 45 #define EROFS_ATTR_RW(_name, _id, _struct) \ 46 EROFS_ATTR_OFFSET(_name, 0644, _id, _struct) 47 48 #define EROFS_RO_ATTR(_name, _id, _struct) \ 49 EROFS_ATTR_OFFSET(_name, 0444, _id, _struct) 50 51 #define EROFS_ATTR_RW_UI(_name, _struct) \ 52 EROFS_ATTR_RW(_name, pointer_ui, _struct) 53 54 #define EROFS_ATTR_RW_BOOL(_name, _struct) \ 55 EROFS_ATTR_RW(_name, pointer_bool, _struct) 56 57 #define ATTR_LIST(name) (&erofs_attr_##name.attr) 58 59 #ifdef CONFIG_EROFS_FS_ZIP 60 EROFS_ATTR_RW_UI(sync_decompress, erofs_mount_opts); 61 EROFS_ATTR_FUNC(drop_caches, 0200); 62 #endif 63 64 static struct attribute *erofs_attrs[] = { 65 #ifdef CONFIG_EROFS_FS_ZIP 66 ATTR_LIST(sync_decompress), 67 ATTR_LIST(drop_caches), 68 #endif 69 NULL, 70 }; 71 ATTRIBUTE_GROUPS(erofs); 72 73 /* Features this copy of erofs supports */ 74 EROFS_ATTR_FEATURE(zero_padding); 75 EROFS_ATTR_FEATURE(compr_cfgs); 76 EROFS_ATTR_FEATURE(big_pcluster); 77 EROFS_ATTR_FEATURE(chunked_file); 78 EROFS_ATTR_FEATURE(device_table); 79 EROFS_ATTR_FEATURE(compr_head2); 80 EROFS_ATTR_FEATURE(sb_chksum); 81 EROFS_ATTR_FEATURE(ztailpacking); 82 EROFS_ATTR_FEATURE(fragments); 83 EROFS_ATTR_FEATURE(dedupe); 84 85 static struct attribute *erofs_feat_attrs[] = { 86 ATTR_LIST(zero_padding), 87 ATTR_LIST(compr_cfgs), 88 ATTR_LIST(big_pcluster), 89 ATTR_LIST(chunked_file), 90 ATTR_LIST(device_table), 91 ATTR_LIST(compr_head2), 92 ATTR_LIST(sb_chksum), 93 ATTR_LIST(ztailpacking), 94 ATTR_LIST(fragments), 95 ATTR_LIST(dedupe), 96 NULL, 97 }; 98 ATTRIBUTE_GROUPS(erofs_feat); 99 100 static unsigned char *__struct_ptr(struct erofs_sb_info *sbi, 101 int struct_type, int offset) 102 { 103 if (struct_type == struct_erofs_sb_info) 104 return (unsigned char *)sbi + offset; 105 if (struct_type == struct_erofs_mount_opts) 106 return (unsigned char *)&sbi->opt + offset; 107 return NULL; 108 } 109 110 static ssize_t erofs_attr_show(struct kobject *kobj, 111 struct attribute *attr, char *buf) 112 { 113 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 114 s_kobj); 115 struct erofs_attr *a = container_of(attr, struct erofs_attr, attr); 116 unsigned char *ptr = __struct_ptr(sbi, a->struct_type, a->offset); 117 118 switch (a->attr_id) { 119 case attr_feature: 120 return sysfs_emit(buf, "supported\n"); 121 case attr_pointer_ui: 122 if (!ptr) 123 return 0; 124 return sysfs_emit(buf, "%u\n", *(unsigned int *)ptr); 125 case attr_pointer_bool: 126 if (!ptr) 127 return 0; 128 return sysfs_emit(buf, "%d\n", *(bool *)ptr); 129 } 130 return 0; 131 } 132 133 static ssize_t erofs_attr_store(struct kobject *kobj, struct attribute *attr, 134 const char *buf, size_t len) 135 { 136 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 137 s_kobj); 138 struct erofs_attr *a = container_of(attr, struct erofs_attr, attr); 139 unsigned char *ptr = __struct_ptr(sbi, a->struct_type, a->offset); 140 unsigned long t; 141 int ret; 142 143 switch (a->attr_id) { 144 case attr_pointer_ui: 145 if (!ptr) 146 return 0; 147 ret = kstrtoul(skip_spaces(buf), 0, &t); 148 if (ret) 149 return ret; 150 if (t != (unsigned int)t) 151 return -ERANGE; 152 #ifdef CONFIG_EROFS_FS_ZIP 153 if (!strcmp(a->attr.name, "sync_decompress") && 154 (t > EROFS_SYNC_DECOMPRESS_FORCE_OFF)) 155 return -EINVAL; 156 #endif 157 *(unsigned int *)ptr = t; 158 return len; 159 case attr_pointer_bool: 160 if (!ptr) 161 return 0; 162 ret = kstrtoul(skip_spaces(buf), 0, &t); 163 if (ret) 164 return ret; 165 if (t != 0 && t != 1) 166 return -EINVAL; 167 *(bool *)ptr = !!t; 168 return len; 169 #ifdef CONFIG_EROFS_FS_ZIP 170 case attr_drop_caches: 171 ret = kstrtoul(skip_spaces(buf), 0, &t); 172 if (ret) 173 return ret; 174 if (t < 1 || t > 3) 175 return -EINVAL; 176 177 if (t & 2) 178 z_erofs_shrink_scan(sbi, ~0UL); 179 if (t & 1) 180 invalidate_mapping_pages(MNGD_MAPPING(sbi), 0, -1); 181 return len; 182 #endif 183 } 184 return 0; 185 } 186 187 static void erofs_sb_release(struct kobject *kobj) 188 { 189 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 190 s_kobj); 191 complete(&sbi->s_kobj_unregister); 192 } 193 194 static const struct sysfs_ops erofs_attr_ops = { 195 .show = erofs_attr_show, 196 .store = erofs_attr_store, 197 }; 198 199 static const struct kobj_type erofs_sb_ktype = { 200 .default_groups = erofs_groups, 201 .sysfs_ops = &erofs_attr_ops, 202 .release = erofs_sb_release, 203 }; 204 205 static const struct kobj_type erofs_ktype = { 206 .sysfs_ops = &erofs_attr_ops, 207 }; 208 209 static struct kset erofs_root = { 210 .kobj = {.ktype = &erofs_ktype}, 211 }; 212 213 static const struct kobj_type erofs_feat_ktype = { 214 .default_groups = erofs_feat_groups, 215 .sysfs_ops = &erofs_attr_ops, 216 }; 217 218 static struct kobject erofs_feat = { 219 .kset = &erofs_root, 220 }; 221 222 int erofs_register_sysfs(struct super_block *sb) 223 { 224 struct erofs_sb_info *sbi = EROFS_SB(sb); 225 int err; 226 227 sbi->s_kobj.kset = &erofs_root; 228 init_completion(&sbi->s_kobj_unregister); 229 err = kobject_init_and_add(&sbi->s_kobj, &erofs_sb_ktype, NULL, "%s", 230 sb->s_sysfs_name); 231 if (err) { 232 kobject_put(&sbi->s_kobj); 233 wait_for_completion(&sbi->s_kobj_unregister); 234 } 235 return err; 236 } 237 238 void erofs_unregister_sysfs(struct super_block *sb) 239 { 240 struct erofs_sb_info *sbi = EROFS_SB(sb); 241 242 if (sbi->s_kobj.state_in_sysfs) { 243 kobject_del(&sbi->s_kobj); 244 kobject_put(&sbi->s_kobj); 245 wait_for_completion(&sbi->s_kobj_unregister); 246 } 247 } 248 249 int __init erofs_init_sysfs(void) 250 { 251 int ret; 252 253 kobject_set_name(&erofs_root.kobj, "erofs"); 254 erofs_root.kobj.parent = fs_kobj; 255 ret = kset_register(&erofs_root); 256 if (ret) 257 goto root_err; 258 259 ret = kobject_init_and_add(&erofs_feat, &erofs_feat_ktype, 260 NULL, "features"); 261 if (ret) 262 goto feat_err; 263 return ret; 264 265 feat_err: 266 kobject_put(&erofs_feat); 267 kset_unregister(&erofs_root); 268 root_err: 269 return ret; 270 } 271 272 void erofs_exit_sysfs(void) 273 { 274 kobject_put(&erofs_feat); 275 kset_unregister(&erofs_root); 276 } 277