xref: /openbmc/linux/fs/jffs2/super.c (revision db719222)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * JFFS2 -- Journalling Flash File System, Version 2.
31da177e4SLinus Torvalds  *
4c00c310eSDavid Woodhouse  * Copyright © 2001-2007 Red Hat, Inc.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Created by David Woodhouse <dwmw2@infradead.org>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * For licensing information, see the file 'LICENCE' in this directory.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/kernel.h>
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
15405f5571SAlexey Dobriyan #include <linux/smp_lock.h>
161da177e4SLinus Torvalds #include <linux/init.h>
171da177e4SLinus Torvalds #include <linux/list.h>
181da177e4SLinus Torvalds #include <linux/fs.h>
199c74034fSArtem Bityutskiy #include <linux/err.h>
201da177e4SLinus Torvalds #include <linux/mount.h>
211da177e4SLinus Torvalds #include <linux/jffs2.h>
221da177e4SLinus Torvalds #include <linux/pagemap.h>
23acaebfd8SDavid Howells #include <linux/mtd/super.h>
241da177e4SLinus Torvalds #include <linux/ctype.h>
251da177e4SLinus Torvalds #include <linux/namei.h>
265f556aabSDavid Woodhouse #include <linux/exportfs.h>
271da177e4SLinus Torvalds #include "compr.h"
281da177e4SLinus Torvalds #include "nodelist.h"
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds static void jffs2_put_super(struct super_block *);
311da177e4SLinus Torvalds 
32e18b890bSChristoph Lameter static struct kmem_cache *jffs2_inode_cachep;
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds static struct inode *jffs2_alloc_inode(struct super_block *sb)
351da177e4SLinus Torvalds {
364e571abaSDavid Woodhouse 	struct jffs2_inode_info *f;
374e571abaSDavid Woodhouse 
384e571abaSDavid Woodhouse 	f = kmem_cache_alloc(jffs2_inode_cachep, GFP_KERNEL);
394e571abaSDavid Woodhouse 	if (!f)
401da177e4SLinus Torvalds 		return NULL;
414e571abaSDavid Woodhouse 	return &f->vfs_inode;
421da177e4SLinus Torvalds }
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds static void jffs2_destroy_inode(struct inode *inode)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds 	kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds 
4951cc5068SAlexey Dobriyan static void jffs2_i_init_once(void *foo)
501da177e4SLinus Torvalds {
514e571abaSDavid Woodhouse 	struct jffs2_inode_info *f = foo;
521da177e4SLinus Torvalds 
534e571abaSDavid Woodhouse 	mutex_init(&f->sem);
544e571abaSDavid Woodhouse 	inode_init_once(&f->vfs_inode);
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
5701ba6875SChristoph Hellwig static void jffs2_write_super(struct super_block *sb)
5801ba6875SChristoph Hellwig {
5901ba6875SChristoph Hellwig 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
60ebc1ac16SChristoph Hellwig 
61ebc1ac16SChristoph Hellwig 	lock_super(sb);
6201ba6875SChristoph Hellwig 	sb->s_dirt = 0;
6301ba6875SChristoph Hellwig 
64ebc1ac16SChristoph Hellwig 	if (!(sb->s_flags & MS_RDONLY)) {
6501ba6875SChristoph Hellwig 		D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
6601ba6875SChristoph Hellwig 		jffs2_flush_wbuf_gc(c, 0);
6701ba6875SChristoph Hellwig 	}
6801ba6875SChristoph Hellwig 
69ebc1ac16SChristoph Hellwig 	unlock_super(sb);
70ebc1ac16SChristoph Hellwig }
71ebc1ac16SChristoph Hellwig 
721da177e4SLinus Torvalds static int jffs2_sync_fs(struct super_block *sb, int wait)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
751da177e4SLinus Torvalds 
76d579ed00SChristoph Hellwig 	jffs2_write_super(sb);
77d579ed00SChristoph Hellwig 
78ced22070SDavid Woodhouse 	mutex_lock(&c->alloc_sem);
791da177e4SLinus Torvalds 	jffs2_flush_wbuf_pad(c);
80ced22070SDavid Woodhouse 	mutex_unlock(&c->alloc_sem);
811da177e4SLinus Torvalds 	return 0;
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds 
845f556aabSDavid Woodhouse static struct inode *jffs2_nfs_get_inode(struct super_block *sb, uint64_t ino,
855f556aabSDavid Woodhouse 					 uint32_t generation)
865f556aabSDavid Woodhouse {
875f556aabSDavid Woodhouse 	/* We don't care about i_generation. We'll destroy the flash
885f556aabSDavid Woodhouse 	   before we start re-using inode numbers anyway. And even
895f556aabSDavid Woodhouse 	   if that wasn't true, we'd have other problems...*/
905f556aabSDavid Woodhouse 	return jffs2_iget(sb, ino);
915f556aabSDavid Woodhouse }
925f556aabSDavid Woodhouse 
935f556aabSDavid Woodhouse static struct dentry *jffs2_fh_to_dentry(struct super_block *sb, struct fid *fid,
945f556aabSDavid Woodhouse 					 int fh_len, int fh_type)
955f556aabSDavid Woodhouse {
965f556aabSDavid Woodhouse         return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
975f556aabSDavid Woodhouse                                     jffs2_nfs_get_inode);
985f556aabSDavid Woodhouse }
995f556aabSDavid Woodhouse 
1005f556aabSDavid Woodhouse static struct dentry *jffs2_fh_to_parent(struct super_block *sb, struct fid *fid,
1015f556aabSDavid Woodhouse 					 int fh_len, int fh_type)
1025f556aabSDavid Woodhouse {
1035f556aabSDavid Woodhouse         return generic_fh_to_parent(sb, fid, fh_len, fh_type,
1045f556aabSDavid Woodhouse                                     jffs2_nfs_get_inode);
1055f556aabSDavid Woodhouse }
1065f556aabSDavid Woodhouse 
1075f556aabSDavid Woodhouse static struct dentry *jffs2_get_parent(struct dentry *child)
1085f556aabSDavid Woodhouse {
1095f556aabSDavid Woodhouse 	struct jffs2_inode_info *f;
1105f556aabSDavid Woodhouse 	uint32_t pino;
1115f556aabSDavid Woodhouse 
1125f556aabSDavid Woodhouse 	BUG_ON(!S_ISDIR(child->d_inode->i_mode));
1135f556aabSDavid Woodhouse 
1145f556aabSDavid Woodhouse 	f = JFFS2_INODE_INFO(child->d_inode);
1155f556aabSDavid Woodhouse 
1165f556aabSDavid Woodhouse 	pino = f->inocache->pino_nlink;
1175f556aabSDavid Woodhouse 
1185f556aabSDavid Woodhouse 	JFFS2_DEBUG("Parent of directory ino #%u is #%u\n",
1195f556aabSDavid Woodhouse 		    f->inocache->ino, pino);
1205f556aabSDavid Woodhouse 
1215f556aabSDavid Woodhouse 	return d_obtain_alias(jffs2_iget(child->d_inode->i_sb, pino));
1225f556aabSDavid Woodhouse }
1235f556aabSDavid Woodhouse 
124ac4cfdd6SAlexey Dobriyan static const struct export_operations jffs2_export_ops = {
1255f556aabSDavid Woodhouse 	.get_parent = jffs2_get_parent,
1265f556aabSDavid Woodhouse 	.fh_to_dentry = jffs2_fh_to_dentry,
1275f556aabSDavid Woodhouse 	.fh_to_parent = jffs2_fh_to_parent,
1285f556aabSDavid Woodhouse };
1295f556aabSDavid Woodhouse 
130ee9b6d61SJosef 'Jeff' Sipek static const struct super_operations jffs2_super_operations =
1311da177e4SLinus Torvalds {
1321da177e4SLinus Torvalds 	.alloc_inode =	jffs2_alloc_inode,
1331da177e4SLinus Torvalds 	.destroy_inode =jffs2_destroy_inode,
1341da177e4SLinus Torvalds 	.put_super =	jffs2_put_super,
1351da177e4SLinus Torvalds 	.write_super =	jffs2_write_super,
1361da177e4SLinus Torvalds 	.statfs =	jffs2_statfs,
1371da177e4SLinus Torvalds 	.remount_fs =	jffs2_remount_fs,
138b57922d9SAl Viro 	.evict_inode =	jffs2_evict_inode,
1391da177e4SLinus Torvalds 	.dirty_inode =	jffs2_dirty_inode,
1401da177e4SLinus Torvalds 	.sync_fs =	jffs2_sync_fs,
1411da177e4SLinus Torvalds };
1421da177e4SLinus Torvalds 
143acaebfd8SDavid Howells /*
144acaebfd8SDavid Howells  * fill in the superblock
145acaebfd8SDavid Howells  */
146acaebfd8SDavid Howells static int jffs2_fill_super(struct super_block *sb, void *data, int silent)
1471da177e4SLinus Torvalds {
1481da177e4SLinus Torvalds 	struct jffs2_sb_info *c;
149db719222SJan Blunck 	int ret;
150db719222SJan Blunck 
151db719222SJan Blunck 	lock_kernel();
152acaebfd8SDavid Howells 
153acaebfd8SDavid Howells 	D1(printk(KERN_DEBUG "jffs2_get_sb_mtd():"
154acaebfd8SDavid Howells 		  " New superblock for device %d (\"%s\")\n",
155acaebfd8SDavid Howells 		  sb->s_mtd->index, sb->s_mtd->name));
1561da177e4SLinus Torvalds 
157f8314dc6SPanagiotis Issaris 	c = kzalloc(sizeof(*c), GFP_KERNEL);
158db719222SJan Blunck 	if (!c) {
159db719222SJan Blunck 		unlock_kernel();
160454e2398SDavid Howells 		return -ENOMEM;
161db719222SJan Blunck 	}
1621da177e4SLinus Torvalds 
163acaebfd8SDavid Howells 	c->mtd = sb->s_mtd;
164acaebfd8SDavid Howells 	c->os_priv = sb;
165acaebfd8SDavid Howells 	sb->s_fs_info = c;
1661da177e4SLinus Torvalds 
167acaebfd8SDavid Howells 	/* Initialize JFFS2 superblock locks, the further initialization will
168acaebfd8SDavid Howells 	 * be done later */
169ced22070SDavid Woodhouse 	mutex_init(&c->alloc_sem);
170ced22070SDavid Woodhouse 	mutex_init(&c->erase_free_sem);
171b6220598SArtem B. Bityuckiy 	init_waitqueue_head(&c->erase_wait);
172b6220598SArtem B. Bityuckiy 	init_waitqueue_head(&c->inocache_wq);
173b6220598SArtem B. Bityuckiy 	spin_lock_init(&c->erase_completion_lock);
174b6220598SArtem B. Bityuckiy 	spin_lock_init(&c->inocache_lock);
175b6220598SArtem B. Bityuckiy 
1761da177e4SLinus Torvalds 	sb->s_op = &jffs2_super_operations;
1775f556aabSDavid Woodhouse 	sb->s_export_op = &jffs2_export_ops;
178acaebfd8SDavid Howells 	sb->s_flags = sb->s_flags | MS_NOATIME;
179aa98d7cfSKaiGai Kohei 	sb->s_xattr = jffs2_xattr_handlers;
180aa98d7cfSKaiGai Kohei #ifdef CONFIG_JFFS2_FS_POSIX_ACL
181aa98d7cfSKaiGai Kohei 	sb->s_flags |= MS_POSIXACL;
182aa98d7cfSKaiGai Kohei #endif
183db719222SJan Blunck 	ret = jffs2_do_fill_super(sb, data, silent);
184db719222SJan Blunck 	unlock_kernel();
185db719222SJan Blunck 	return ret;
1861da177e4SLinus Torvalds }
1871da177e4SLinus Torvalds 
188454e2398SDavid Howells static int jffs2_get_sb(struct file_system_type *fs_type,
1891da177e4SLinus Torvalds 			int flags, const char *dev_name,
190454e2398SDavid Howells 			void *data, struct vfsmount *mnt)
1911da177e4SLinus Torvalds {
192acaebfd8SDavid Howells 	return get_sb_mtd(fs_type, flags, dev_name, data, jffs2_fill_super,
193acaebfd8SDavid Howells 			  mnt);
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds static void jffs2_put_super (struct super_block *sb)
1971da177e4SLinus Torvalds {
1981da177e4SLinus Torvalds 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
2011da177e4SLinus Torvalds 
2026cfd0148SChristoph Hellwig 	lock_kernel();
2036cfd0148SChristoph Hellwig 
2048c85e125SChristoph Hellwig 	if (sb->s_dirt)
2058c85e125SChristoph Hellwig 		jffs2_write_super(sb);
2068c85e125SChristoph Hellwig 
207ced22070SDavid Woodhouse 	mutex_lock(&c->alloc_sem);
2081da177e4SLinus Torvalds 	jffs2_flush_wbuf_pad(c);
209ced22070SDavid Woodhouse 	mutex_unlock(&c->alloc_sem);
210e631ddbaSFerenc Havasi 
211e631ddbaSFerenc Havasi 	jffs2_sum_exit(c);
212e631ddbaSFerenc Havasi 
2131da177e4SLinus Torvalds 	jffs2_free_ino_caches(c);
2141da177e4SLinus Torvalds 	jffs2_free_raw_node_refs(c);
2154ce1f562SFerenc Havasi 	if (jffs2_blocks_use_vmalloc(c))
2161da177e4SLinus Torvalds 		vfree(c->blocks);
2171da177e4SLinus Torvalds 	else
2181da177e4SLinus Torvalds 		kfree(c->blocks);
2191da177e4SLinus Torvalds 	jffs2_flash_cleanup(c);
2201da177e4SLinus Torvalds 	kfree(c->inocache_list);
221aa98d7cfSKaiGai Kohei 	jffs2_clear_xattr_subsystem(c);
2221da177e4SLinus Torvalds 	if (c->mtd->sync)
2231da177e4SLinus Torvalds 		c->mtd->sync(c->mtd);
2241da177e4SLinus Torvalds 
2256cfd0148SChristoph Hellwig 	unlock_kernel();
2266cfd0148SChristoph Hellwig 
2271da177e4SLinus Torvalds 	D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds static void jffs2_kill_sb(struct super_block *sb)
2311da177e4SLinus Torvalds {
2321da177e4SLinus Torvalds 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
233a69dde91SArtem B. Bityuckiy 	if (!(sb->s_flags & MS_RDONLY))
234a69dde91SArtem B. Bityuckiy 		jffs2_stop_garbage_collect_thread(c);
235acaebfd8SDavid Howells 	kill_mtd_super(sb);
2361da177e4SLinus Torvalds 	kfree(c);
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds static struct file_system_type jffs2_fs_type = {
2401da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
2411da177e4SLinus Torvalds 	.name =		"jffs2",
2421da177e4SLinus Torvalds 	.get_sb =	jffs2_get_sb,
2431da177e4SLinus Torvalds 	.kill_sb =	jffs2_kill_sb,
2441da177e4SLinus Torvalds };
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds static int __init init_jffs2_fs(void)
2471da177e4SLinus Torvalds {
2481da177e4SLinus Torvalds 	int ret;
2491da177e4SLinus Torvalds 
2503e68fbb5SDavid Woodhouse 	/* Paranoia checks for on-medium structures. If we ask GCC
2513e68fbb5SDavid Woodhouse 	   to pack them with __attribute__((packed)) then it _also_
2523e68fbb5SDavid Woodhouse 	   assumes that they're not aligned -- so it emits crappy
2533e68fbb5SDavid Woodhouse 	   code on some architectures. Ideally we want an attribute
2543e68fbb5SDavid Woodhouse 	   which means just 'no padding', without the alignment
2553e68fbb5SDavid Woodhouse 	   thing. But GCC doesn't have that -- we have to just
2563e68fbb5SDavid Woodhouse 	   hope the structs are the right sizes, instead. */
2572ecd05aeSAlexey Dobriyan 	BUILD_BUG_ON(sizeof(struct jffs2_unknown_node) != 12);
2582ecd05aeSAlexey Dobriyan 	BUILD_BUG_ON(sizeof(struct jffs2_raw_dirent) != 40);
2592ecd05aeSAlexey Dobriyan 	BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
2602ecd05aeSAlexey Dobriyan 	BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
2613e68fbb5SDavid Woodhouse 
2621da177e4SLinus Torvalds 	printk(KERN_INFO "JFFS2 version 2.2."
2632f82ce1eSAndrew Victor #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
2641da177e4SLinus Torvalds 	       " (NAND)"
2651da177e4SLinus Torvalds #endif
266e631ddbaSFerenc Havasi #ifdef CONFIG_JFFS2_SUMMARY
267e631ddbaSFerenc Havasi 	       " (SUMMARY) "
268e631ddbaSFerenc Havasi #endif
269c00c310eSDavid Woodhouse 	       " © 2001-2006 Red Hat, Inc.\n");
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	jffs2_inode_cachep = kmem_cache_create("jffs2_i",
2721da177e4SLinus Torvalds 					     sizeof(struct jffs2_inode_info),
273fffb60f9SPaul Jackson 					     0, (SLAB_RECLAIM_ACCOUNT|
274fffb60f9SPaul Jackson 						SLAB_MEM_SPREAD),
27520c2df83SPaul Mundt 					     jffs2_i_init_once);
2761da177e4SLinus Torvalds 	if (!jffs2_inode_cachep) {
2771da177e4SLinus Torvalds 		printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
2781da177e4SLinus Torvalds 		return -ENOMEM;
2791da177e4SLinus Torvalds 	}
2801da177e4SLinus Torvalds 	ret = jffs2_compressors_init();
2811da177e4SLinus Torvalds 	if (ret) {
2821da177e4SLinus Torvalds 		printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
2831da177e4SLinus Torvalds 		goto out;
2841da177e4SLinus Torvalds 	}
2851da177e4SLinus Torvalds 	ret = jffs2_create_slab_caches();
2861da177e4SLinus Torvalds 	if (ret) {
2871da177e4SLinus Torvalds 		printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
2881da177e4SLinus Torvalds 		goto out_compressors;
2891da177e4SLinus Torvalds 	}
2901da177e4SLinus Torvalds 	ret = register_filesystem(&jffs2_fs_type);
2911da177e4SLinus Torvalds 	if (ret) {
2921da177e4SLinus Torvalds 		printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
2931da177e4SLinus Torvalds 		goto out_slab;
2941da177e4SLinus Torvalds 	}
2951da177e4SLinus Torvalds 	return 0;
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds  out_slab:
2981da177e4SLinus Torvalds 	jffs2_destroy_slab_caches();
2991da177e4SLinus Torvalds  out_compressors:
3001da177e4SLinus Torvalds 	jffs2_compressors_exit();
3011da177e4SLinus Torvalds  out:
3021da177e4SLinus Torvalds 	kmem_cache_destroy(jffs2_inode_cachep);
3031da177e4SLinus Torvalds 	return ret;
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds static void __exit exit_jffs2_fs(void)
3071da177e4SLinus Torvalds {
3081da177e4SLinus Torvalds 	unregister_filesystem(&jffs2_fs_type);
3091da177e4SLinus Torvalds 	jffs2_destroy_slab_caches();
3101da177e4SLinus Torvalds 	jffs2_compressors_exit();
3111da177e4SLinus Torvalds 	kmem_cache_destroy(jffs2_inode_cachep);
3121da177e4SLinus Torvalds }
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds module_init(init_jffs2_fs);
3151da177e4SLinus Torvalds module_exit(exit_jffs2_fs);
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds MODULE_DESCRIPTION("The Journalling Flash File System, v2");
3181da177e4SLinus Torvalds MODULE_AUTHOR("Red Hat, Inc.");
3191da177e4SLinus Torvalds MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for
3201da177e4SLinus Torvalds 		       // the sake of this tag. It's Free Software.
321