2007-05-11 09:51:50 +04:00
/* MTD-based superblock management
*
* Copyright © 2001 - 2007 Red Hat , Inc . All Rights Reserved .
2010-08-08 23:58:20 +04:00
* Copyright © 2001 - 2010 David Woodhouse < dwmw2 @ infradead . org >
*
2007-05-11 09:51:50 +04:00
* Written by : David Howells < dhowells @ redhat . com >
* David Woodhouse < dwmw2 @ infradead . org >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/mtd/super.h>
# include <linux/namei.h>
2011-07-10 20:43:28 +04:00
# include <linux/export.h>
2007-05-11 09:51:50 +04:00
# include <linux/ctype.h>
2010-04-22 16:11:43 +04:00
# include <linux/slab.h>
2013-10-14 01:05:23 +04:00
# include <linux/major.h>
2007-05-11 09:51:50 +04:00
/*
* compare superblocks to see if they ' re equivalent
* - they are if the underlying MTD device is the same
*/
static int get_sb_mtd_compare ( struct super_block * sb , void * _mtd )
{
struct mtd_info * mtd = _mtd ;
if ( sb - > s_mtd = = mtd ) {
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: Match on device %d ( \" %s \" ) \n " ,
2007-05-11 09:51:50 +04:00
mtd - > index , mtd - > name ) ;
return 1 ;
}
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: No match, device %d ( \" %s \" ), device %d ( \" %s \" ) \n " ,
2007-05-11 09:51:50 +04:00
sb - > s_mtd - > index , sb - > s_mtd - > name , mtd - > index , mtd - > name ) ;
return 0 ;
}
/*
* mark the superblock by the MTD device it is using
* - set the device number to be the correct MTD block device for pesuperstence
* of NFS exports
*/
static int get_sb_mtd_set ( struct super_block * sb , void * _mtd )
{
struct mtd_info * mtd = _mtd ;
sb - > s_mtd = mtd ;
sb - > s_dev = MKDEV ( MTD_BLOCK_MAJOR , mtd - > index ) ;
2010-04-22 16:11:43 +04:00
sb - > s_bdi = mtd - > backing_dev_info ;
2007-05-11 09:51:50 +04:00
return 0 ;
}
/*
* get a superblock on an MTD - backed filesystem
*/
2010-07-25 00:56:46 +04:00
static struct dentry * mount_mtd_aux ( struct file_system_type * fs_type , int flags ,
2007-05-11 09:51:50 +04:00
const char * dev_name , void * data ,
struct mtd_info * mtd ,
2010-07-25 00:56:46 +04:00
int ( * fill_super ) ( struct super_block * , void * , int ) )
2007-05-11 09:51:50 +04:00
{
struct super_block * sb ;
int ret ;
2012-06-25 15:55:37 +04:00
sb = sget ( fs_type , get_sb_mtd_compare , get_sb_mtd_set , flags , mtd ) ;
2007-05-11 09:51:50 +04:00
if ( IS_ERR ( sb ) )
goto out_error ;
if ( sb - > s_root )
goto already_mounted ;
/* fresh new superblock */
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: New superblock for device %d ( \" %s \" ) \n " ,
2007-05-11 09:51:50 +04:00
mtd - > index , mtd - > name ) ;
ret = fill_super ( sb , data , flags & MS_SILENT ? 1 : 0 ) ;
if ( ret < 0 ) {
2009-05-06 09:34:22 +04:00
deactivate_locked_super ( sb ) ;
2010-07-25 00:56:46 +04:00
return ERR_PTR ( ret ) ;
2007-05-11 09:51:50 +04:00
}
/* go */
sb - > s_flags | = MS_ACTIVE ;
2010-07-25 00:56:46 +04:00
return dget ( sb - > s_root ) ;
2007-05-11 09:51:50 +04:00
/* new mountpoint for an already mounted superblock */
already_mounted :
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: Device %d ( \" %s \" ) is already mounted \n " ,
2007-05-11 09:51:50 +04:00
mtd - > index , mtd - > name ) ;
2010-07-25 00:56:46 +04:00
put_mtd_device ( mtd ) ;
return dget ( sb - > s_root ) ;
2007-05-11 09:51:50 +04:00
out_error :
put_mtd_device ( mtd ) ;
2010-07-25 00:56:46 +04:00
return ERR_CAST ( sb ) ;
2007-05-11 09:51:50 +04:00
}
/*
* get a superblock on an MTD - backed filesystem by MTD device number
*/
2010-07-25 00:56:46 +04:00
static struct dentry * mount_mtd_nr ( struct file_system_type * fs_type , int flags ,
2007-05-11 09:51:50 +04:00
const char * dev_name , void * data , int mtdnr ,
2010-07-25 00:56:46 +04:00
int ( * fill_super ) ( struct super_block * , void * , int ) )
2007-05-11 09:51:50 +04:00
{
struct mtd_info * mtd ;
mtd = get_mtd_device ( NULL , mtdnr ) ;
2007-06-01 22:21:59 +04:00
if ( IS_ERR ( mtd ) ) {
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: Device #%u doesn't appear to exist \n " , mtdnr ) ;
2010-07-25 00:56:46 +04:00
return ERR_CAST ( mtd ) ;
2007-05-11 09:51:50 +04:00
}
2010-07-25 00:56:46 +04:00
return mount_mtd_aux ( fs_type , flags , dev_name , data , mtd , fill_super ) ;
2007-05-11 09:51:50 +04:00
}
/*
* set up an MTD - based superblock
*/
2010-07-25 00:56:46 +04:00
struct dentry * mount_mtd ( struct file_system_type * fs_type , int flags ,
2007-05-11 09:51:50 +04:00
const char * dev_name , void * data ,
2010-07-25 00:56:46 +04:00
int ( * fill_super ) ( struct super_block * , void * , int ) )
2007-05-11 09:51:50 +04:00
{
2008-08-02 03:01:21 +04:00
# ifdef CONFIG_BLOCK
2008-08-01 13:00:11 +04:00
struct block_device * bdev ;
2008-08-02 03:01:21 +04:00
int ret , major ;
# endif
int mtdnr ;
2007-05-11 09:51:50 +04:00
if ( ! dev_name )
2010-07-25 00:56:46 +04:00
return ERR_PTR ( - EINVAL ) ;
2007-05-11 09:51:50 +04:00
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: dev_name \" %s \" \n " , dev_name ) ;
2007-05-11 09:51:50 +04:00
/* the preferred way of mounting in future; especially when
* CONFIG_BLOCK = n - we specify the underlying MTD device by number or
* by name , so that we don ' t require block device support to be present
* in the kernel . */
if ( dev_name [ 0 ] = = ' m ' & & dev_name [ 1 ] = = ' t ' & & dev_name [ 2 ] = = ' d ' ) {
if ( dev_name [ 3 ] = = ' : ' ) {
struct mtd_info * mtd ;
/* mount by MTD device name */
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: mtd:%%s, name \" %s \" \n " ,
2007-05-11 09:51:50 +04:00
dev_name + 4 ) ;
2010-01-29 23:57:18 +03:00
mtd = get_mtd_device_nm ( dev_name + 4 ) ;
if ( ! IS_ERR ( mtd ) )
2010-07-25 00:56:46 +04:00
return mount_mtd_aux (
2010-01-29 23:57:18 +03:00
fs_type , flags ,
dev_name , data , mtd ,
2010-07-25 00:56:46 +04:00
fill_super ) ;
2007-05-11 09:51:50 +04:00
printk ( KERN_NOTICE " MTD: "
" MTD device with name \" %s \" not found. \n " ,
dev_name + 4 ) ;
} else if ( isdigit ( dev_name [ 3 ] ) ) {
/* mount by MTD device number name */
char * endptr ;
mtdnr = simple_strtoul ( dev_name + 3 , & endptr , 0 ) ;
if ( ! * endptr ) {
/* It was a valid number */
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: mtd%%d, mtdnr %d \n " ,
2007-05-11 09:51:50 +04:00
mtdnr ) ;
2010-07-25 00:56:46 +04:00
return mount_mtd_nr ( fs_type , flags ,
2007-05-11 09:51:50 +04:00
dev_name , data ,
2010-07-25 00:56:46 +04:00
mtdnr , fill_super ) ;
2007-05-11 09:51:50 +04:00
}
}
}
2008-08-02 03:01:21 +04:00
# ifdef CONFIG_BLOCK
2007-05-11 09:51:50 +04:00
/* try the old way - the hack where we allowed users to mount
* / dev / mtdblock $ ( n ) but didn ' t actually _use_ the blockdev
*/
2008-08-01 13:00:11 +04:00
bdev = lookup_bdev ( dev_name ) ;
if ( IS_ERR ( bdev ) ) {
ret = PTR_ERR ( bdev ) ;
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: lookup_bdev() returned %d \n " , ret ) ;
2010-07-25 00:56:46 +04:00
return ERR_PTR ( ret ) ;
2007-05-11 09:51:50 +04:00
}
2011-07-19 21:06:09 +04:00
pr_debug ( " MTDSB: lookup_bdev() returned 0 \n " ) ;
2007-05-11 09:51:50 +04:00
2008-08-01 13:00:11 +04:00
ret = - EINVAL ;
2007-05-11 09:51:50 +04:00
2008-08-02 03:01:21 +04:00
major = MAJOR ( bdev - > bd_dev ) ;
2008-08-01 13:00:11 +04:00
mtdnr = MINOR ( bdev - > bd_dev ) ;
bdput ( bdev ) ;
2007-05-11 09:51:50 +04:00
2008-08-02 03:01:21 +04:00
if ( major ! = MTD_BLOCK_MAJOR )
goto not_an_MTD_device ;
2010-07-25 00:56:46 +04:00
return mount_mtd_nr ( fs_type , flags , dev_name , data , mtdnr , fill_super ) ;
2007-05-11 09:51:50 +04:00
not_an_MTD_device :
2008-08-02 03:01:21 +04:00
# endif /* CONFIG_BLOCK */
2007-05-11 09:51:50 +04:00
if ( ! ( flags & MS_SILENT ) )
printk ( KERN_NOTICE
" MTD: Attempt to mount non-MTD device \" %s \" \n " ,
dev_name ) ;
2010-07-25 00:56:46 +04:00
return ERR_PTR ( - EINVAL ) ;
2007-05-11 09:51:50 +04:00
}
2010-07-25 00:56:46 +04:00
EXPORT_SYMBOL_GPL ( mount_mtd ) ;
2007-05-11 09:51:50 +04:00
/*
* destroy an MTD - based superblock
*/
void kill_mtd_super ( struct super_block * sb )
{
generic_shutdown_super ( sb ) ;
put_mtd_device ( sb - > s_mtd ) ;
sb - > s_mtd = NULL ;
}
EXPORT_SYMBOL_GPL ( kill_mtd_super ) ;