| .. | .. |
|---|
| 19 | 19 | #include <linux/fs.h> |
|---|
| 20 | 20 | #include <linux/err.h> |
|---|
| 21 | 21 | #include <linux/mount.h> |
|---|
| 22 | | -#include <linux/parser.h> |
|---|
| 22 | +#include <linux/fs_context.h> |
|---|
| 23 | +#include <linux/fs_parser.h> |
|---|
| 23 | 24 | #include <linux/jffs2.h> |
|---|
| 24 | 25 | #include <linux/pagemap.h> |
|---|
| 25 | 26 | #include <linux/mtd/super.h> |
|---|
| .. | .. |
|---|
| 44 | 45 | return &f->vfs_inode; |
|---|
| 45 | 46 | } |
|---|
| 46 | 47 | |
|---|
| 47 | | -static void jffs2_i_callback(struct rcu_head *head) |
|---|
| 48 | +static void jffs2_free_inode(struct inode *inode) |
|---|
| 48 | 49 | { |
|---|
| 49 | | - struct inode *inode = container_of(head, struct inode, i_rcu); |
|---|
| 50 | 50 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); |
|---|
| 51 | 51 | |
|---|
| 52 | 52 | kfree(f->target); |
|---|
| 53 | 53 | kmem_cache_free(jffs2_inode_cachep, f); |
|---|
| 54 | | -} |
|---|
| 55 | | - |
|---|
| 56 | | -static void jffs2_destroy_inode(struct inode *inode) |
|---|
| 57 | | -{ |
|---|
| 58 | | - call_rcu(&inode->i_rcu, jffs2_i_callback); |
|---|
| 59 | 54 | } |
|---|
| 60 | 55 | |
|---|
| 61 | 56 | static void jffs2_i_init_once(void *foo) |
|---|
| .. | .. |
|---|
| 93 | 88 | |
|---|
| 94 | 89 | if (opts->override_compr) |
|---|
| 95 | 90 | seq_printf(s, ",compr=%s", jffs2_compr_name(opts->compr)); |
|---|
| 96 | | - if (opts->rp_size) |
|---|
| 91 | + if (opts->set_rp_size) |
|---|
| 97 | 92 | seq_printf(s, ",rp_size=%u", opts->rp_size / 1024); |
|---|
| 98 | 93 | |
|---|
| 99 | 94 | return 0; |
|---|
| .. | .. |
|---|
| 163 | 158 | /* |
|---|
| 164 | 159 | * JFFS2 mount options. |
|---|
| 165 | 160 | * |
|---|
| 161 | + * Opt_source: The source device |
|---|
| 166 | 162 | * Opt_override_compr: override default compressor |
|---|
| 167 | 163 | * Opt_rp_size: size of reserved pool in KiB |
|---|
| 168 | | - * Opt_err: just end of array marker |
|---|
| 169 | 164 | */ |
|---|
| 170 | 165 | enum { |
|---|
| 171 | 166 | Opt_override_compr, |
|---|
| 172 | 167 | Opt_rp_size, |
|---|
| 173 | | - Opt_err, |
|---|
| 174 | 168 | }; |
|---|
| 175 | 169 | |
|---|
| 176 | | -static const match_table_t tokens = { |
|---|
| 177 | | - {Opt_override_compr, "compr=%s"}, |
|---|
| 178 | | - {Opt_rp_size, "rp_size=%u"}, |
|---|
| 179 | | - {Opt_err, NULL}, |
|---|
| 180 | | -}; |
|---|
| 181 | | - |
|---|
| 182 | | -static int jffs2_parse_options(struct jffs2_sb_info *c, char *data) |
|---|
| 183 | | -{ |
|---|
| 184 | | - substring_t args[MAX_OPT_ARGS]; |
|---|
| 185 | | - char *p, *name; |
|---|
| 186 | | - unsigned int opt; |
|---|
| 187 | | - |
|---|
| 188 | | - if (!data) |
|---|
| 189 | | - return 0; |
|---|
| 190 | | - |
|---|
| 191 | | - while ((p = strsep(&data, ","))) { |
|---|
| 192 | | - int token; |
|---|
| 193 | | - |
|---|
| 194 | | - if (!*p) |
|---|
| 195 | | - continue; |
|---|
| 196 | | - |
|---|
| 197 | | - token = match_token(p, tokens, args); |
|---|
| 198 | | - switch (token) { |
|---|
| 199 | | - case Opt_override_compr: |
|---|
| 200 | | - name = match_strdup(&args[0]); |
|---|
| 201 | | - |
|---|
| 202 | | - if (!name) |
|---|
| 203 | | - return -ENOMEM; |
|---|
| 204 | | - if (!strcmp(name, "none")) |
|---|
| 205 | | - c->mount_opts.compr = JFFS2_COMPR_MODE_NONE; |
|---|
| 170 | +static const struct constant_table jffs2_param_compr[] = { |
|---|
| 171 | + {"none", JFFS2_COMPR_MODE_NONE }, |
|---|
| 206 | 172 | #ifdef CONFIG_JFFS2_LZO |
|---|
| 207 | | - else if (!strcmp(name, "lzo")) |
|---|
| 208 | | - c->mount_opts.compr = JFFS2_COMPR_MODE_FORCELZO; |
|---|
| 173 | + {"lzo", JFFS2_COMPR_MODE_FORCELZO }, |
|---|
| 209 | 174 | #endif |
|---|
| 210 | 175 | #ifdef CONFIG_JFFS2_ZLIB |
|---|
| 211 | | - else if (!strcmp(name, "zlib")) |
|---|
| 212 | | - c->mount_opts.compr = |
|---|
| 213 | | - JFFS2_COMPR_MODE_FORCEZLIB; |
|---|
| 176 | + {"zlib", JFFS2_COMPR_MODE_FORCEZLIB }, |
|---|
| 214 | 177 | #endif |
|---|
| 215 | | - else { |
|---|
| 216 | | - pr_err("Error: unknown compressor \"%s\"\n", |
|---|
| 217 | | - name); |
|---|
| 218 | | - kfree(name); |
|---|
| 219 | | - return -EINVAL; |
|---|
| 220 | | - } |
|---|
| 221 | | - kfree(name); |
|---|
| 222 | | - c->mount_opts.override_compr = true; |
|---|
| 223 | | - break; |
|---|
| 224 | | - case Opt_rp_size: |
|---|
| 225 | | - if (match_int(&args[0], &opt)) |
|---|
| 226 | | - return -EINVAL; |
|---|
| 227 | | - opt *= 1024; |
|---|
| 228 | | - if (opt > c->mtd->size) { |
|---|
| 229 | | - pr_warn("Too large reserve pool specified, max " |
|---|
| 230 | | - "is %llu KB\n", c->mtd->size / 1024); |
|---|
| 231 | | - return -EINVAL; |
|---|
| 232 | | - } |
|---|
| 233 | | - c->mount_opts.rp_size = opt; |
|---|
| 234 | | - break; |
|---|
| 235 | | - default: |
|---|
| 236 | | - pr_err("Error: unrecognized mount option '%s' or missing value\n", |
|---|
| 237 | | - p); |
|---|
| 238 | | - return -EINVAL; |
|---|
| 239 | | - } |
|---|
| 178 | + {} |
|---|
| 179 | +}; |
|---|
| 180 | + |
|---|
| 181 | +static const struct fs_parameter_spec jffs2_fs_parameters[] = { |
|---|
| 182 | + fsparam_enum ("compr", Opt_override_compr, jffs2_param_compr), |
|---|
| 183 | + fsparam_u32 ("rp_size", Opt_rp_size), |
|---|
| 184 | + {} |
|---|
| 185 | +}; |
|---|
| 186 | + |
|---|
| 187 | +static int jffs2_parse_param(struct fs_context *fc, struct fs_parameter *param) |
|---|
| 188 | +{ |
|---|
| 189 | + struct fs_parse_result result; |
|---|
| 190 | + struct jffs2_sb_info *c = fc->s_fs_info; |
|---|
| 191 | + int opt; |
|---|
| 192 | + |
|---|
| 193 | + opt = fs_parse(fc, jffs2_fs_parameters, param, &result); |
|---|
| 194 | + if (opt < 0) |
|---|
| 195 | + return opt; |
|---|
| 196 | + |
|---|
| 197 | + switch (opt) { |
|---|
| 198 | + case Opt_override_compr: |
|---|
| 199 | + c->mount_opts.compr = result.uint_32; |
|---|
| 200 | + c->mount_opts.override_compr = true; |
|---|
| 201 | + break; |
|---|
| 202 | + case Opt_rp_size: |
|---|
| 203 | + if (result.uint_32 > UINT_MAX / 1024) |
|---|
| 204 | + return invalf(fc, "jffs2: rp_size unrepresentable"); |
|---|
| 205 | + c->mount_opts.rp_size = result.uint_32 * 1024; |
|---|
| 206 | + c->mount_opts.set_rp_size = true; |
|---|
| 207 | + break; |
|---|
| 208 | + default: |
|---|
| 209 | + return -EINVAL; |
|---|
| 240 | 210 | } |
|---|
| 241 | 211 | |
|---|
| 242 | 212 | return 0; |
|---|
| 243 | 213 | } |
|---|
| 244 | 214 | |
|---|
| 245 | | -static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data) |
|---|
| 215 | +static inline void jffs2_update_mount_opts(struct fs_context *fc) |
|---|
| 246 | 216 | { |
|---|
| 247 | | - struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); |
|---|
| 248 | | - int err; |
|---|
| 217 | + struct jffs2_sb_info *new_c = fc->s_fs_info; |
|---|
| 218 | + struct jffs2_sb_info *c = JFFS2_SB_INFO(fc->root->d_sb); |
|---|
| 219 | + |
|---|
| 220 | + mutex_lock(&c->alloc_sem); |
|---|
| 221 | + if (new_c->mount_opts.override_compr) { |
|---|
| 222 | + c->mount_opts.override_compr = new_c->mount_opts.override_compr; |
|---|
| 223 | + c->mount_opts.compr = new_c->mount_opts.compr; |
|---|
| 224 | + } |
|---|
| 225 | + if (new_c->mount_opts.set_rp_size) { |
|---|
| 226 | + c->mount_opts.set_rp_size = new_c->mount_opts.set_rp_size; |
|---|
| 227 | + c->mount_opts.rp_size = new_c->mount_opts.rp_size; |
|---|
| 228 | + } |
|---|
| 229 | + mutex_unlock(&c->alloc_sem); |
|---|
| 230 | +} |
|---|
| 231 | + |
|---|
| 232 | +static int jffs2_reconfigure(struct fs_context *fc) |
|---|
| 233 | +{ |
|---|
| 234 | + struct super_block *sb = fc->root->d_sb; |
|---|
| 249 | 235 | |
|---|
| 250 | 236 | sync_filesystem(sb); |
|---|
| 251 | | - err = jffs2_parse_options(c, data); |
|---|
| 252 | | - if (err) |
|---|
| 253 | | - return -EINVAL; |
|---|
| 237 | + jffs2_update_mount_opts(fc); |
|---|
| 254 | 238 | |
|---|
| 255 | | - return jffs2_do_remount_fs(sb, flags, data); |
|---|
| 239 | + return jffs2_do_remount_fs(sb, fc); |
|---|
| 256 | 240 | } |
|---|
| 257 | 241 | |
|---|
| 258 | 242 | static const struct super_operations jffs2_super_operations = |
|---|
| 259 | 243 | { |
|---|
| 260 | 244 | .alloc_inode = jffs2_alloc_inode, |
|---|
| 261 | | - .destroy_inode =jffs2_destroy_inode, |
|---|
| 245 | + .free_inode = jffs2_free_inode, |
|---|
| 262 | 246 | .put_super = jffs2_put_super, |
|---|
| 263 | 247 | .statfs = jffs2_statfs, |
|---|
| 264 | | - .remount_fs = jffs2_remount_fs, |
|---|
| 265 | 248 | .evict_inode = jffs2_evict_inode, |
|---|
| 266 | 249 | .dirty_inode = jffs2_dirty_inode, |
|---|
| 267 | 250 | .show_options = jffs2_show_options, |
|---|
| .. | .. |
|---|
| 271 | 254 | /* |
|---|
| 272 | 255 | * fill in the superblock |
|---|
| 273 | 256 | */ |
|---|
| 274 | | -static int jffs2_fill_super(struct super_block *sb, void *data, int silent) |
|---|
| 257 | +static int jffs2_fill_super(struct super_block *sb, struct fs_context *fc) |
|---|
| 275 | 258 | { |
|---|
| 276 | | - struct jffs2_sb_info *c; |
|---|
| 277 | | - int ret; |
|---|
| 259 | + struct jffs2_sb_info *c = sb->s_fs_info; |
|---|
| 278 | 260 | |
|---|
| 279 | 261 | jffs2_dbg(1, "jffs2_get_sb_mtd():" |
|---|
| 280 | 262 | " New superblock for device %d (\"%s\")\n", |
|---|
| 281 | 263 | sb->s_mtd->index, sb->s_mtd->name); |
|---|
| 282 | 264 | |
|---|
| 283 | | - c = kzalloc(sizeof(*c), GFP_KERNEL); |
|---|
| 284 | | - if (!c) |
|---|
| 285 | | - return -ENOMEM; |
|---|
| 286 | | - |
|---|
| 287 | 265 | c->mtd = sb->s_mtd; |
|---|
| 288 | 266 | c->os_priv = sb; |
|---|
| 289 | | - sb->s_fs_info = c; |
|---|
| 290 | 267 | |
|---|
| 291 | | - ret = jffs2_parse_options(c, data); |
|---|
| 292 | | - if (ret) |
|---|
| 293 | | - return -EINVAL; |
|---|
| 268 | + if (c->mount_opts.rp_size > c->mtd->size) |
|---|
| 269 | + return invalf(fc, "jffs2: Too large reserve pool specified, max is %llu KB", |
|---|
| 270 | + c->mtd->size / 1024); |
|---|
| 294 | 271 | |
|---|
| 295 | 272 | /* Initialize JFFS2 superblock locks, the further initialization will |
|---|
| 296 | 273 | * be done later */ |
|---|
| .. | .. |
|---|
| 308 | 285 | #ifdef CONFIG_JFFS2_FS_POSIX_ACL |
|---|
| 309 | 286 | sb->s_flags |= SB_POSIXACL; |
|---|
| 310 | 287 | #endif |
|---|
| 311 | | - ret = jffs2_do_fill_super(sb, data, silent); |
|---|
| 312 | | - return ret; |
|---|
| 288 | + return jffs2_do_fill_super(sb, fc); |
|---|
| 313 | 289 | } |
|---|
| 314 | 290 | |
|---|
| 315 | | -static struct dentry *jffs2_mount(struct file_system_type *fs_type, |
|---|
| 316 | | - int flags, const char *dev_name, |
|---|
| 317 | | - void *data) |
|---|
| 291 | +static int jffs2_get_tree(struct fs_context *fc) |
|---|
| 318 | 292 | { |
|---|
| 319 | | - return mount_mtd(fs_type, flags, dev_name, data, jffs2_fill_super); |
|---|
| 293 | + return get_tree_mtd(fc, jffs2_fill_super); |
|---|
| 294 | +} |
|---|
| 295 | + |
|---|
| 296 | +static void jffs2_free_fc(struct fs_context *fc) |
|---|
| 297 | +{ |
|---|
| 298 | + kfree(fc->s_fs_info); |
|---|
| 299 | +} |
|---|
| 300 | + |
|---|
| 301 | +static const struct fs_context_operations jffs2_context_ops = { |
|---|
| 302 | + .free = jffs2_free_fc, |
|---|
| 303 | + .parse_param = jffs2_parse_param, |
|---|
| 304 | + .get_tree = jffs2_get_tree, |
|---|
| 305 | + .reconfigure = jffs2_reconfigure, |
|---|
| 306 | +}; |
|---|
| 307 | + |
|---|
| 308 | +static int jffs2_init_fs_context(struct fs_context *fc) |
|---|
| 309 | +{ |
|---|
| 310 | + struct jffs2_sb_info *ctx; |
|---|
| 311 | + |
|---|
| 312 | + ctx = kzalloc(sizeof(struct jffs2_sb_info), GFP_KERNEL); |
|---|
| 313 | + if (!ctx) |
|---|
| 314 | + return -ENOMEM; |
|---|
| 315 | + |
|---|
| 316 | + fc->s_fs_info = ctx; |
|---|
| 317 | + fc->ops = &jffs2_context_ops; |
|---|
| 318 | + return 0; |
|---|
| 320 | 319 | } |
|---|
| 321 | 320 | |
|---|
| 322 | 321 | static void jffs2_put_super (struct super_block *sb) |
|---|
| .. | .. |
|---|
| 353 | 352 | static struct file_system_type jffs2_fs_type = { |
|---|
| 354 | 353 | .owner = THIS_MODULE, |
|---|
| 355 | 354 | .name = "jffs2", |
|---|
| 356 | | - .mount = jffs2_mount, |
|---|
| 355 | + .init_fs_context = jffs2_init_fs_context, |
|---|
| 356 | + .parameters = jffs2_fs_parameters, |
|---|
| 357 | 357 | .kill_sb = jffs2_kill_sb, |
|---|
| 358 | 358 | }; |
|---|
| 359 | 359 | MODULE_ALIAS_FS("jffs2"); |
|---|
| .. | .. |
|---|
| 439 | 439 | MODULE_AUTHOR("Red Hat, Inc."); |
|---|
| 440 | 440 | MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for |
|---|
| 441 | 441 | // the sake of this tag. It's Free Software. |
|---|
| 442 | +MODULE_IMPORT_NS(ANDROID_GKI_VFS_EXPORT_ONLY); |
|---|