2007-06-12 17:07:21 +04:00
/*
* Copyright ( C ) 2007 Oracle . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
2007-08-29 17:11:44 +04:00
# include <linux/blkdev.h>
2007-03-21 18:12:56 +03:00
# include <linux/module.h>
2007-03-22 19:13:20 +03:00
# include <linux/buffer_head.h>
2007-03-21 18:12:56 +03:00
# include <linux/fs.h>
# include <linux/pagemap.h>
# include <linux/highmem.h>
# include <linux/time.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/smp_lock.h>
# include <linux/backing-dev.h>
2007-08-29 17:11:44 +04:00
# include <linux/mount.h>
2007-03-27 00:00:06 +04:00
# include <linux/mpage.h>
2007-03-29 19:56:46 +04:00
# include <linux/swap.h>
# include <linux/writeback.h>
2007-04-20 05:01:03 +04:00
# include <linux/statfs.h>
2007-06-08 23:33:54 +04:00
# include <linux/compat.h>
2007-08-29 17:11:44 +04:00
# include <linux/parser.h>
2007-12-18 04:14:04 +03:00
# include <linux/ctype.h>
2007-12-19 00:15:09 +03:00
# include <linux/namei.h>
2007-03-21 18:12:56 +03:00
# include "ctree.h"
2007-03-22 19:13:20 +03:00
# include "disk-io.h"
2007-03-23 17:01:08 +03:00
# include "transaction.h"
2007-04-02 18:50:19 +04:00
# include "btrfs_inode.h"
2007-04-10 17:27:04 +04:00
# include "ioctl.h"
2007-05-24 21:35:57 +04:00
# include "print-tree.h"
2007-11-16 19:45:54 +03:00
# include "xattr.h"
2007-03-21 18:12:56 +03:00
2007-10-16 00:14:19 +04:00
# define BTRFS_SUPER_MAGIC 0x9123683E
2007-03-29 23:15:27 +04:00
2007-06-12 14:35:45 +04:00
static struct super_operations btrfs_super_ops ;
2007-03-29 19:56:46 +04:00
2007-06-12 14:35:45 +04:00
static void btrfs_put_super ( struct super_block * sb )
2007-04-17 21:26:50 +04:00
{
2007-06-12 14:35:45 +04:00
struct btrfs_root * root = btrfs_sb ( sb ) ;
2007-08-29 23:47:34 +04:00
struct btrfs_fs_info * fs = root - > fs_info ;
2007-04-17 21:26:50 +04:00
int ret ;
2007-06-12 14:35:45 +04:00
ret = close_ctree ( root ) ;
if ( ret ) {
printk ( " close ctree returns %d \n " , ret ) ;
2007-03-29 19:56:46 +04:00
}
2007-08-29 23:47:34 +04:00
btrfs_sysfs_del_super ( fs ) ;
2007-06-12 14:35:45 +04:00
sb - > s_fs_info = NULL ;
2007-03-29 19:56:46 +04:00
}
2007-08-29 17:11:44 +04:00
enum {
2008-01-02 18:01:11 +03:00
Opt_subvol , Opt_nodatasum , Opt_nodatacow , Opt_max_extent ,
2008-01-30 00:03:38 +03:00
Opt_max_inline , Opt_alloc_start , Opt_nobarrier , Opt_ssd , Opt_err ,
2007-08-29 17:11:44 +04:00
} ;
static match_table_t tokens = {
{ Opt_subvol , " subvol=%s " } ,
2007-12-14 23:30:32 +03:00
{ Opt_nodatasum , " nodatasum " } ,
2007-12-18 04:14:01 +03:00
{ Opt_nodatacow , " nodatacow " } ,
2008-01-09 17:23:21 +03:00
{ Opt_nobarrier , " nobarrier " } ,
2007-12-18 04:14:04 +03:00
{ Opt_max_extent , " max_extent=%s " } ,
2008-01-30 00:03:38 +03:00
{ Opt_max_inline , " max_inline=%s " } ,
2008-01-02 18:01:11 +03:00
{ Opt_alloc_start , " alloc_start=%s " } ,
2008-01-18 18:54:22 +03:00
{ Opt_ssd , " ssd " } ,
2007-08-29 17:11:44 +04:00
{ Opt_err , NULL }
} ;
2007-12-22 00:27:24 +03:00
u64 btrfs_parse_size ( char * str )
2007-12-18 04:14:04 +03:00
{
2007-12-22 00:27:24 +03:00
u64 res ;
2007-12-18 04:14:04 +03:00
int mult = 1 ;
char * end ;
char last ;
res = simple_strtoul ( str , & end , 10 ) ;
last = end [ 0 ] ;
if ( isalpha ( last ) ) {
last = tolower ( last ) ;
switch ( last ) {
case ' g ' :
mult * = 1024 ;
case ' m ' :
mult * = 1024 ;
case ' k ' :
mult * = 1024 ;
}
res = res * mult ;
}
return res ;
}
2007-08-29 17:11:44 +04:00
static int parse_options ( char * options ,
struct btrfs_root * root ,
char * * subvol_name )
{
char * p ;
2007-12-14 23:30:32 +03:00
struct btrfs_fs_info * info = NULL ;
2007-08-29 17:11:44 +04:00
substring_t args [ MAX_OPT_ARGS ] ;
2007-12-14 23:30:32 +03:00
2007-08-29 17:11:44 +04:00
if ( ! options )
return 1 ;
2007-12-18 04:14:01 +03:00
/*
* strsep changes the string , duplicate it because parse_options
* gets called twice
*/
options = kstrdup ( options , GFP_NOFS ) ;
if ( ! options )
return - ENOMEM ;
if ( root )
info = root - > fs_info ;
2007-08-29 17:11:44 +04:00
while ( ( p = strsep ( & options , " , " ) ) ! = NULL ) {
int token ;
if ( ! * p )
continue ;
token = match_token ( p , tokens , args ) ;
switch ( token ) {
case Opt_subvol :
2007-12-18 04:14:01 +03:00
if ( subvol_name ) {
2007-12-14 23:30:32 +03:00
* subvol_name = match_strdup ( & args [ 0 ] ) ;
2007-12-18 04:14:01 +03:00
}
2007-12-14 23:30:32 +03:00
break ;
case Opt_nodatasum :
2007-12-18 04:14:01 +03:00
if ( info ) {
printk ( " btrfs: setting nodatacsum \n " ) ;
2007-12-14 23:30:32 +03:00
btrfs_set_opt ( info - > mount_opt , NODATASUM ) ;
2007-12-18 04:14:01 +03:00
}
break ;
case Opt_nodatacow :
if ( info ) {
printk ( " btrfs: setting nodatacow \n " ) ;
btrfs_set_opt ( info - > mount_opt , NODATACOW ) ;
btrfs_set_opt ( info - > mount_opt , NODATASUM ) ;
}
2007-08-29 17:11:44 +04:00
break ;
2008-01-18 18:54:22 +03:00
case Opt_ssd :
if ( info ) {
printk ( " btrfs: use ssd allocation scheme \n " ) ;
btrfs_set_opt ( info - > mount_opt , SSD ) ;
}
break ;
2008-01-09 17:23:21 +03:00
case Opt_nobarrier :
if ( info ) {
printk ( " btrfs: turning off barriers \n " ) ;
btrfs_set_opt ( info - > mount_opt , NOBARRIER ) ;
}
break ;
2007-12-18 04:14:04 +03:00
case Opt_max_extent :
if ( info ) {
char * num = match_strdup ( & args [ 0 ] ) ;
if ( num ) {
2007-12-22 00:27:24 +03:00
info - > max_extent =
btrfs_parse_size ( num ) ;
2007-12-18 04:14:04 +03:00
kfree ( num ) ;
info - > max_extent = max_t ( u64 ,
info - > max_extent ,
root - > sectorsize ) ;
printk ( " btrfs: max_extent at %Lu \n " ,
info - > max_extent ) ;
}
}
break ;
2008-01-30 00:03:38 +03:00
case Opt_max_inline :
if ( info ) {
char * num = match_strdup ( & args [ 0 ] ) ;
if ( num ) {
info - > max_inline =
btrfs_parse_size ( num ) ;
kfree ( num ) ;
info - > max_inline = max_t ( u64 ,
info - > max_inline ,
root - > sectorsize ) ;
printk ( " btrfs: max_inline at %Lu \n " ,
info - > max_inline ) ;
}
}
break ;
2008-01-02 18:01:11 +03:00
case Opt_alloc_start :
if ( info ) {
char * num = match_strdup ( & args [ 0 ] ) ;
if ( num ) {
info - > alloc_start =
btrfs_parse_size ( num ) ;
kfree ( num ) ;
printk ( " btrfs: allocations start at "
" %Lu \n " , info - > alloc_start ) ;
}
}
break ;
2007-08-29 17:11:44 +04:00
default :
2007-12-18 04:14:01 +03:00
break ;
2007-08-29 17:11:44 +04:00
}
}
2007-12-18 04:14:01 +03:00
kfree ( options ) ;
2007-08-29 17:11:44 +04:00
return 1 ;
}
2007-06-12 14:35:45 +04:00
static int btrfs_fill_super ( struct super_block * sb , void * data , int silent )
2007-03-29 19:56:46 +04:00
{
2007-06-12 14:35:45 +04:00
struct inode * inode ;
struct dentry * root_dentry ;
struct btrfs_super_block * disk_super ;
struct btrfs_root * tree_root ;
struct btrfs_inode * bi ;
int err ;
2007-04-19 00:15:28 +04:00
2007-06-12 14:35:45 +04:00
sb - > s_maxbytes = MAX_LFS_FILESIZE ;
sb - > s_magic = BTRFS_SUPER_MAGIC ;
sb - > s_op = & btrfs_super_ops ;
2007-11-16 19:45:54 +03:00
sb - > s_xattr = btrfs_xattr_handlers ;
2007-06-12 14:35:45 +04:00
sb - > s_time_gran = 1 ;
2007-04-19 00:15:28 +04:00
2007-06-12 14:35:45 +04:00
tree_root = open_ctree ( sb ) ;
2007-04-16 17:22:45 +04:00
2007-06-12 14:35:45 +04:00
if ( ! tree_root | | IS_ERR ( tree_root ) ) {
printk ( " btrfs: open_ctree failed \n " ) ;
return - EIO ;
2007-04-19 00:15:28 +04:00
}
2007-06-12 14:35:45 +04:00
sb - > s_fs_info = tree_root ;
2007-10-16 00:14:19 +04:00
disk_super = & tree_root - > fs_info - > super_copy ;
2007-06-12 14:35:45 +04:00
inode = btrfs_iget_locked ( sb , btrfs_super_root_dir ( disk_super ) ,
tree_root ) ;
bi = BTRFS_I ( inode ) ;
bi - > location . objectid = inode - > i_ino ;
bi - > location . offset = 0 ;
bi - > root = tree_root ;
2007-08-28 00:49:44 +04:00
2007-06-12 14:35:45 +04:00
btrfs_set_key_type ( & bi - > location , BTRFS_INODE_ITEM_KEY ) ;
2007-04-19 00:15:28 +04:00
2007-06-12 14:35:45 +04:00
if ( ! inode ) {
2007-04-16 17:22:45 +04:00
err = - ENOMEM ;
2007-06-12 14:35:45 +04:00
goto fail_close ;
2007-03-29 23:15:27 +04:00
}
2007-06-12 14:35:45 +04:00
if ( inode - > i_state & I_NEW ) {
btrfs_read_locked_inode ( inode ) ;
unlock_new_inode ( inode ) ;
2007-03-29 23:15:27 +04:00
}
2007-06-12 14:35:45 +04:00
root_dentry = d_alloc_root ( inode ) ;
if ( ! root_dentry ) {
iput ( inode ) ;
err = - ENOMEM ;
goto fail_close ;
2007-03-29 23:15:27 +04:00
}
2007-08-29 23:47:34 +04:00
2007-12-14 23:30:32 +03:00
parse_options ( ( char * ) data , tree_root , NULL ) ;
2007-08-29 23:47:34 +04:00
/* this does the super kobj at the same time */
err = btrfs_sysfs_add_super ( tree_root - > fs_info ) ;
if ( err )
goto fail_close ;
2007-06-12 14:35:45 +04:00
sb - > s_root = root_dentry ;
btrfs_transaction_queue_work ( tree_root , HZ * 30 ) ;
2008-02-21 00:11:05 +03:00
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
save_mount_options ( sb , data ) ;
# endif
2007-04-11 00:58:11 +04:00
return 0 ;
2007-06-12 14:35:45 +04:00
fail_close :
close_ctree ( tree_root ) ;
return err ;
2007-04-11 00:58:11 +04:00
}
2007-06-12 14:35:45 +04:00
static int btrfs_sync_fs ( struct super_block * sb , int wait )
2007-04-10 17:27:04 +04:00
{
struct btrfs_trans_handle * trans ;
2007-06-12 14:35:45 +04:00
struct btrfs_root * root ;
2007-04-10 17:27:04 +04:00
int ret ;
2007-06-12 14:35:45 +04:00
root = btrfs_sb ( sb ) ;
2007-04-11 00:58:11 +04:00
2007-06-12 14:35:45 +04:00
sb - > s_dirt = 0 ;
if ( ! wait ) {
filemap_flush ( root - > fs_info - > btree_inode - > i_mapping ) ;
return 0 ;
}
2007-08-10 22:06:19 +04:00
btrfs_clean_old_snapshots ( root ) ;
2007-04-10 17:27:04 +04:00
mutex_lock ( & root - > fs_info - > fs_mutex ) ;
2007-08-10 22:06:19 +04:00
btrfs_defrag_dirty_roots ( root - > fs_info ) ;
2007-04-10 17:27:04 +04:00
trans = btrfs_start_transaction ( root , 1 ) ;
ret = btrfs_commit_transaction ( trans , root ) ;
2007-06-12 14:35:45 +04:00
sb - > s_dirt = 0 ;
2007-04-10 17:27:04 +04:00
mutex_unlock ( & root - > fs_info - > fs_mutex ) ;
2007-06-22 22:16:25 +04:00
return ret ;
2007-04-02 18:50:19 +04:00
}
2007-06-12 14:35:45 +04:00
static void btrfs_write_super ( struct super_block * sb )
2007-04-02 18:50:19 +04:00
{
2007-06-12 14:35:45 +04:00
sb - > s_dirt = 0 ;
2007-04-02 18:50:19 +04:00
}
2007-08-29 17:11:44 +04:00
/*
* This is almost a copy of get_sb_bdev in fs / super . c .
* We need the local copy to allow direct mounting of
* subvolumes , but this could be easily integrated back
* into the generic version . - - hch
*/
/* start copy & paste */
static int set_bdev_super ( struct super_block * s , void * data )
{
s - > s_bdev = data ;
s - > s_dev = s - > s_bdev - > bd_dev ;
return 0 ;
}
static int test_bdev_super ( struct super_block * s , void * data )
{
return ( void * ) s - > s_bdev = = data ;
}
int btrfs_get_sb_bdev ( struct file_system_type * fs_type ,
int flags , const char * dev_name , void * data ,
int ( * fill_super ) ( struct super_block * , void * , int ) ,
struct vfsmount * mnt , const char * subvol )
{
struct block_device * bdev = NULL ;
struct super_block * s ;
struct dentry * root ;
int error = 0 ;
bdev = open_bdev_excl ( dev_name , flags , fs_type ) ;
if ( IS_ERR ( bdev ) )
return PTR_ERR ( bdev ) ;
/*
* once the super is inserted into the list by sget , s_umount
* will protect the lockfs code from trying to start a snapshot
* while we are mounting
*/
down ( & bdev - > bd_mount_sem ) ;
s = sget ( fs_type , test_bdev_super , set_bdev_super , bdev ) ;
up ( & bdev - > bd_mount_sem ) ;
if ( IS_ERR ( s ) )
goto error_s ;
if ( s - > s_root ) {
if ( ( flags ^ s - > s_flags ) & MS_RDONLY ) {
up_write ( & s - > s_umount ) ;
deactivate_super ( s ) ;
error = - EBUSY ;
goto error_bdev ;
}
close_bdev_excl ( bdev ) ;
} else {
char b [ BDEVNAME_SIZE ] ;
s - > s_flags = flags ;
strlcpy ( s - > s_id , bdevname ( bdev , b ) , sizeof ( s - > s_id ) ) ;
sb_set_blocksize ( s , block_size ( bdev ) ) ;
error = fill_super ( s , data , flags & MS_SILENT ? 1 : 0 ) ;
if ( error ) {
up_write ( & s - > s_umount ) ;
deactivate_super ( s ) ;
goto error ;
}
s - > s_flags | = MS_ACTIVE ;
}
if ( subvol ) {
root = lookup_one_len ( subvol , s - > s_root , strlen ( subvol ) ) ;
if ( IS_ERR ( root ) ) {
up_write ( & s - > s_umount ) ;
deactivate_super ( s ) ;
error = PTR_ERR ( root ) ;
goto error ;
}
if ( ! root - > d_inode ) {
dput ( root ) ;
up_write ( & s - > s_umount ) ;
deactivate_super ( s ) ;
error = - ENXIO ;
goto error ;
}
} else {
root = dget ( s - > s_root ) ;
}
mnt - > mnt_sb = s ;
mnt - > mnt_root = root ;
return 0 ;
error_s :
error = PTR_ERR ( s ) ;
error_bdev :
close_bdev_excl ( bdev ) ;
error :
return error ;
}
/* end copy & paste */
2007-03-21 18:12:56 +03:00
static int btrfs_get_sb ( struct file_system_type * fs_type ,
2007-08-29 17:11:44 +04:00
int flags , const char * dev_name , void * data , struct vfsmount * mnt )
2007-03-21 18:12:56 +03:00
{
2007-08-29 17:11:44 +04:00
int ret ;
2007-08-29 17:11:44 +04:00
char * subvol_name = NULL ;
2007-08-29 17:11:44 +04:00
2007-08-29 17:11:44 +04:00
parse_options ( ( char * ) data , NULL , & subvol_name ) ;
2007-08-29 17:11:44 +04:00
ret = btrfs_get_sb_bdev ( fs_type , flags , dev_name , data ,
btrfs_fill_super , mnt ,
subvol_name ? subvol_name : " default " ) ;
2007-12-18 04:14:04 +03:00
if ( subvol_name )
kfree ( subvol_name ) ;
2007-08-29 17:11:44 +04:00
return ret ;
2007-03-21 18:12:56 +03:00
}
2007-04-20 05:01:03 +04:00
static int btrfs_statfs ( struct dentry * dentry , struct kstatfs * buf )
{
struct btrfs_root * root = btrfs_sb ( dentry - > d_sb ) ;
2007-06-26 18:06:50 +04:00
struct btrfs_super_block * disk_super = & root - > fs_info - > super_copy ;
2007-10-16 00:15:53 +04:00
int bits = dentry - > d_sb - > s_blocksize_bits ;
2007-04-20 05:01:03 +04:00
buf - > f_namelen = BTRFS_NAME_LEN ;
2007-10-16 00:15:53 +04:00
buf - > f_blocks = btrfs_super_total_bytes ( disk_super ) > > bits ;
buf - > f_bfree = buf - > f_blocks -
( btrfs_super_bytes_used ( disk_super ) > > bits ) ;
2007-04-20 05:01:03 +04:00
buf - > f_bavail = buf - > f_bfree ;
buf - > f_bsize = dentry - > d_sb - > s_blocksize ;
buf - > f_type = BTRFS_SUPER_MAGIC ;
return 0 ;
}
2007-04-24 19:52:22 +04:00
2007-03-21 18:12:56 +03:00
static struct file_system_type btrfs_fs_type = {
. owner = THIS_MODULE ,
. name = " btrfs " ,
. get_sb = btrfs_get_sb ,
. kill_sb = kill_block_super ,
. fs_flags = FS_REQUIRES_DEV ,
} ;
2008-01-22 20:46:56 +03:00
static void btrfs_write_super_lockfs ( struct super_block * sb )
{
struct btrfs_root * root = btrfs_sb ( sb ) ;
btrfs_transaction_flush_work ( root ) ;
}
static void btrfs_unlockfs ( struct super_block * sb )
{
struct btrfs_root * root = btrfs_sb ( sb ) ;
btrfs_transaction_queue_work ( root , HZ * 30 ) ;
}
2007-03-21 18:12:56 +03:00
2007-03-22 19:13:20 +03:00
static struct super_operations btrfs_super_ops = {
2007-03-25 21:44:56 +04:00
. delete_inode = btrfs_delete_inode ,
2008-01-16 19:44:43 +03:00
. put_inode = btrfs_put_inode ,
2007-03-22 19:13:20 +03:00
. put_super = btrfs_put_super ,
2007-03-23 17:01:08 +03:00
. write_super = btrfs_write_super ,
. sync_fs = btrfs_sync_fs ,
2008-02-21 00:11:05 +03:00
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
. read_inode = btrfs_read_locked_inode ,
# else
. show_options = generic_show_options ,
# endif
2007-03-26 20:00:39 +04:00
. write_inode = btrfs_write_inode ,
2007-04-24 19:52:22 +04:00
. dirty_inode = btrfs_dirty_inode ,
2007-04-02 18:50:19 +04:00
. alloc_inode = btrfs_alloc_inode ,
. destroy_inode = btrfs_destroy_inode ,
2007-04-20 05:01:03 +04:00
. statfs = btrfs_statfs ,
2008-01-22 20:46:56 +03:00
. write_super_lockfs = btrfs_write_super_lockfs ,
. unlockfs = btrfs_unlockfs ,
2007-03-22 19:13:20 +03:00
} ;
2007-03-21 18:12:56 +03:00
static int __init init_btrfs_fs ( void )
{
2007-04-02 18:50:19 +04:00
int err ;
2007-08-29 23:47:34 +04:00
err = btrfs_init_sysfs ( ) ;
if ( err )
return err ;
2007-06-08 23:33:54 +04:00
btrfs_init_transaction_sys ( ) ;
2007-06-12 14:35:45 +04:00
err = btrfs_init_cachep ( ) ;
2007-04-02 18:50:19 +04:00
if ( err )
2007-11-19 18:22:33 +03:00
goto free_transaction_sys ;
2008-01-25 00:13:08 +03:00
err = extent_io_init ( ) ;
2007-11-19 18:22:33 +03:00
if ( err )
goto free_cachep ;
2008-01-25 00:13:08 +03:00
err = extent_map_init ( ) ;
if ( err )
goto free_extent_io ;
2007-11-19 18:22:33 +03:00
err = register_filesystem ( & btrfs_fs_type ) ;
if ( err )
goto free_extent_map ;
return 0 ;
free_extent_map :
extent_map_exit ( ) ;
2008-01-25 00:13:08 +03:00
free_extent_io :
extent_io_exit ( ) ;
2007-11-19 18:22:33 +03:00
free_cachep :
btrfs_destroy_cachep ( ) ;
free_transaction_sys :
btrfs_exit_transaction_sys ( ) ;
btrfs_exit_sysfs ( ) ;
return err ;
2007-03-21 18:12:56 +03:00
}
static void __exit exit_btrfs_fs ( void )
{
2007-06-08 23:33:54 +04:00
btrfs_exit_transaction_sys ( ) ;
2007-06-12 14:35:45 +04:00
btrfs_destroy_cachep ( ) ;
2007-08-28 00:49:44 +04:00
extent_map_exit ( ) ;
2008-01-25 00:13:08 +03:00
extent_io_exit ( ) ;
2007-03-21 18:12:56 +03:00
unregister_filesystem ( & btrfs_fs_type ) ;
2007-08-29 23:47:34 +04:00
btrfs_exit_sysfs ( ) ;
2007-03-21 18:12:56 +03:00
}
module_init ( init_btrfs_fs )
module_exit ( exit_btrfs_fs )
MODULE_LICENSE ( " GPL " ) ;