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 EROFS_ATTR_FEATURE(48bit); 85 86 static struct attribute *erofs_feat_attrs[] = { 87 ATTR_LIST(zero_padding), 88 ATTR_LIST(compr_cfgs), 89 ATTR_LIST(big_pcluster), 90 ATTR_LIST(chunked_file), 91 ATTR_LIST(device_table), 92 ATTR_LIST(compr_head2), 93 ATTR_LIST(sb_chksum), 94 ATTR_LIST(ztailpacking), 95 ATTR_LIST(fragments), 96 ATTR_LIST(dedupe), 97 ATTR_LIST(48bit), 98 NULL, 99 }; 100 ATTRIBUTE_GROUPS(erofs_feat); 101 102 static unsigned char *__struct_ptr(struct erofs_sb_info *sbi, 103 int struct_type, int offset) 104 { 105 if (struct_type == struct_erofs_sb_info) 106 return (unsigned char *)sbi + offset; 107 if (struct_type == struct_erofs_mount_opts) 108 return (unsigned char *)&sbi->opt + offset; 109 return NULL; 110 } 111 112 static ssize_t erofs_attr_show(struct kobject *kobj, 113 struct attribute *attr, char *buf) 114 { 115 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 116 s_kobj); 117 struct erofs_attr *a = container_of(attr, struct erofs_attr, attr); 118 unsigned char *ptr = __struct_ptr(sbi, a->struct_type, a->offset); 119 120 switch (a->attr_id) { 121 case attr_feature: 122 return sysfs_emit(buf, "supported\n"); 123 case attr_pointer_ui: 124 if (!ptr) 125 return 0; 126 return sysfs_emit(buf, "%u\n", *(unsigned int *)ptr); 127 case attr_pointer_bool: 128 if (!ptr) 129 return 0; 130 return sysfs_emit(buf, "%d\n", *(bool *)ptr); 131 } 132 return 0; 133 } 134 135 static ssize_t erofs_attr_store(struct kobject *kobj, struct attribute *attr, 136 const char *buf, size_t len) 137 { 138 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 139 s_kobj); 140 struct erofs_attr *a = container_of(attr, struct erofs_attr, attr); 141 unsigned char *ptr = __struct_ptr(sbi, a->struct_type, a->offset); 142 unsigned long t; 143 int ret; 144 145 switch (a->attr_id) { 146 case attr_pointer_ui: 147 if (!ptr) 148 return 0; 149 ret = kstrtoul(skip_spaces(buf), 0, &t); 150 if (ret) 151 return ret; 152 if (t != (unsigned int)t) 153 return -ERANGE; 154 #ifdef CONFIG_EROFS_FS_ZIP 155 if (!strcmp(a->attr.name, "sync_decompress") && 156 (t > EROFS_SYNC_DECOMPRESS_FORCE_OFF)) 157 return -EINVAL; 158 #endif 159 *(unsigned int *)ptr = t; 160 return len; 161 case attr_pointer_bool: 162 if (!ptr) 163 return 0; 164 ret = kstrtoul(skip_spaces(buf), 0, &t); 165 if (ret) 166 return ret; 167 if (t != 0 && t != 1) 168 return -EINVAL; 169 *(bool *)ptr = !!t; 170 return len; 171 #ifdef CONFIG_EROFS_FS_ZIP 172 case attr_drop_caches: 173 ret = kstrtoul(skip_spaces(buf), 0, &t); 174 if (ret) 175 return ret; 176 if (t < 1 || t > 3) 177 return -EINVAL; 178 179 if (t & 2) 180 z_erofs_shrink_scan(sbi, ~0UL); 181 if (t & 1) 182 invalidate_mapping_pages(MNGD_MAPPING(sbi), 0, -1); 183 return len; 184 #endif 185 } 186 return 0; 187 } 188 189 static void erofs_sb_release(struct kobject *kobj) 190 { 191 struct erofs_sb_info *sbi = container_of(kobj, struct erofs_sb_info, 192 s_kobj); 193 complete(&sbi->s_kobj_unregister); 194 } 195 196 static const struct sysfs_ops erofs_attr_ops = { 197 .show = erofs_attr_show, 198 .store = erofs_attr_store, 199 }; 200 201 static const struct kobj_type erofs_sb_ktype = { 202 .default_groups = erofs_groups, 203 .sysfs_ops = &erofs_attr_ops, 204 .release = erofs_sb_release, 205 }; 206 207 static const struct kobj_type erofs_ktype = { 208 .sysfs_ops = &erofs_attr_ops, 209 }; 210 211 static struct kset erofs_root = { 212 .kobj = {.ktype = &erofs_ktype}, 213 }; 214 215 static const struct kobj_type erofs_feat_ktype = { 216 .default_groups = erofs_feat_groups, 217 .sysfs_ops = &erofs_attr_ops, 218 }; 219 220 static struct kobject erofs_feat = { 221 .kset = &erofs_root, 222 }; 223 224 int erofs_register_sysfs(struct super_block *sb) 225 { 226 struct erofs_sb_info *sbi = EROFS_SB(sb); 227 int err; 228 229 sbi->s_kobj.kset = &erofs_root; 230 init_completion(&sbi->s_kobj_unregister); 231 err = kobject_init_and_add(&sbi->s_kobj, &erofs_sb_ktype, NULL, "%s", 232 sb->s_sysfs_name); 233 if (err) { 234 kobject_put(&sbi->s_kobj); 235 wait_for_completion(&sbi->s_kobj_unregister); 236 } 237 return err; 238 } 239 240 void erofs_unregister_sysfs(struct super_block *sb) 241 { 242 struct erofs_sb_info *sbi = EROFS_SB(sb); 243 244 if (sbi->s_kobj.state_in_sysfs) { 245 kobject_del(&sbi->s_kobj); 246 kobject_put(&sbi->s_kobj); 247 wait_for_completion(&sbi->s_kobj_unregister); 248 } 249 } 250 251 int __init erofs_init_sysfs(void) 252 { 253 int ret; 254 255 kobject_set_name(&erofs_root.kobj, "erofs"); 256 erofs_root.kobj.parent = fs_kobj; 257 ret = kset_register(&erofs_root); 258 if (ret) 259 goto root_err; 260 261 ret = kobject_init_and_add(&erofs_feat, &erofs_feat_ktype, 262 NULL, "features"); 263 if (ret) 264 goto feat_err; 265 return ret; 266 267 feat_err: 268 kobject_put(&erofs_feat); 269 kset_unregister(&erofs_root); 270 root_err: 271 return ret; 272 } 273 274 void erofs_exit_sysfs(void) 275 { 276 kobject_put(&erofs_feat); 277 kset_unregister(&erofs_root); 278 } 279