Blame view
fs/jffs2/super.c
10.2 KB
1da177e4c
|
1 2 3 |
/* * JFFS2 -- Journalling Flash File System, Version 2. * |
c00c310ea
|
4 |
* Copyright © 2001-2007 Red Hat, Inc. |
1da177e4c
|
5 6 7 8 9 |
* * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * |
1da177e4c
|
10 |
*/ |
5a528957e
|
11 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
1da177e4c
|
12 13 14 15 16 17 |
#include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/list.h> #include <linux/fs.h> |
9c74034f8
|
18 |
#include <linux/err.h> |
1da177e4c
|
19 |
#include <linux/mount.h> |
92abc475d
|
20 |
#include <linux/parser.h> |
1da177e4c
|
21 22 |
#include <linux/jffs2.h> #include <linux/pagemap.h> |
acaebfd8a
|
23 |
#include <linux/mtd/super.h> |
1da177e4c
|
24 25 |
#include <linux/ctype.h> #include <linux/namei.h> |
92abc475d
|
26 |
#include <linux/seq_file.h> |
5f556aab9
|
27 |
#include <linux/exportfs.h> |
1da177e4c
|
28 29 30 31 |
#include "compr.h" #include "nodelist.h" static void jffs2_put_super(struct super_block *); |
e18b890bb
|
32 |
static struct kmem_cache *jffs2_inode_cachep; |
1da177e4c
|
33 34 35 |
static struct inode *jffs2_alloc_inode(struct super_block *sb) { |
4e571aba7
|
36 37 38 39 |
struct jffs2_inode_info *f; f = kmem_cache_alloc(jffs2_inode_cachep, GFP_KERNEL); if (!f) |
1da177e4c
|
40 |
return NULL; |
4e571aba7
|
41 |
return &f->vfs_inode; |
1da177e4c
|
42 |
} |
fa0d7e3de
|
43 |
static void jffs2_i_callback(struct rcu_head *head) |
1da177e4c
|
44 |
{ |
fa0d7e3de
|
45 |
struct inode *inode = container_of(head, struct inode, i_rcu); |
1da177e4c
|
46 47 |
kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); } |
fa0d7e3de
|
48 49 50 51 |
static void jffs2_destroy_inode(struct inode *inode) { call_rcu(&inode->i_rcu, jffs2_i_callback); } |
51cc50685
|
52 |
static void jffs2_i_init_once(void *foo) |
1da177e4c
|
53 |
{ |
4e571aba7
|
54 |
struct jffs2_inode_info *f = foo; |
1da177e4c
|
55 |
|
4e571aba7
|
56 57 |
mutex_init(&f->sem); inode_init_once(&f->vfs_inode); |
1da177e4c
|
58 |
} |
92abc475d
|
59 60 61 62 63 |
static const char *jffs2_compr_name(unsigned int compr) { switch (compr) { case JFFS2_COMPR_MODE_NONE: return "none"; |
123005f3c
|
64 65 66 67 68 69 70 71 |
#ifdef CONFIG_JFFS2_LZO case JFFS2_COMPR_MODE_FORCELZO: return "lzo"; #endif #ifdef CONFIG_JFFS2_ZLIB case JFFS2_COMPR_MODE_FORCEZLIB: return "zlib"; #endif |
92abc475d
|
72 73 74 75 76 77 |
default: /* should never happen; programmer error */ WARN_ON(1); return ""; } } |
34c80b1d9
|
78 |
static int jffs2_show_options(struct seq_file *s, struct dentry *root) |
92abc475d
|
79 |
{ |
34c80b1d9
|
80 |
struct jffs2_sb_info *c = JFFS2_SB_INFO(root->d_sb); |
92abc475d
|
81 82 83 84 |
struct jffs2_mount_opts *opts = &c->mount_opts; if (opts->override_compr) seq_printf(s, ",compr=%s", jffs2_compr_name(opts->compr)); |
8da8ba2ea
|
85 86 |
if (opts->rp_size) seq_printf(s, ",rp_size=%u", opts->rp_size / 1024); |
92abc475d
|
87 88 89 |
return 0; } |
1da177e4c
|
90 91 92 |
static int jffs2_sync_fs(struct super_block *sb, int wait) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); |
a445f784a
|
93 94 95 |
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER cancel_delayed_work_sync(&c->wbuf_dwork); #endif |
ced220703
|
96 |
mutex_lock(&c->alloc_sem); |
1da177e4c
|
97 |
jffs2_flush_wbuf_pad(c); |
ced220703
|
98 |
mutex_unlock(&c->alloc_sem); |
1da177e4c
|
99 100 |
return 0; } |
5f556aab9
|
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
static struct inode *jffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, uint32_t generation) { /* We don't care about i_generation. We'll destroy the flash before we start re-using inode numbers anyway. And even if that wasn't true, we'd have other problems...*/ return jffs2_iget(sb, ino); } static struct dentry *jffs2_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { return generic_fh_to_dentry(sb, fid, fh_len, fh_type, jffs2_nfs_get_inode); } static struct dentry *jffs2_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { return generic_fh_to_parent(sb, fid, fh_len, fh_type, jffs2_nfs_get_inode); } static struct dentry *jffs2_get_parent(struct dentry *child) { struct jffs2_inode_info *f; uint32_t pino; |
e36cb0b89
|
128 |
BUG_ON(!d_is_dir(child)); |
5f556aab9
|
129 |
|
2b0143b5c
|
130 |
f = JFFS2_INODE_INFO(d_inode(child)); |
5f556aab9
|
131 132 133 134 135 136 |
pino = f->inocache->pino_nlink; JFFS2_DEBUG("Parent of directory ino #%u is #%u ", f->inocache->ino, pino); |
2b0143b5c
|
137 |
return d_obtain_alias(jffs2_iget(d_inode(child)->i_sb, pino)); |
5f556aab9
|
138 |
} |
ac4cfdd6d
|
139 |
static const struct export_operations jffs2_export_ops = { |
5f556aab9
|
140 141 142 143 |
.get_parent = jffs2_get_parent, .fh_to_dentry = jffs2_fh_to_dentry, .fh_to_parent = jffs2_fh_to_parent, }; |
92abc475d
|
144 145 146 147 |
/* * JFFS2 mount options. * * Opt_override_compr: override default compressor |
8da8ba2ea
|
148 |
* Opt_rp_size: size of reserved pool in KiB |
92abc475d
|
149 150 151 152 |
* Opt_err: just end of array marker */ enum { Opt_override_compr, |
8da8ba2ea
|
153 |
Opt_rp_size, |
92abc475d
|
154 155 156 157 158 |
Opt_err, }; static const match_table_t tokens = { {Opt_override_compr, "compr=%s"}, |
8da8ba2ea
|
159 |
{Opt_rp_size, "rp_size=%u"}, |
92abc475d
|
160 161 162 163 164 165 166 |
{Opt_err, NULL}, }; static int jffs2_parse_options(struct jffs2_sb_info *c, char *data) { substring_t args[MAX_OPT_ARGS]; char *p, *name; |
8da8ba2ea
|
167 |
unsigned int opt; |
92abc475d
|
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
if (!data) return 0; while ((p = strsep(&data, ","))) { int token; if (!*p) continue; token = match_token(p, tokens, args); switch (token) { case Opt_override_compr: name = match_strdup(&args[0]); if (!name) return -ENOMEM; |
123005f3c
|
185 |
if (!strcmp(name, "none")) |
92abc475d
|
186 |
c->mount_opts.compr = JFFS2_COMPR_MODE_NONE; |
123005f3c
|
187 188 189 190 191 192 193 194 195 196 |
#ifdef CONFIG_JFFS2_LZO else if (!strcmp(name, "lzo")) c->mount_opts.compr = JFFS2_COMPR_MODE_FORCELZO; #endif #ifdef CONFIG_JFFS2_ZLIB else if (!strcmp(name, "zlib")) c->mount_opts.compr = JFFS2_COMPR_MODE_FORCEZLIB; #endif else { |
5a528957e
|
197 198 |
pr_err("Error: unknown compressor \"%s\" ", |
da320f055
|
199 |
name); |
123005f3c
|
200 201 |
kfree(name); return -EINVAL; |
92abc475d
|
202 203 |
} kfree(name); |
123005f3c
|
204 |
c->mount_opts.override_compr = true; |
92abc475d
|
205 |
break; |
8da8ba2ea
|
206 207 208 209 210 211 212 213 214 215 216 217 |
case Opt_rp_size: if (match_int(&args[0], &opt)) return -EINVAL; opt *= 1024; if (opt > c->mtd->size) { pr_warn("Too large reserve pool specified, max " "is %llu KB ", c->mtd->size / 1024); return -EINVAL; } c->mount_opts.rp_size = opt; break; |
92abc475d
|
218 |
default: |
5a528957e
|
219 220 |
pr_err("Error: unrecognized mount option '%s' or missing value ", |
da320f055
|
221 |
p); |
92abc475d
|
222 223 224 225 226 227 228 229 230 231 232 |
return -EINVAL; } } return 0; } static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); int err; |
02b9984d6
|
233 |
sync_filesystem(sb); |
92abc475d
|
234 235 236 237 238 239 |
err = jffs2_parse_options(c, data); if (err) return -EINVAL; return jffs2_do_remount_fs(sb, flags, data); } |
ee9b6d61a
|
240 |
static const struct super_operations jffs2_super_operations = |
1da177e4c
|
241 242 243 |
{ .alloc_inode = jffs2_alloc_inode, .destroy_inode =jffs2_destroy_inode, |
1da177e4c
|
244 |
.put_super = jffs2_put_super, |
1da177e4c
|
245 246 |
.statfs = jffs2_statfs, .remount_fs = jffs2_remount_fs, |
b57922d97
|
247 |
.evict_inode = jffs2_evict_inode, |
1da177e4c
|
248 |
.dirty_inode = jffs2_dirty_inode, |
92abc475d
|
249 |
.show_options = jffs2_show_options, |
1da177e4c
|
250 251 |
.sync_fs = jffs2_sync_fs, }; |
acaebfd8a
|
252 253 254 255 |
/* * fill in the superblock */ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) |
1da177e4c
|
256 |
{ |
1da177e4c
|
257 |
struct jffs2_sb_info *c; |
db7192221
|
258 |
int ret; |
9c261b33a
|
259 |
jffs2_dbg(1, "jffs2_get_sb_mtd():" |
acaebfd8a
|
260 261 |
" New superblock for device %d (\"%s\") ", |
9c261b33a
|
262 |
sb->s_mtd->index, sb->s_mtd->name); |
1da177e4c
|
263 |
|
f8314dc60
|
264 |
c = kzalloc(sizeof(*c), GFP_KERNEL); |
1a028dd2d
|
265 |
if (!c) |
454e2398b
|
266 |
return -ENOMEM; |
1da177e4c
|
267 |
|
acaebfd8a
|
268 269 270 |
c->mtd = sb->s_mtd; c->os_priv = sb; sb->s_fs_info = c; |
1da177e4c
|
271 |
|
92abc475d
|
272 273 274 275 276 |
ret = jffs2_parse_options(c, data); if (ret) { kfree(c); return -EINVAL; } |
acaebfd8a
|
277 278 |
/* Initialize JFFS2 superblock locks, the further initialization will * be done later */ |
ced220703
|
279 280 |
mutex_init(&c->alloc_sem); mutex_init(&c->erase_free_sem); |
b62205986
|
281 282 283 284 |
init_waitqueue_head(&c->erase_wait); init_waitqueue_head(&c->inocache_wq); spin_lock_init(&c->erase_completion_lock); spin_lock_init(&c->inocache_lock); |
1da177e4c
|
285 |
sb->s_op = &jffs2_super_operations; |
5f556aab9
|
286 |
sb->s_export_op = &jffs2_export_ops; |
acaebfd8a
|
287 |
sb->s_flags = sb->s_flags | MS_NOATIME; |
aa98d7cf5
|
288 289 290 291 |
sb->s_xattr = jffs2_xattr_handlers; #ifdef CONFIG_JFFS2_FS_POSIX_ACL sb->s_flags |= MS_POSIXACL; #endif |
db7192221
|
292 |
ret = jffs2_do_fill_super(sb, data, silent); |
db7192221
|
293 |
return ret; |
1da177e4c
|
294 |
} |
848b83a59
|
295 |
static struct dentry *jffs2_mount(struct file_system_type *fs_type, |
454e2398b
|
296 |
int flags, const char *dev_name, |
848b83a59
|
297 |
void *data) |
1da177e4c
|
298 |
{ |
848b83a59
|
299 |
return mount_mtd(fs_type, flags, dev_name, data, jffs2_fill_super); |
1da177e4c
|
300 301 302 303 304 |
} static void jffs2_put_super (struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); |
9c261b33a
|
305 306 |
jffs2_dbg(2, "%s() ", __func__); |
1da177e4c
|
307 |
|
ced220703
|
308 |
mutex_lock(&c->alloc_sem); |
1da177e4c
|
309 |
jffs2_flush_wbuf_pad(c); |
ced220703
|
310 |
mutex_unlock(&c->alloc_sem); |
e631ddba5
|
311 312 |
jffs2_sum_exit(c); |
1da177e4c
|
313 314 |
jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); |
1d5cfdb07
|
315 |
kvfree(c->blocks); |
1da177e4c
|
316 317 |
jffs2_flash_cleanup(c); kfree(c->inocache_list); |
aa98d7cf5
|
318 |
jffs2_clear_xattr_subsystem(c); |
327cf2922
|
319 |
mtd_sync(c->mtd); |
9c261b33a
|
320 321 |
jffs2_dbg(1, "%s(): returning ", __func__); |
1da177e4c
|
322 323 324 325 326 |
} static void jffs2_kill_sb(struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); |
a69dde91e
|
327 328 |
if (!(sb->s_flags & MS_RDONLY)) jffs2_stop_garbage_collect_thread(c); |
acaebfd8a
|
329 |
kill_mtd_super(sb); |
1da177e4c
|
330 331 332 333 334 335 |
kfree(c); } static struct file_system_type jffs2_fs_type = { .owner = THIS_MODULE, .name = "jffs2", |
848b83a59
|
336 |
.mount = jffs2_mount, |
1da177e4c
|
337 338 |
.kill_sb = jffs2_kill_sb, }; |
7f78e0351
|
339 |
MODULE_ALIAS_FS("jffs2"); |
1da177e4c
|
340 341 342 343 |
static int __init init_jffs2_fs(void) { int ret; |
3e68fbb59
|
344 345 346 347 348 349 350 |
/* Paranoia checks for on-medium structures. If we ask GCC to pack them with __attribute__((packed)) then it _also_ assumes that they're not aligned -- so it emits crappy code on some architectures. Ideally we want an attribute which means just 'no padding', without the alignment thing. But GCC doesn't have that -- we have to just hope the structs are the right sizes, instead. */ |
2ecd05ae6
|
351 352 353 354 |
BUILD_BUG_ON(sizeof(struct jffs2_unknown_node) != 12); BUILD_BUG_ON(sizeof(struct jffs2_raw_dirent) != 40); BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68); BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32); |
3e68fbb59
|
355 |
|
5a528957e
|
356 |
pr_info("version 2.2." |
2f82ce1eb
|
357 |
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER |
1da177e4c
|
358 359 |
" (NAND)" #endif |
e631ddba5
|
360 361 362 |
#ifdef CONFIG_JFFS2_SUMMARY " (SUMMARY) " #endif |
c00c310ea
|
363 364 |
" © 2001-2006 Red Hat, Inc. "); |
1da177e4c
|
365 366 367 |
jffs2_inode_cachep = kmem_cache_create("jffs2_i", sizeof(struct jffs2_inode_info), |
fffb60f93
|
368 |
0, (SLAB_RECLAIM_ACCOUNT| |
5d097056c
|
369 |
SLAB_MEM_SPREAD|SLAB_ACCOUNT), |
20c2df83d
|
370 |
jffs2_i_init_once); |
1da177e4c
|
371 |
if (!jffs2_inode_cachep) { |
5a528957e
|
372 373 |
pr_err("error: Failed to initialise inode cache "); |
1da177e4c
|
374 375 376 377 |
return -ENOMEM; } ret = jffs2_compressors_init(); if (ret) { |
5a528957e
|
378 379 |
pr_err("error: Failed to initialise compressors "); |
1da177e4c
|
380 381 382 383 |
goto out; } ret = jffs2_create_slab_caches(); if (ret) { |
5a528957e
|
384 385 |
pr_err("error: Failed to initialise slab caches "); |
1da177e4c
|
386 387 388 389 |
goto out_compressors; } ret = register_filesystem(&jffs2_fs_type); if (ret) { |
5a528957e
|
390 391 |
pr_err("error: Failed to register filesystem "); |
1da177e4c
|
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
goto out_slab; } return 0; out_slab: jffs2_destroy_slab_caches(); out_compressors: jffs2_compressors_exit(); out: kmem_cache_destroy(jffs2_inode_cachep); return ret; } static void __exit exit_jffs2_fs(void) { unregister_filesystem(&jffs2_fs_type); jffs2_destroy_slab_caches(); jffs2_compressors_exit(); |
8c0a85377
|
410 411 412 413 414 415 |
/* * Make sure all delayed rcu free inodes are flushed before we * destroy cache. */ rcu_barrier(); |
1da177e4c
|
416 417 418 419 420 421 422 423 |
kmem_cache_destroy(jffs2_inode_cachep); } module_init(init_jffs2_fs); module_exit(exit_jffs2_fs); MODULE_DESCRIPTION("The Journalling Flash File System, v2"); MODULE_AUTHOR("Red Hat, Inc."); |
182ec4eee
|
424 |
MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for |
1da177e4c
|
425 |
// the sake of this tag. It's Free Software. |