2005-04-17 02:20:36 +04:00
/*
* Copyright 2000 by Hans Reiser , licensing governed by reiserfs / README
*
* Trivial changes by Alan Cox to add the LFS fixes
*
* Trivial Changes :
* Rights granted to Hans Reiser to redistribute under other terms providing
* he accepts all liability including but not limited to patent , fitness
* for purpose , and direct or indirect claims arising from failure to perform .
*
* NO WARRANTY
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/vmalloc.h>
# include <linux/time.h>
# include <asm/uaccess.h>
# include <linux/reiserfs_fs.h>
# include <linux/reiserfs_acl.h>
# include <linux/reiserfs_xattr.h>
# include <linux/smp_lock.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/buffer_head.h>
# include <linux/vfs.h>
# include <linux/namespace.h>
# include <linux/mount.h>
# include <linux/namei.h>
# include <linux/quotaops.h>
struct file_system_type reiserfs_fs_type ;
static const char reiserfs_3_5_magic_string [ ] = REISERFS_SUPER_MAGIC_STRING ;
static const char reiserfs_3_6_magic_string [ ] = REISER2FS_SUPER_MAGIC_STRING ;
static const char reiserfs_jr_magic_string [ ] = REISER2FS_JR_SUPER_MAGIC_STRING ;
int is_reiserfs_3_5 ( struct reiserfs_super_block * rs )
{
return ! strncmp ( rs - > s_v1 . s_magic , reiserfs_3_5_magic_string ,
strlen ( reiserfs_3_5_magic_string ) ) ;
}
int is_reiserfs_3_6 ( struct reiserfs_super_block * rs )
{
return ! strncmp ( rs - > s_v1 . s_magic , reiserfs_3_6_magic_string ,
strlen ( reiserfs_3_6_magic_string ) ) ;
}
int is_reiserfs_jr ( struct reiserfs_super_block * rs )
{
return ! strncmp ( rs - > s_v1 . s_magic , reiserfs_jr_magic_string ,
strlen ( reiserfs_jr_magic_string ) ) ;
}
static int is_any_reiserfs_magic_string ( struct reiserfs_super_block * rs )
{
return ( is_reiserfs_3_5 ( rs ) | | is_reiserfs_3_6 ( rs ) | |
is_reiserfs_jr ( rs ) ) ;
}
static int reiserfs_remount ( struct super_block * s , int * flags , char * data ) ;
static int reiserfs_statfs ( struct super_block * s , struct kstatfs * buf ) ;
static int reiserfs_sync_fs ( struct super_block * s , int wait )
{
if ( ! ( s - > s_flags & MS_RDONLY ) ) {
struct reiserfs_transaction_handle th ;
reiserfs_write_lock ( s ) ;
if ( ! journal_begin ( & th , s , 1 ) )
if ( ! journal_end_sync ( & th , s , 1 ) )
reiserfs_flush_old_commits ( s ) ;
s - > s_dirt = 0 ; /* Even if it's not true.
* We ' ll loop forever in sync_supers otherwise */
reiserfs_write_unlock ( s ) ;
} else {
s - > s_dirt = 0 ;
}
return 0 ;
}
static void reiserfs_write_super ( struct super_block * s )
{
reiserfs_sync_fs ( s , 1 ) ;
}
static void reiserfs_write_super_lockfs ( struct super_block * s )
{
struct reiserfs_transaction_handle th ;
reiserfs_write_lock ( s ) ;
if ( ! ( s - > s_flags & MS_RDONLY ) ) {
int err = journal_begin ( & th , s , 1 ) ;
if ( err ) {
reiserfs_block_writes ( & th ) ;
} else {
reiserfs_prepare_for_journal ( s , SB_BUFFER_WITH_SB ( s ) , 1 ) ;
journal_mark_dirty ( & th , s , SB_BUFFER_WITH_SB ( s ) ) ;
reiserfs_block_writes ( & th ) ;
journal_end_sync ( & th , s , 1 ) ;
}
}
s - > s_dirt = 0 ;
reiserfs_write_unlock ( s ) ;
}
static void reiserfs_unlockfs ( struct super_block * s ) {
reiserfs_allow_writes ( s ) ;
}
2005-05-01 19:59:17 +04:00
extern const struct in_core_key MAX_IN_CORE_KEY ;
2005-04-17 02:20:36 +04:00
/* this is used to delete "save link" when there are no items of a
file it points to . It can either happen if unlink is completed but
" save unlink " removal , or if file has both unlink and truncate
pending and as unlink completes first ( because key of " save link "
protecting unlink is bigger that a key lf " save link " which
protects truncate ) , so there left no items to make truncate
completion on */
static int remove_save_link_only ( struct super_block * s , struct reiserfs_key * key , int oid_free )
{
struct reiserfs_transaction_handle th ;
int err ;
/* we are going to do one balancing */
err = journal_begin ( & th , s , JOURNAL_PER_BALANCE_CNT ) ;
if ( err )
return err ;
reiserfs_delete_solid_item ( & th , NULL , key ) ;
if ( oid_free )
/* removals are protected by direct items */
reiserfs_release_objectid ( & th , le32_to_cpu ( key - > k_objectid ) ) ;
return journal_end ( & th , s , JOURNAL_PER_BALANCE_CNT ) ;
}
# ifdef CONFIG_QUOTA
static int reiserfs_quota_on_mount ( struct super_block * , int ) ;
# endif
/* look for uncompleted unlinks and truncates and complete them */
static int finish_unfinished ( struct super_block * s )
{
INITIALIZE_PATH ( path ) ;
struct cpu_key max_cpu_key , obj_key ;
struct reiserfs_key save_link_key ;
int retval = 0 ;
struct item_head * ih ;
struct buffer_head * bh ;
int item_pos ;
char * item ;
int done ;
struct inode * inode ;
int truncate ;
# ifdef CONFIG_QUOTA
int i ;
int ms_active_set ;
# endif
/* compose key to look for "save" links */
max_cpu_key . version = KEY_FORMAT_3_5 ;
2005-05-22 03:33:34 +04:00
max_cpu_key . on_disk_key . k_dir_id = ~ 0U ;
max_cpu_key . on_disk_key . k_objectid = ~ 0U ;
set_cpu_key_k_offset ( & max_cpu_key , ~ 0U ) ;
2005-04-17 02:20:36 +04:00
max_cpu_key . key_length = 3 ;
# ifdef CONFIG_QUOTA
/* Needed for iput() to work correctly and not trash data */
if ( s - > s_flags & MS_ACTIVE ) {
ms_active_set = 0 ;
} else {
ms_active_set = 1 ;
s - > s_flags | = MS_ACTIVE ;
}
/* Turn on quotas so that they are updated correctly */
for ( i = 0 ; i < MAXQUOTAS ; i + + ) {
if ( REISERFS_SB ( s ) - > s_qf_names [ i ] ) {
int ret = reiserfs_quota_on_mount ( s , i ) ;
if ( ret < 0 )
reiserfs_warning ( s , " reiserfs: cannot turn on journalled quota: error %d " , ret ) ;
}
}
# endif
done = 0 ;
REISERFS_SB ( s ) - > s_is_unlinked_ok = 1 ;
while ( ! retval ) {
retval = search_item ( s , & max_cpu_key , & path ) ;
if ( retval ! = ITEM_NOT_FOUND ) {
reiserfs_warning ( s , " vs-2140: finish_unfinished: search_by_key returned %d " ,
retval ) ;
break ;
}
bh = get_last_bh ( & path ) ;
item_pos = get_item_pos ( & path ) ;
if ( item_pos ! = B_NR_ITEMS ( bh ) ) {
reiserfs_warning ( s , " vs-2060: finish_unfinished: wrong position found " ) ;
break ;
}
item_pos - - ;
ih = B_N_PITEM_HEAD ( bh , item_pos ) ;
if ( le32_to_cpu ( ih - > ih_key . k_dir_id ) ! = MAX_KEY_OBJECTID )
/* there are no "save" links anymore */
break ;
save_link_key = ih - > ih_key ;
if ( is_indirect_le_ih ( ih ) )
truncate = 1 ;
else
truncate = 0 ;
/* reiserfs_iget needs k_dirid and k_objectid only */
item = B_I_PITEM ( bh , ih ) ;
2005-05-01 19:59:18 +04:00
obj_key . on_disk_key . k_dir_id = le32_to_cpu ( * ( __le32 * ) item ) ;
2005-04-17 02:20:36 +04:00
obj_key . on_disk_key . k_objectid = le32_to_cpu ( ih - > ih_key . k_objectid ) ;
2005-05-01 19:59:19 +04:00
obj_key . on_disk_key . k_offset = 0 ;
obj_key . on_disk_key . k_type = 0 ;
2005-04-17 02:20:36 +04:00
pathrelse ( & path ) ;
inode = reiserfs_iget ( s , & obj_key ) ;
if ( ! inode ) {
/* the unlink almost completed, it just did not manage to remove
" save " link and release objectid */
reiserfs_warning ( s , " vs-2180: finish_unfinished: iget failed for %K " ,
& obj_key ) ;
retval = remove_save_link_only ( s , & save_link_key , 1 ) ;
continue ;
}
if ( ! truncate & & inode - > i_nlink ) {
/* file is not unlinked */
reiserfs_warning ( s , " vs-2185: finish_unfinished: file %K is not unlinked " ,
& obj_key ) ;
retval = remove_save_link_only ( s , & save_link_key , 0 ) ;
continue ;
}
DQUOT_INIT ( inode ) ;
if ( truncate & & S_ISDIR ( inode - > i_mode ) ) {
/* We got a truncate request for a dir which is impossible.
The only imaginable way is to execute unfinished truncate request
then boot into old kernel , remove the file and create dir with
the same key . */
reiserfs_warning ( s , " green-2101: impossible truncate on a directory %k. Please report " , INODE_PKEY ( inode ) ) ;
retval = remove_save_link_only ( s , & save_link_key , 0 ) ;
truncate = 0 ;
iput ( inode ) ;
continue ;
}
if ( truncate ) {
REISERFS_I ( inode ) - > i_flags | = i_link_saved_truncate_mask ;
/* not completed truncate found. New size was committed together
with " save " link */
reiserfs_info ( s , " Truncating %k to %Ld .. " ,
INODE_PKEY ( inode ) , inode - > i_size ) ;
reiserfs_truncate_file ( inode , 0 /*don't update modification time*/ ) ;
retval = remove_save_link ( inode , truncate ) ;
} else {
REISERFS_I ( inode ) - > i_flags | = i_link_saved_unlink_mask ;
/* not completed unlink (rmdir) found */
reiserfs_info ( s , " Removing %k.. " , INODE_PKEY ( inode ) ) ;
/* removal gets completed in iput */
retval = 0 ;
}
iput ( inode ) ;
printk ( " done \n " ) ;
done + + ;
}
REISERFS_SB ( s ) - > s_is_unlinked_ok = 0 ;
# ifdef CONFIG_QUOTA
/* Turn quotas off */
for ( i = 0 ; i < MAXQUOTAS ; i + + ) {
if ( sb_dqopt ( s ) - > files [ i ] )
vfs_quota_off_mount ( s , i ) ;
}
if ( ms_active_set )
/* Restore the flag back */
s - > s_flags & = ~ MS_ACTIVE ;
# endif
pathrelse ( & path ) ;
if ( done )
reiserfs_info ( s , " There were %d uncompleted unlinks/truncates. "
" Completed \n " , done ) ;
return retval ;
}
/* to protect file being unlinked from getting lost we "safe" link files
being unlinked . This link will be deleted in the same transaction with last
item of file . mounting the filesytem we scan all these links and remove
files which almost got lost */
void add_save_link ( struct reiserfs_transaction_handle * th ,
struct inode * inode , int truncate )
{
INITIALIZE_PATH ( path ) ;
int retval ;
struct cpu_key key ;
struct item_head ih ;
2005-05-01 19:59:18 +04:00
__le32 link ;
2005-04-17 02:20:36 +04:00
BUG_ON ( ! th - > t_trans_id ) ;
/* file can only get one "save link" of each kind */
RFALSE ( truncate & &
( REISERFS_I ( inode ) - > i_flags & i_link_saved_truncate_mask ) ,
" saved link already exists for truncated inode %lx " ,
( long ) inode - > i_ino ) ;
RFALSE ( ! truncate & &
( REISERFS_I ( inode ) - > i_flags & i_link_saved_unlink_mask ) ,
" saved link already exists for unlinked inode %lx " ,
( long ) inode - > i_ino ) ;
/* setup key of "save" link */
key . version = KEY_FORMAT_3_5 ;
key . on_disk_key . k_dir_id = MAX_KEY_OBJECTID ;
key . on_disk_key . k_objectid = inode - > i_ino ;
if ( ! truncate ) {
/* unlink, rmdir, rename */
set_cpu_key_k_offset ( & key , 1 + inode - > i_sb - > s_blocksize ) ;
set_cpu_key_k_type ( & key , TYPE_DIRECT ) ;
/* item head of "safe" link */
make_le_item_head ( & ih , & key , key . version , 1 + inode - > i_sb - > s_blocksize , TYPE_DIRECT ,
4 /*length*/ , 0xffff /*free space*/ ) ;
} else {
/* truncate */
if ( S_ISDIR ( inode - > i_mode ) )
reiserfs_warning ( inode - > i_sb , " green-2102: Adding a truncate savelink for a directory %k! Please report " , INODE_PKEY ( inode ) ) ;
set_cpu_key_k_offset ( & key , 1 ) ;
set_cpu_key_k_type ( & key , TYPE_INDIRECT ) ;
/* item head of "safe" link */
make_le_item_head ( & ih , & key , key . version , 1 , TYPE_INDIRECT ,
4 /*length*/ , 0 /*free space*/ ) ;
}
key . key_length = 3 ;
/* look for its place in the tree */
retval = search_item ( inode - > i_sb , & key , & path ) ;
if ( retval ! = ITEM_NOT_FOUND ) {
if ( retval ! = - ENOSPC )
reiserfs_warning ( inode - > i_sb , " vs-2100: add_save_link: "
" search_by_key (%K) returned %d " , & key , retval ) ;
pathrelse ( & path ) ;
return ;
}
/* body of "save" link */
link = INODE_PKEY ( inode ) - > k_dir_id ;
/* put "save" link inot tree, don't charge quota to anyone */
retval = reiserfs_insert_item ( th , & path , & key , & ih , NULL , ( char * ) & link ) ;
if ( retval ) {
if ( retval ! = - ENOSPC )
reiserfs_warning ( inode - > i_sb , " vs-2120: add_save_link: insert_item returned %d " ,
retval ) ;
} else {
if ( truncate )
REISERFS_I ( inode ) - > i_flags | = i_link_saved_truncate_mask ;
else
REISERFS_I ( inode ) - > i_flags | = i_link_saved_unlink_mask ;
}
}
/* this opens transaction unlike add_save_link */
int remove_save_link ( struct inode * inode , int truncate )
{
struct reiserfs_transaction_handle th ;
struct reiserfs_key key ;
int err ;
/* we are going to do one balancing only */
err = journal_begin ( & th , inode - > i_sb , JOURNAL_PER_BALANCE_CNT ) ;
if ( err )
return err ;
/* setup key of "save" link */
key . k_dir_id = cpu_to_le32 ( MAX_KEY_OBJECTID ) ;
key . k_objectid = INODE_PKEY ( inode ) - > k_objectid ;
if ( ! truncate ) {
/* unlink, rmdir, rename */
set_le_key_k_offset ( KEY_FORMAT_3_5 , & key ,
1 + inode - > i_sb - > s_blocksize ) ;
set_le_key_k_type ( KEY_FORMAT_3_5 , & key , TYPE_DIRECT ) ;
} else {
/* truncate */
set_le_key_k_offset ( KEY_FORMAT_3_5 , & key , 1 ) ;
set_le_key_k_type ( KEY_FORMAT_3_5 , & key , TYPE_INDIRECT ) ;
}
if ( ( truncate & &
( REISERFS_I ( inode ) - > i_flags & i_link_saved_truncate_mask ) ) | |
( ! truncate & &
( REISERFS_I ( inode ) - > i_flags & i_link_saved_unlink_mask ) ) )
/* don't take quota bytes from anywhere */
reiserfs_delete_solid_item ( & th , NULL , & key ) ;
if ( ! truncate ) {
reiserfs_release_objectid ( & th , inode - > i_ino ) ;
REISERFS_I ( inode ) - > i_flags & = ~ i_link_saved_unlink_mask ;
} else
REISERFS_I ( inode ) - > i_flags & = ~ i_link_saved_truncate_mask ;
return journal_end ( & th , inode - > i_sb , JOURNAL_PER_BALANCE_CNT ) ;
}
static void reiserfs_put_super ( struct super_block * s )
{
int i ;
struct reiserfs_transaction_handle th ;
th . t_trans_id = 0 ;
if ( REISERFS_SB ( s ) - > xattr_root ) {
d_invalidate ( REISERFS_SB ( s ) - > xattr_root ) ;
dput ( REISERFS_SB ( s ) - > xattr_root ) ;
}
if ( REISERFS_SB ( s ) - > priv_root ) {
d_invalidate ( REISERFS_SB ( s ) - > priv_root ) ;
dput ( REISERFS_SB ( s ) - > priv_root ) ;
}
/* change file system state to current state if it was mounted with read-write permissions */
if ( ! ( s - > s_flags & MS_RDONLY ) ) {
if ( ! journal_begin ( & th , s , 10 ) ) {
reiserfs_prepare_for_journal ( s , SB_BUFFER_WITH_SB ( s ) , 1 ) ;
set_sb_umount_state ( SB_DISK_SUPER_BLOCK ( s ) , REISERFS_SB ( s ) - > s_mount_state ) ;
journal_mark_dirty ( & th , s , SB_BUFFER_WITH_SB ( s ) ) ;
}
}
/* note, journal_release checks for readonly mount, and can decide not
* * to do a journal_end
*/
journal_release ( & th , s ) ;
for ( i = 0 ; i < SB_BMAP_NR ( s ) ; i + + )
brelse ( SB_AP_BITMAP ( s ) [ i ] . bh ) ;
vfree ( SB_AP_BITMAP ( s ) ) ;
brelse ( SB_BUFFER_WITH_SB ( s ) ) ;
print_statistics ( s ) ;
if ( REISERFS_SB ( s ) - > s_kmallocs ! = 0 ) {
reiserfs_warning ( s , " vs-2004: reiserfs_put_super: allocated memory left %d " ,
REISERFS_SB ( s ) - > s_kmallocs ) ;
}
if ( REISERFS_SB ( s ) - > reserved_blocks ! = 0 ) {
reiserfs_warning ( s , " green-2005: reiserfs_put_super: reserved blocks left %d " ,
REISERFS_SB ( s ) - > reserved_blocks ) ;
}
reiserfs_proc_info_done ( s ) ;
kfree ( s - > s_fs_info ) ;
s - > s_fs_info = NULL ;
return ;
}
static kmem_cache_t * reiserfs_inode_cachep ;
static struct inode * reiserfs_alloc_inode ( struct super_block * sb )
{
struct reiserfs_inode_info * ei ;
ei = ( struct reiserfs_inode_info * ) kmem_cache_alloc ( reiserfs_inode_cachep , SLAB_KERNEL ) ;
if ( ! ei )
return NULL ;
return & ei - > vfs_inode ;
}
static void reiserfs_destroy_inode ( struct inode * inode )
{
kmem_cache_free ( reiserfs_inode_cachep , REISERFS_I ( inode ) ) ;
}
static void init_once ( void * foo , kmem_cache_t * cachep , unsigned long flags )
{
struct reiserfs_inode_info * ei = ( struct reiserfs_inode_info * ) foo ;
if ( ( flags & ( SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR ) ) = =
SLAB_CTOR_CONSTRUCTOR ) {
INIT_LIST_HEAD ( & ei - > i_prealloc_list ) ;
inode_init_once ( & ei - > vfs_inode ) ;
ei - > i_acl_access = NULL ;
ei - > i_acl_default = NULL ;
}
}
static int init_inodecache ( void )
{
reiserfs_inode_cachep = kmem_cache_create ( " reiser_inode_cache " ,
sizeof ( struct reiserfs_inode_info ) ,
0 , SLAB_RECLAIM_ACCOUNT ,
init_once , NULL ) ;
if ( reiserfs_inode_cachep = = NULL )
return - ENOMEM ;
return 0 ;
}
static void destroy_inodecache ( void )
{
if ( kmem_cache_destroy ( reiserfs_inode_cachep ) )
reiserfs_warning ( NULL , " reiserfs_inode_cache: not all structures were freed " ) ;
}
/* we don't mark inodes dirty, we just log them */
static void reiserfs_dirty_inode ( struct inode * inode ) {
struct reiserfs_transaction_handle th ;
int err = 0 ;
if ( inode - > i_sb - > s_flags & MS_RDONLY ) {
reiserfs_warning ( inode - > i_sb , " clm-6006: writing inode %lu on readonly FS " ,
inode - > i_ino ) ;
return ;
}
reiserfs_write_lock ( inode - > i_sb ) ;
/* this is really only used for atime updates, so they don't have
* * to be included in O_SYNC or fsync
*/
err = journal_begin ( & th , inode - > i_sb , 1 ) ;
if ( err ) {
reiserfs_write_unlock ( inode - > i_sb ) ;
return ;
}
reiserfs_update_sd ( & th , inode ) ;
journal_end ( & th , inode - > i_sb , 1 ) ;
reiserfs_write_unlock ( inode - > i_sb ) ;
}
static void reiserfs_clear_inode ( struct inode * inode )
{
struct posix_acl * acl ;
acl = REISERFS_I ( inode ) - > i_acl_access ;
if ( acl & & ! IS_ERR ( acl ) )
posix_acl_release ( acl ) ;
REISERFS_I ( inode ) - > i_acl_access = NULL ;
acl = REISERFS_I ( inode ) - > i_acl_default ;
if ( acl & & ! IS_ERR ( acl ) )
posix_acl_release ( acl ) ;
REISERFS_I ( inode ) - > i_acl_default = NULL ;
}
# ifdef CONFIG_QUOTA
static ssize_t reiserfs_quota_write ( struct super_block * , int , const char * , size_t , loff_t ) ;
static ssize_t reiserfs_quota_read ( struct super_block * , int , char * , size_t , loff_t ) ;
# endif
static struct super_operations reiserfs_sops =
{
. alloc_inode = reiserfs_alloc_inode ,
. destroy_inode = reiserfs_destroy_inode ,
. write_inode = reiserfs_write_inode ,
. dirty_inode = reiserfs_dirty_inode ,
. delete_inode = reiserfs_delete_inode ,
. clear_inode = reiserfs_clear_inode ,
. put_super = reiserfs_put_super ,
. write_super = reiserfs_write_super ,
. sync_fs = reiserfs_sync_fs ,
. write_super_lockfs = reiserfs_write_super_lockfs ,
. unlockfs = reiserfs_unlockfs ,
. statfs = reiserfs_statfs ,
. remount_fs = reiserfs_remount ,
# ifdef CONFIG_QUOTA
. quota_read = reiserfs_quota_read ,
. quota_write = reiserfs_quota_write ,
# endif
} ;
# ifdef CONFIG_QUOTA
# define QTYPE2NAME(t) ((t)==USRQUOTA?"user":"group")
static int reiserfs_dquot_initialize ( struct inode * , int ) ;
static int reiserfs_dquot_drop ( struct inode * ) ;
static int reiserfs_write_dquot ( struct dquot * ) ;
static int reiserfs_acquire_dquot ( struct dquot * ) ;
static int reiserfs_release_dquot ( struct dquot * ) ;
static int reiserfs_mark_dquot_dirty ( struct dquot * ) ;
static int reiserfs_write_info ( struct super_block * , int ) ;
static int reiserfs_quota_on ( struct super_block * , int , int , char * ) ;
static struct dquot_operations reiserfs_quota_operations =
{
. initialize = reiserfs_dquot_initialize ,
. drop = reiserfs_dquot_drop ,
. alloc_space = dquot_alloc_space ,
. alloc_inode = dquot_alloc_inode ,
. free_space = dquot_free_space ,
. free_inode = dquot_free_inode ,
. transfer = dquot_transfer ,
. write_dquot = reiserfs_write_dquot ,
. acquire_dquot = reiserfs_acquire_dquot ,
. release_dquot = reiserfs_release_dquot ,
. mark_dirty = reiserfs_mark_dquot_dirty ,
. write_info = reiserfs_write_info ,
} ;
static struct quotactl_ops reiserfs_qctl_operations =
{
. quota_on = reiserfs_quota_on ,
. quota_off = vfs_quota_off ,
. quota_sync = vfs_quota_sync ,
. get_info = vfs_get_dqinfo ,
. set_info = vfs_set_dqinfo ,
. get_dqblk = vfs_get_dqblk ,
. set_dqblk = vfs_set_dqblk ,
} ;
# endif
static struct export_operations reiserfs_export_ops = {
. encode_fh = reiserfs_encode_fh ,
. decode_fh = reiserfs_decode_fh ,
. get_parent = reiserfs_get_parent ,
. get_dentry = reiserfs_get_dentry ,
} ;
/* this struct is used in reiserfs_getopt () for containing the value for those
mount options that have values rather than being toggles . */
typedef struct {
char * value ;
int setmask ; /* bitmask which is to set on mount_options bitmask when this
value is found , 0 is no bits are to be changed . */
int clrmask ; /* bitmask which is to clear on mount_options bitmask when this
value is found , 0 is no bits are to be changed . This is
applied BEFORE setmask */
} arg_desc_t ;
/* Set this bit in arg_required to allow empty arguments */
# define REISERFS_OPT_ALLOWEMPTY 31
/* this struct is used in reiserfs_getopt() for describing the set of reiserfs
mount options */
typedef struct {
char * option_name ;
int arg_required ; /* 0 if argument is not required, not 0 otherwise */
const arg_desc_t * values ; /* list of values accepted by an option */
int setmask ; /* bitmask which is to set on mount_options bitmask when this
value is found , 0 is no bits are to be changed . */
int clrmask ; /* bitmask which is to clear on mount_options bitmask when this
value is found , 0 is no bits are to be changed . This is
applied BEFORE setmask */
} opt_desc_t ;
/* possible values for -o data= */
static const arg_desc_t logging_mode [ ] = {
{ " ordered " , 1 < < REISERFS_DATA_ORDERED , ( 1 < < REISERFS_DATA_LOG | 1 < < REISERFS_DATA_WRITEBACK ) } ,
{ " journal " , 1 < < REISERFS_DATA_LOG , ( 1 < < REISERFS_DATA_ORDERED | 1 < < REISERFS_DATA_WRITEBACK ) } ,
{ " writeback " , 1 < < REISERFS_DATA_WRITEBACK , ( 1 < < REISERFS_DATA_ORDERED | 1 < < REISERFS_DATA_LOG ) } ,
{ NULL , 0 }
} ;
/* possible values for -o barrier= */
static const arg_desc_t barrier_mode [ ] = {
{ " none " , 1 < < REISERFS_BARRIER_NONE , 1 < < REISERFS_BARRIER_FLUSH } ,
{ " flush " , 1 < < REISERFS_BARRIER_FLUSH , 1 < < REISERFS_BARRIER_NONE } ,
{ NULL , 0 }
} ;
/* possible values for "-o block-allocator=" and bits which are to be set in
s_mount_opt of reiserfs specific part of in - core super block */
static const arg_desc_t balloc [ ] = {
{ " noborder " , 1 < < REISERFS_NO_BORDER , 0 } ,
{ " border " , 0 , 1 < < REISERFS_NO_BORDER } ,
{ " no_unhashed_relocation " , 1 < < REISERFS_NO_UNHASHED_RELOCATION , 0 } ,
{ " hashed_relocation " , 1 < < REISERFS_HASHED_RELOCATION , 0 } ,
{ " test4 " , 1 < < REISERFS_TEST4 , 0 } ,
{ " notest4 " , 0 , 1 < < REISERFS_TEST4 } ,
{ NULL , 0 , 0 }
} ;
static const arg_desc_t tails [ ] = {
{ " on " , 1 < < REISERFS_LARGETAIL , 1 < < REISERFS_SMALLTAIL } ,
{ " off " , 0 , ( 1 < < REISERFS_LARGETAIL ) | ( 1 < < REISERFS_SMALLTAIL ) } ,
{ " small " , 1 < < REISERFS_SMALLTAIL , 1 < < REISERFS_LARGETAIL } ,
{ NULL , 0 , 0 }
} ;
static const arg_desc_t error_actions [ ] = {
{ " panic " , 1 < < REISERFS_ERROR_PANIC ,
( 1 < < REISERFS_ERROR_RO | 1 < < REISERFS_ERROR_CONTINUE ) } ,
{ " ro-remount " , 1 < < REISERFS_ERROR_RO ,
( 1 < < REISERFS_ERROR_PANIC | 1 < < REISERFS_ERROR_CONTINUE ) } ,
# ifdef REISERFS_JOURNAL_ERROR_ALLOWS_NO_LOG
{ " continue " , 1 < < REISERFS_ERROR_CONTINUE ,
( 1 < < REISERFS_ERROR_PANIC | 1 < < REISERFS_ERROR_RO ) } ,
# endif
{ NULL , 0 , 0 } ,
} ;
int reiserfs_default_io_size = 128 * 1024 ; /* Default recommended I/O size is 128k.
There might be broken applications that are
confused by this . Use nolargeio mount option
to get usual i / o size = PAGE_SIZE .
*/
/* proceed only one option from a list *cur - string containing of mount options
opts - array of options which are accepted
opt_arg - if option is found and requires an argument and if it is specifed
in the input - pointer to the argument is stored here
bit_flags - if option requires to set a certain bit - it is set here
return - 1 if unknown option is found , opt - > arg_required otherwise */
static int reiserfs_getopt ( struct super_block * s , char * * cur , opt_desc_t * opts , char * * opt_arg ,
unsigned long * bit_flags )
{
char * p ;
/* foo=bar,
^ ^ ^
| | + - - option_end
| + - - arg_start
+ - - option_start
*/
const opt_desc_t * opt ;
const arg_desc_t * arg ;
p = * cur ;
/* assume argument cannot contain commas */
* cur = strchr ( p , ' , ' ) ;
if ( * cur ) {
* ( * cur ) = ' \0 ' ;
( * cur ) + + ;
}
if ( ! strncmp ( p , " alloc= " , 6 ) ) {
/* Ugly special case, probably we should redo options parser so that
it can understand several arguments for some options , also so that
it can fill several bitfields with option values . */
if ( reiserfs_parse_alloc_options ( s , p + 6 ) ) {
return - 1 ;
} else {
return 0 ;
}
}
/* for every option in the list */
for ( opt = opts ; opt - > option_name ; opt + + ) {
if ( ! strncmp ( p , opt - > option_name , strlen ( opt - > option_name ) ) ) {
if ( bit_flags ) {
if ( opt - > clrmask = = ( 1 < < REISERFS_UNSUPPORTED_OPT ) )
reiserfs_warning ( s , " %s not supported. " , p ) ;
else
* bit_flags & = ~ opt - > clrmask ;
if ( opt - > setmask = = ( 1 < < REISERFS_UNSUPPORTED_OPT ) )
reiserfs_warning ( s , " %s not supported. " , p ) ;
else
* bit_flags | = opt - > setmask ;
}
break ;
}
}
if ( ! opt - > option_name ) {
reiserfs_warning ( s , " unknown mount option \" %s \" " , p ) ;
return - 1 ;
}
p + = strlen ( opt - > option_name ) ;
switch ( * p ) {
case ' = ' :
if ( ! opt - > arg_required ) {
reiserfs_warning ( s , " the option \" %s \" does not require an argument " ,
opt - > option_name ) ;
return - 1 ;
}
break ;
case 0 :
if ( opt - > arg_required ) {
reiserfs_warning ( s , " the option \" %s \" requires an argument " , opt - > option_name ) ;
return - 1 ;
}
break ;
default :
reiserfs_warning ( s , " head of option \" %s \" is only correct " , opt - > option_name ) ;
return - 1 ;
}
/* move to the argument, or to next option if argument is not required */
p + + ;
if ( opt - > arg_required & & ! ( opt - > arg_required & ( 1 < < REISERFS_OPT_ALLOWEMPTY ) ) & & ! strlen ( p ) ) {
/* this catches "option=," if not allowed */
reiserfs_warning ( s , " empty argument for \" %s \" " , opt - > option_name ) ;
return - 1 ;
}
if ( ! opt - > values ) {
/* *=NULLopt_arg contains pointer to argument */
* opt_arg = p ;
return opt - > arg_required & ~ ( 1 < < REISERFS_OPT_ALLOWEMPTY ) ;
}
/* values possible for this option are listed in opt->values */
for ( arg = opt - > values ; arg - > value ; arg + + ) {
if ( ! strcmp ( p , arg - > value ) ) {
if ( bit_flags ) {
* bit_flags & = ~ arg - > clrmask ;
* bit_flags | = arg - > setmask ;
}
return opt - > arg_required ;
}
}
reiserfs_warning ( s , " bad value \" %s \" for option \" %s \" " , p , opt - > option_name ) ;
return - 1 ;
}
/* returns 0 if something is wrong in option string, 1 - otherwise */
static int reiserfs_parse_options ( struct super_block * s , char * options , /* string given via mount's -o */
unsigned long * mount_options ,
/* after the parsing phase, contains the
collection of bitflags defining what
mount options were selected . */
unsigned long * blocks , /* strtol-ed from NNN of resize=NNN */
char * * jdev_name ,
unsigned int * commit_max_age )
{
int c ;
char * arg = NULL ;
char * pos ;
opt_desc_t opts [ ] = {
/* Compatibility stuff, so that -o notail for old setups still work */
{ " tails " , . arg_required = ' t ' , . values = tails } ,
{ " notail " , . clrmask = ( 1 < < REISERFS_LARGETAIL ) | ( 1 < < REISERFS_SMALLTAIL ) } ,
{ " conv " , . setmask = 1 < < REISERFS_CONVERT } ,
{ " attrs " , . setmask = 1 < < REISERFS_ATTRS } ,
{ " noattrs " , . clrmask = 1 < < REISERFS_ATTRS } ,
# ifdef CONFIG_REISERFS_FS_XATTR
{ " user_xattr " , . setmask = 1 < < REISERFS_XATTRS_USER } ,
{ " nouser_xattr " , . clrmask = 1 < < REISERFS_XATTRS_USER } ,
# else
{ " user_xattr " , . setmask = 1 < < REISERFS_UNSUPPORTED_OPT } ,
{ " nouser_xattr " , . clrmask = 1 < < REISERFS_UNSUPPORTED_OPT } ,
# endif
# ifdef CONFIG_REISERFS_FS_POSIX_ACL
{ " acl " , . setmask = 1 < < REISERFS_POSIXACL } ,
{ " noacl " , . clrmask = 1 < < REISERFS_POSIXACL } ,
# else
{ " acl " , . setmask = 1 < < REISERFS_UNSUPPORTED_OPT } ,
{ " noacl " , . clrmask = 1 < < REISERFS_UNSUPPORTED_OPT } ,
# endif
{ " nolog " , } , /* This is unsupported */
{ " replayonly " , . setmask = 1 < < REPLAYONLY } ,
{ " block-allocator " , . arg_required = ' a ' , . values = balloc } ,
{ " data " , . arg_required = ' d ' , . values = logging_mode } ,
{ " barrier " , . arg_required = ' b ' , . values = barrier_mode } ,
{ " resize " , . arg_required = ' r ' , . values = NULL } ,
{ " jdev " , . arg_required = ' j ' , . values = NULL } ,
{ " nolargeio " , . arg_required = ' w ' , . values = NULL } ,
{ " commit " , . arg_required = ' c ' , . values = NULL } ,
{ " usrquota " , } ,
{ " grpquota " , } ,
{ " errors " , . arg_required = ' e ' , . values = error_actions } ,
{ " usrjquota " , . arg_required = ' u ' | ( 1 < < REISERFS_OPT_ALLOWEMPTY ) , . values = NULL } ,
{ " grpjquota " , . arg_required = ' g ' | ( 1 < < REISERFS_OPT_ALLOWEMPTY ) , . values = NULL } ,
{ " jqfmt " , . arg_required = ' f ' , . values = NULL } ,
{ NULL , }
} ;
* blocks = 0 ;
if ( ! options | | ! * options )
/* use default configuration: create tails, journaling on, no
conversion to newest format */
return 1 ;
for ( pos = options ; pos ; ) {
c = reiserfs_getopt ( s , & pos , opts , & arg , mount_options ) ;
if ( c = = - 1 )
/* wrong option is given */
return 0 ;
if ( c = = ' r ' ) {
char * p ;
p = NULL ;
2005-05-01 19:59:05 +04:00
/* "resize=NNN" or "resize=auto" */
if ( ! strcmp ( arg , " auto " ) ) {
/* From JFS code, to auto-get the size.*/
* blocks = s - > s_bdev - > bd_inode - > i_size > > s - > s_blocksize_bits ;
} else {
* blocks = simple_strtoul ( arg , & p , 0 ) ;
if ( * p ! = ' \0 ' ) {
/* NNN does not look like a number */
reiserfs_warning ( s , " reiserfs_parse_options: bad value %s " , arg ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
}
}
if ( c = = ' c ' ) {
char * p = NULL ;
unsigned long val = simple_strtoul ( arg , & p , 0 ) ;
/* commit=NNN (time in seconds) */
if ( * p ! = ' \0 ' | | val > = ( unsigned int ) - 1 ) {
2005-05-01 19:59:05 +04:00
reiserfs_warning ( s , " reiserfs_parse_options: bad value %s " , arg ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
* commit_max_age = ( unsigned int ) val ;
}
if ( c = = ' w ' ) {
char * p = NULL ;
int val = simple_strtoul ( arg , & p , 0 ) ;
if ( * p ! = ' \0 ' ) {
reiserfs_warning ( s , " reiserfs_parse_options: non-numeric value %s for nolargeio option " , arg ) ;
return 0 ;
}
if ( val )
reiserfs_default_io_size = PAGE_SIZE ;
else
reiserfs_default_io_size = 128 * 1024 ;
}
if ( c = = ' j ' ) {
if ( arg & & * arg & & jdev_name ) {
if ( * jdev_name ) { //Hm, already assigned?
reiserfs_warning ( s , " reiserfs_parse_options: journal device was already specified to be %s " , * jdev_name ) ;
return 0 ;
}
* jdev_name = arg ;
}
}
# ifdef CONFIG_QUOTA
if ( c = = ' u ' | | c = = ' g ' ) {
int qtype = c = = ' u ' ? USRQUOTA : GRPQUOTA ;
if ( sb_any_quota_enabled ( s ) ) {
reiserfs_warning ( s , " reiserfs_parse_options: cannot change journalled quota options when quota turned on. " ) ;
return 0 ;
}
if ( * arg ) { /* Some filename specified? */
if ( REISERFS_SB ( s ) - > s_qf_names [ qtype ] & & strcmp ( REISERFS_SB ( s ) - > s_qf_names [ qtype ] , arg ) ) {
reiserfs_warning ( s , " reiserfs_parse_options: %s quota file already specified. " , QTYPE2NAME ( qtype ) ) ;
return 0 ;
}
if ( strchr ( arg , ' / ' ) ) {
reiserfs_warning ( s , " reiserfs_parse_options: quotafile must be on filesystem root. " ) ;
return 0 ;
}
REISERFS_SB ( s ) - > s_qf_names [ qtype ] = kmalloc ( strlen ( arg ) + 1 , GFP_KERNEL ) ;
if ( ! REISERFS_SB ( s ) - > s_qf_names [ qtype ] ) {
reiserfs_warning ( s , " reiserfs_parse_options: not enough memory for storing quotafile name. " ) ;
return 0 ;
}
strcpy ( REISERFS_SB ( s ) - > s_qf_names [ qtype ] , arg ) ;
}
else {
if ( REISERFS_SB ( s ) - > s_qf_names [ qtype ] ) {
kfree ( REISERFS_SB ( s ) - > s_qf_names [ qtype ] ) ;
REISERFS_SB ( s ) - > s_qf_names [ qtype ] = NULL ;
}
}
}
if ( c = = ' f ' ) {
if ( ! strcmp ( arg , " vfsold " ) )
REISERFS_SB ( s ) - > s_jquota_fmt = QFMT_VFS_OLD ;
else if ( ! strcmp ( arg , " vfsv0 " ) )
REISERFS_SB ( s ) - > s_jquota_fmt = QFMT_VFS_V0 ;
else {
reiserfs_warning ( s , " reiserfs_parse_options: unknown quota format specified. " ) ;
return 0 ;
}
}
# else
if ( c = = ' u ' | | c = = ' g ' | | c = = ' f ' ) {
reiserfs_warning ( s , " reiserfs_parse_options: journalled quota options not supported. " ) ;
return 0 ;
}
# endif
}
# ifdef CONFIG_QUOTA
if ( ! REISERFS_SB ( s ) - > s_jquota_fmt & & ( REISERFS_SB ( s ) - > s_qf_names [ USRQUOTA ] | | REISERFS_SB ( s ) - > s_qf_names [ GRPQUOTA ] ) ) {
reiserfs_warning ( s , " reiserfs_parse_options: journalled quota format not specified. " ) ;
return 0 ;
}
# endif
return 1 ;
}
static void switch_data_mode ( struct super_block * s , unsigned long mode ) {
REISERFS_SB ( s ) - > s_mount_opt & = ~ ( ( 1 < < REISERFS_DATA_LOG ) |
( 1 < < REISERFS_DATA_ORDERED ) |
( 1 < < REISERFS_DATA_WRITEBACK ) ) ;
REISERFS_SB ( s ) - > s_mount_opt | = ( 1 < < mode ) ;
}
static void handle_data_mode ( struct super_block * s , unsigned long mount_options )
{
if ( mount_options & ( 1 < < REISERFS_DATA_LOG ) ) {
if ( ! reiserfs_data_log ( s ) ) {
switch_data_mode ( s , REISERFS_DATA_LOG ) ;
reiserfs_info ( s , " switching to journaled data mode \n " ) ;
}
} else if ( mount_options & ( 1 < < REISERFS_DATA_ORDERED ) ) {
if ( ! reiserfs_data_ordered ( s ) ) {
switch_data_mode ( s , REISERFS_DATA_ORDERED ) ;
reiserfs_info ( s , " switching to ordered data mode \n " ) ;
}
} else if ( mount_options & ( 1 < < REISERFS_DATA_WRITEBACK ) ) {
if ( ! reiserfs_data_writeback ( s ) ) {
switch_data_mode ( s , REISERFS_DATA_WRITEBACK ) ;
reiserfs_info ( s , " switching to writeback data mode \n " ) ;
}
}
}
static void handle_barrier_mode ( struct super_block * s , unsigned long bits ) {
int flush = ( 1 < < REISERFS_BARRIER_FLUSH ) ;
int none = ( 1 < < REISERFS_BARRIER_NONE ) ;
int all_barrier = flush | none ;
if ( bits & all_barrier ) {
REISERFS_SB ( s ) - > s_mount_opt & = ~ all_barrier ;
if ( bits & flush ) {
REISERFS_SB ( s ) - > s_mount_opt | = flush ;
printk ( " reiserfs: enabling write barrier flush mode \n " ) ;
} else if ( bits & none ) {
REISERFS_SB ( s ) - > s_mount_opt | = none ;
printk ( " reiserfs: write barriers turned off \n " ) ;
}
}
}
static void handle_attrs ( struct super_block * s )
{
struct reiserfs_super_block * rs ;
if ( reiserfs_attrs ( s ) ) {
rs = SB_DISK_SUPER_BLOCK ( s ) ;
if ( old_format_only ( s ) ) {
reiserfs_warning ( s , " reiserfs: cannot support attributes on 3.5.x disk format " ) ;
REISERFS_SB ( s ) - > s_mount_opt & = ~ ( 1 < < REISERFS_ATTRS ) ;
return ;
}
if ( ! ( le32_to_cpu ( rs - > s_flags ) & reiserfs_attrs_cleared ) ) {
reiserfs_warning ( s , " reiserfs: cannot support attributes until flag is set in super-block " ) ;
REISERFS_SB ( s ) - > s_mount_opt & = ~ ( 1 < < REISERFS_ATTRS ) ;
}
}
}
static int reiserfs_remount ( struct super_block * s , int * mount_flags , char * arg )
{
struct reiserfs_super_block * rs ;
struct reiserfs_transaction_handle th ;
unsigned long blocks ;
unsigned long mount_options = REISERFS_SB ( s ) - > s_mount_opt ;
unsigned long safe_mask = 0 ;
unsigned int commit_max_age = ( unsigned int ) - 1 ;
struct reiserfs_journal * journal = SB_JOURNAL ( s ) ;
int err ;
# ifdef CONFIG_QUOTA
int i ;
# endif
rs = SB_DISK_SUPER_BLOCK ( s ) ;
if ( ! reiserfs_parse_options ( s , arg , & mount_options , & blocks , NULL , & commit_max_age ) ) {
# ifdef CONFIG_QUOTA
for ( i = 0 ; i < MAXQUOTAS ; i + + )
if ( REISERFS_SB ( s ) - > s_qf_names [ i ] ) {
kfree ( REISERFS_SB ( s ) - > s_qf_names [ i ] ) ;
REISERFS_SB ( s ) - > s_qf_names [ i ] = NULL ;
}
# endif
return - EINVAL ;
}
handle_attrs ( s ) ;
/* Add options that are safe here */
safe_mask | = 1 < < REISERFS_SMALLTAIL ;
safe_mask | = 1 < < REISERFS_LARGETAIL ;
safe_mask | = 1 < < REISERFS_NO_BORDER ;
safe_mask | = 1 < < REISERFS_NO_UNHASHED_RELOCATION ;
safe_mask | = 1 < < REISERFS_HASHED_RELOCATION ;
safe_mask | = 1 < < REISERFS_TEST4 ;
safe_mask | = 1 < < REISERFS_ATTRS ;
safe_mask | = 1 < < REISERFS_XATTRS_USER ;
safe_mask | = 1 < < REISERFS_POSIXACL ;
safe_mask | = 1 < < REISERFS_BARRIER_FLUSH ;
safe_mask | = 1 < < REISERFS_BARRIER_NONE ;
safe_mask | = 1 < < REISERFS_ERROR_RO ;
safe_mask | = 1 < < REISERFS_ERROR_CONTINUE ;
safe_mask | = 1 < < REISERFS_ERROR_PANIC ;
/* Update the bitmask, taking care to keep
* the bits we ' re not allowed to change here */
REISERFS_SB ( s ) - > s_mount_opt = ( REISERFS_SB ( s ) - > s_mount_opt & ~ safe_mask ) | ( mount_options & safe_mask ) ;
if ( commit_max_age ! = 0 & & commit_max_age ! = ( unsigned int ) - 1 ) {
journal - > j_max_commit_age = commit_max_age ;
journal - > j_max_trans_age = commit_max_age ;
}
else if ( commit_max_age = = 0 )
{
/* 0 means restore defaults. */
journal - > j_max_commit_age = journal - > j_default_max_commit_age ;
journal - > j_max_trans_age = JOURNAL_MAX_TRANS_AGE ;
}
if ( blocks ) {
int rc = reiserfs_resize ( s , blocks ) ;
if ( rc ! = 0 )
return rc ;
}
if ( * mount_flags & MS_RDONLY ) {
reiserfs_xattr_init ( s , * mount_flags ) ;
/* remount read-only */
if ( s - > s_flags & MS_RDONLY )
/* it is read-only already */
return 0 ;
/* try to remount file system with read-only permissions */
if ( sb_umount_state ( rs ) = = REISERFS_VALID_FS | | REISERFS_SB ( s ) - > s_mount_state ! = REISERFS_VALID_FS ) {
return 0 ;
}
err = journal_begin ( & th , s , 10 ) ;
if ( err )
return err ;
/* Mounting a rw partition read-only. */
reiserfs_prepare_for_journal ( s , SB_BUFFER_WITH_SB ( s ) , 1 ) ;
set_sb_umount_state ( rs , REISERFS_SB ( s ) - > s_mount_state ) ;
journal_mark_dirty ( & th , s , SB_BUFFER_WITH_SB ( s ) ) ;
} else {
/* remount read-write */
if ( ! ( s - > s_flags & MS_RDONLY ) ) {
reiserfs_xattr_init ( s , * mount_flags ) ;
return 0 ; /* We are read-write already */
}
if ( reiserfs_is_journal_aborted ( journal ) )
return journal - > j_errno ;
handle_data_mode ( s , mount_options ) ;
handle_barrier_mode ( s , mount_options ) ;
REISERFS_SB ( s ) - > s_mount_state = sb_umount_state ( rs ) ;
s - > s_flags & = ~ MS_RDONLY ; /* now it is safe to call journal_begin */
err = journal_begin ( & th , s , 10 ) ;
if ( err )
return err ;
/* Mount a partition which is read-only, read-write */
reiserfs_prepare_for_journal ( s , SB_BUFFER_WITH_SB ( s ) , 1 ) ;
REISERFS_SB ( s ) - > s_mount_state = sb_umount_state ( rs ) ;
s - > s_flags & = ~ MS_RDONLY ;
set_sb_umount_state ( rs , REISERFS_ERROR_FS ) ;
/* mark_buffer_dirty (SB_BUFFER_WITH_SB (s), 1); */
journal_mark_dirty ( & th , s , SB_BUFFER_WITH_SB ( s ) ) ;
REISERFS_SB ( s ) - > s_mount_state = REISERFS_VALID_FS ;
}
/* this will force a full flush of all journal lists */
SB_JOURNAL ( s ) - > j_must_wait = 1 ;
err = journal_end ( & th , s , 10 ) ;
if ( err )
return err ;
s - > s_dirt = 0 ;
if ( ! ( * mount_flags & MS_RDONLY ) ) {
finish_unfinished ( s ) ;
reiserfs_xattr_init ( s , * mount_flags ) ;
}
return 0 ;
}
/* load_bitmap_info_data - Sets up the reiserfs_bitmap_info structure from disk.
* @ sb - superblock for this filesystem
* @ bi - the bitmap info to be loaded . Requires that bi - > bh is valid .
*
* This routine counts how many free bits there are , finding the first zero
* as a side effect . Could also be implemented as a loop of test_bit ( ) calls , or
* a loop of find_first_zero_bit ( ) calls . This implementation is similar to
* find_first_zero_bit ( ) , but doesn ' t return after it finds the first bit .
* Should only be called on fs mount , but should be fairly efficient anyways .
*
* bi - > first_zero_hint is considered unset if it = = 0 , since the bitmap itself
* will * invariably occupt block 0 represented in the bitmap . The only
* exception to this is when free_count also = = 0 , since there will be no
* free blocks at all .
*/
static void load_bitmap_info_data ( struct super_block * sb ,
struct reiserfs_bitmap_info * bi )
{
unsigned long * cur = ( unsigned long * ) bi - > bh - > b_data ;
while ( ( char * ) cur < ( bi - > bh - > b_data + sb - > s_blocksize ) ) {
/* No need to scan if all 0's or all 1's.
* Since we ' re only counting 0 ' s , we can simply ignore all 1 ' s */
if ( * cur = = 0 ) {
if ( bi - > first_zero_hint = = 0 ) {
bi - > first_zero_hint = ( ( char * ) cur - bi - > bh - > b_data ) < < 3 ;
}
bi - > free_count + = sizeof ( unsigned long ) * 8 ;
} else if ( * cur ! = ~ 0L ) {
int b ;
for ( b = 0 ; b < sizeof ( unsigned long ) * 8 ; b + + ) {
if ( ! reiserfs_test_le_bit ( b , cur ) ) {
bi - > free_count + + ;
if ( bi - > first_zero_hint = = 0 )
bi - > first_zero_hint =
( ( ( char * ) cur - bi - > bh - > b_data ) < < 3 ) + b ;
}
}
}
cur + + ;
}
# ifdef CONFIG_REISERFS_CHECK
// This outputs a lot of unneded info on big FSes
// reiserfs_warning ("bitmap loaded from block %d: %d free blocks",
// bi->bh->b_blocknr, bi->free_count);
# endif
}
static int read_bitmaps ( struct super_block * s )
{
int i , bmap_nr ;
SB_AP_BITMAP ( s ) = vmalloc ( sizeof ( struct reiserfs_bitmap_info ) * SB_BMAP_NR ( s ) ) ;
if ( SB_AP_BITMAP ( s ) = = 0 )
return 1 ;
memset ( SB_AP_BITMAP ( s ) , 0 , sizeof ( struct reiserfs_bitmap_info ) * SB_BMAP_NR ( s ) ) ;
for ( i = 0 , bmap_nr = REISERFS_DISK_OFFSET_IN_BYTES / s - > s_blocksize + 1 ;
i < SB_BMAP_NR ( s ) ; i + + , bmap_nr = s - > s_blocksize * 8 * i ) {
SB_AP_BITMAP ( s ) [ i ] . bh = sb_getblk ( s , bmap_nr ) ;
if ( ! buffer_uptodate ( SB_AP_BITMAP ( s ) [ i ] . bh ) )
ll_rw_block ( READ , 1 , & SB_AP_BITMAP ( s ) [ i ] . bh ) ;
}
for ( i = 0 ; i < SB_BMAP_NR ( s ) ; i + + ) {
wait_on_buffer ( SB_AP_BITMAP ( s ) [ i ] . bh ) ;
if ( ! buffer_uptodate ( SB_AP_BITMAP ( s ) [ i ] . bh ) ) {
reiserfs_warning ( s , " sh-2029: reiserfs read_bitmaps: "
" bitmap block (#%lu) reading failed " ,
SB_AP_BITMAP ( s ) [ i ] . bh - > b_blocknr ) ;
for ( i = 0 ; i < SB_BMAP_NR ( s ) ; i + + )
brelse ( SB_AP_BITMAP ( s ) [ i ] . bh ) ;
vfree ( SB_AP_BITMAP ( s ) ) ;
SB_AP_BITMAP ( s ) = NULL ;
return 1 ;
}
load_bitmap_info_data ( s , SB_AP_BITMAP ( s ) + i ) ;
}
return 0 ;
}
static int read_old_bitmaps ( struct super_block * s )
{
int i ;
struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK ( s ) ;
int bmp1 = ( REISERFS_OLD_DISK_OFFSET_IN_BYTES / s - > s_blocksize ) + 1 ; /* first of bitmap blocks */
/* read true bitmap */
SB_AP_BITMAP ( s ) = vmalloc ( sizeof ( struct reiserfs_buffer_info * ) * sb_bmap_nr ( rs ) ) ;
if ( SB_AP_BITMAP ( s ) = = 0 )
return 1 ;
memset ( SB_AP_BITMAP ( s ) , 0 , sizeof ( struct reiserfs_buffer_info * ) * sb_bmap_nr ( rs ) ) ;
for ( i = 0 ; i < sb_bmap_nr ( rs ) ; i + + ) {
SB_AP_BITMAP ( s ) [ i ] . bh = sb_bread ( s , bmp1 + i ) ;
if ( ! SB_AP_BITMAP ( s ) [ i ] . bh )
return 1 ;
load_bitmap_info_data ( s , SB_AP_BITMAP ( s ) + i ) ;
}
return 0 ;
}
static int read_super_block ( struct super_block * s , int offset )
{
struct buffer_head * bh ;
struct reiserfs_super_block * rs ;
int fs_blocksize ;
bh = sb_bread ( s , offset / s - > s_blocksize ) ;
if ( ! bh ) {
reiserfs_warning ( s , " sh-2006: read_super_block: "
" bread failed (dev %s, block %lu, size %lu) " ,
reiserfs_bdevname ( s ) , offset / s - > s_blocksize , s - > s_blocksize ) ;
return 1 ;
}
rs = ( struct reiserfs_super_block * ) bh - > b_data ;
if ( ! is_any_reiserfs_magic_string ( rs ) ) {
brelse ( bh ) ;
return 1 ;
}
//
// ok, reiserfs signature (old or new) found in at the given offset
//
fs_blocksize = sb_blocksize ( rs ) ;
brelse ( bh ) ;
sb_set_blocksize ( s , fs_blocksize ) ;
bh = sb_bread ( s , offset / s - > s_blocksize ) ;
if ( ! bh ) {
reiserfs_warning ( s , " sh-2007: read_super_block: "
" bread failed (dev %s, block %lu, size %lu) \n " ,
reiserfs_bdevname ( s ) , offset / s - > s_blocksize , s - > s_blocksize ) ;
return 1 ;
}
rs = ( struct reiserfs_super_block * ) bh - > b_data ;
if ( sb_blocksize ( rs ) ! = s - > s_blocksize ) {
reiserfs_warning ( s , " sh-2011: read_super_block: "
" can't find a reiserfs filesystem on (dev %s, block %Lu, size %lu) \n " ,
reiserfs_bdevname ( s ) , ( unsigned long long ) bh - > b_blocknr , s - > s_blocksize ) ;
brelse ( bh ) ;
return 1 ;
}
2005-05-01 19:59:18 +04:00
if ( rs - > s_v1 . s_root_block = = cpu_to_le32 ( - 1 ) ) {
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
reiserfs_warning ( s , " Unfinished reiserfsck --rebuild-tree run detected. Please run \n "
" reiserfsck --rebuild-tree and wait for a completion. If that fails \n "
" get newer reiserfsprogs package " ) ;
return 1 ;
}
SB_BUFFER_WITH_SB ( s ) = bh ;
SB_DISK_SUPER_BLOCK ( s ) = rs ;
if ( is_reiserfs_jr ( rs ) ) {
/* magic is of non-standard journal filesystem, look at s_version to
find which format is in use */
if ( sb_version ( rs ) = = REISERFS_VERSION_2 )
reiserfs_warning ( s , " read_super_block: found reiserfs format \" 3.6 \" "
" with non-standard journal " ) ;
else if ( sb_version ( rs ) = = REISERFS_VERSION_1 )
reiserfs_warning ( s , " read_super_block: found reiserfs format \" 3.5 \" "
" with non-standard journal " ) ;
else {
reiserfs_warning ( s , " sh-2012: read_super_block: found unknown "
" format \" %u \" of reiserfs with non-standard magic " ,
sb_version ( rs ) ) ;
return 1 ;
}
}
else
/* s_version of standard format may contain incorrect information,
so we just look at the magic string */
reiserfs_info ( s , " found reiserfs format \" %s \" with standard journal \n " ,
is_reiserfs_3_5 ( rs ) ? " 3.5 " : " 3.6 " ) ;
s - > s_op = & reiserfs_sops ;
s - > s_export_op = & reiserfs_export_ops ;
# ifdef CONFIG_QUOTA
s - > s_qcop = & reiserfs_qctl_operations ;
s - > dq_op = & reiserfs_quota_operations ;
# endif
/* new format is limited by the 32 bit wide i_blocks field, want to
* * be one full block below that .
*/
s - > s_maxbytes = ( 512LL < < 32 ) - s - > s_blocksize ;
return 0 ;
}
/* after journal replay, reread all bitmap and super blocks */
static int reread_meta_blocks ( struct super_block * s ) {
int i ;
ll_rw_block ( READ , 1 , & ( SB_BUFFER_WITH_SB ( s ) ) ) ;
wait_on_buffer ( SB_BUFFER_WITH_SB ( s ) ) ;
if ( ! buffer_uptodate ( SB_BUFFER_WITH_SB ( s ) ) ) {
reiserfs_warning ( s , " reread_meta_blocks, error reading the super " ) ;
return 1 ;
}
for ( i = 0 ; i < SB_BMAP_NR ( s ) ; i + + ) {
ll_rw_block ( READ , 1 , & ( SB_AP_BITMAP ( s ) [ i ] . bh ) ) ;
wait_on_buffer ( SB_AP_BITMAP ( s ) [ i ] . bh ) ;
if ( ! buffer_uptodate ( SB_AP_BITMAP ( s ) [ i ] . bh ) ) {
reiserfs_warning ( s , " reread_meta_blocks, error reading bitmap block number %d at %llu " ,
i , ( unsigned long long ) SB_AP_BITMAP ( s ) [ i ] . bh - > b_blocknr ) ;
return 1 ;
}
}
return 0 ;
}
/////////////////////////////////////////////////////
// hash detection stuff
// if root directory is empty - we set default - Yura's - hash and
// warn about it
// FIXME: we look for only one name in a directory. If tea and yura
// bith have the same value - we ask user to send report to the
// mailing list
static __u32 find_hash_out ( struct super_block * s )
{
int retval ;
struct inode * inode ;
struct cpu_key key ;
INITIALIZE_PATH ( path ) ;
struct reiserfs_dir_entry de ;
__u32 hash = DEFAULT_HASH ;
inode = s - > s_root - > d_inode ;
do { // Some serious "goto"-hater was there ;)
u32 teahash , r5hash , yurahash ;
make_cpu_key ( & key , inode , ~ 0 , TYPE_DIRENTRY , 3 ) ;
retval = search_by_entry_key ( s , & key , & path , & de ) ;
if ( retval = = IO_ERROR ) {
pathrelse ( & path ) ;
return UNSET_HASH ;
}
if ( retval = = NAME_NOT_FOUND )
de . de_entry_num - - ;
set_de_name_and_namelen ( & de ) ;
if ( deh_offset ( & ( de . de_deh [ de . de_entry_num ] ) ) = = DOT_DOT_OFFSET ) {
/* allow override in this case */
if ( reiserfs_rupasov_hash ( s ) ) {
hash = YURA_HASH ;
}
reiserfs_warning ( s , " FS seems to be empty, autodetect "
" is using the default hash " ) ;
break ;
}
r5hash = GET_HASH_VALUE ( r5_hash ( de . de_name , de . de_namelen ) ) ;
teahash = GET_HASH_VALUE ( keyed_hash ( de . de_name , de . de_namelen ) ) ;
yurahash = GET_HASH_VALUE ( yura_hash ( de . de_name , de . de_namelen ) ) ;
if ( ( ( teahash = = r5hash ) & & ( GET_HASH_VALUE ( deh_offset ( & ( de . de_deh [ de . de_entry_num ] ) ) ) = = r5hash ) ) | |
( ( teahash = = yurahash ) & & ( yurahash = = GET_HASH_VALUE ( deh_offset ( & ( de . de_deh [ de . de_entry_num ] ) ) ) ) ) | |
( ( r5hash = = yurahash ) & & ( yurahash = = GET_HASH_VALUE ( deh_offset ( & ( de . de_deh [ de . de_entry_num ] ) ) ) ) ) ) {
reiserfs_warning ( s , " Unable to automatically detect hash function. "
" Please mount with -o hash={tea,rupasov,r5} " ,
reiserfs_bdevname ( s ) ) ;
hash = UNSET_HASH ;
break ;
}
if ( GET_HASH_VALUE ( deh_offset ( & ( de . de_deh [ de . de_entry_num ] ) ) ) = = yurahash )
hash = YURA_HASH ;
else if ( GET_HASH_VALUE ( deh_offset ( & ( de . de_deh [ de . de_entry_num ] ) ) ) = = teahash )
hash = TEA_HASH ;
else if ( GET_HASH_VALUE ( deh_offset ( & ( de . de_deh [ de . de_entry_num ] ) ) ) = = r5hash )
hash = R5_HASH ;
else {
reiserfs_warning ( s , " Unrecognised hash function " ) ;
hash = UNSET_HASH ;
}
} while ( 0 ) ;
pathrelse ( & path ) ;
return hash ;
}
// finds out which hash names are sorted with
static int what_hash ( struct super_block * s )
{
__u32 code ;
code = sb_hash_function_code ( SB_DISK_SUPER_BLOCK ( s ) ) ;
/* reiserfs_hash_detect() == true if any of the hash mount options
* * were used . We must check them to make sure the user isn ' t
* * using a bad hash value
*/
if ( code = = UNSET_HASH | | reiserfs_hash_detect ( s ) )
code = find_hash_out ( s ) ;
if ( code ! = UNSET_HASH & & reiserfs_hash_detect ( s ) ) {
/* detection has found the hash, and we must check against the
* * mount options
*/
if ( reiserfs_rupasov_hash ( s ) & & code ! = YURA_HASH ) {
reiserfs_warning ( s , " Error, %s hash detected, "
" unable to force rupasov hash " , reiserfs_hashname ( code ) ) ;
code = UNSET_HASH ;
} else if ( reiserfs_tea_hash ( s ) & & code ! = TEA_HASH ) {
reiserfs_warning ( s , " Error, %s hash detected, "
" unable to force tea hash " , reiserfs_hashname ( code ) ) ;
code = UNSET_HASH ;
} else if ( reiserfs_r5_hash ( s ) & & code ! = R5_HASH ) {
reiserfs_warning ( s , " Error, %s hash detected, "
" unable to force r5 hash " , reiserfs_hashname ( code ) ) ;
code = UNSET_HASH ;
}
} else {
/* find_hash_out was not called or could not determine the hash */
if ( reiserfs_rupasov_hash ( s ) ) {
code = YURA_HASH ;
} else if ( reiserfs_tea_hash ( s ) ) {
code = TEA_HASH ;
} else if ( reiserfs_r5_hash ( s ) ) {
code = R5_HASH ;
}
}
/* if we are mounted RW, and we have a new valid hash code, update
* * the super
*/
if ( code ! = UNSET_HASH & &
! ( s - > s_flags & MS_RDONLY ) & &
code ! = sb_hash_function_code ( SB_DISK_SUPER_BLOCK ( s ) ) ) {
set_sb_hash_function_code ( SB_DISK_SUPER_BLOCK ( s ) , code ) ;
}
return code ;
}
// return pointer to appropriate function
static hashf_t hash_function ( struct super_block * s )
{
switch ( what_hash ( s ) ) {
case TEA_HASH :
reiserfs_info ( s , " Using tea hash to sort names \n " ) ;
return keyed_hash ;
case YURA_HASH :
reiserfs_info ( s , " Using rupasov hash to sort names \n " ) ;
return yura_hash ;
case R5_HASH :
reiserfs_info ( s , " Using r5 hash to sort names \n " ) ;
return r5_hash ;
}
return NULL ;
}
// this is used to set up correct value for old partitions
static int function2code ( hashf_t func )
{
if ( func = = keyed_hash )
return TEA_HASH ;
if ( func = = yura_hash )
return YURA_HASH ;
if ( func = = r5_hash )
return R5_HASH ;
BUG ( ) ; // should never happen
return 0 ;
}
# define SWARN(silent, s, ...) \
if ( ! ( silent ) ) \
reiserfs_warning ( s , __VA_ARGS__ )
static int reiserfs_fill_super ( struct super_block * s , void * data , int silent )
{
struct inode * root_inode ;
int j ;
struct reiserfs_transaction_handle th ;
int old_format = 0 ;
unsigned long blocks ;
unsigned int commit_max_age = 0 ;
int jinit_done = 0 ;
struct reiserfs_iget_args args ;
struct reiserfs_super_block * rs ;
char * jdev_name ;
struct reiserfs_sb_info * sbi ;
int errval = - EINVAL ;
sbi = kmalloc ( sizeof ( struct reiserfs_sb_info ) , GFP_KERNEL ) ;
if ( ! sbi ) {
errval = - ENOMEM ;
goto error ;
}
s - > s_fs_info = sbi ;
memset ( sbi , 0 , sizeof ( struct reiserfs_sb_info ) ) ;
/* Set default values for options: non-aggressive tails, RO on errors */
REISERFS_SB ( s ) - > s_mount_opt | = ( 1 < < REISERFS_SMALLTAIL ) ;
REISERFS_SB ( s ) - > s_mount_opt | = ( 1 < < REISERFS_ERROR_RO ) ;
/* no preallocation minimum, be smart in
reiserfs_file_write instead */
REISERFS_SB ( s ) - > s_alloc_options . preallocmin = 0 ;
/* Preallocate by 16 blocks (17-1) at once */
REISERFS_SB ( s ) - > s_alloc_options . preallocsize = 17 ;
/* Initialize the rwsem for xattr dir */
init_rwsem ( & REISERFS_SB ( s ) - > xattr_dir_sem ) ;
/* setup default block allocator options */
reiserfs_init_alloc_options ( s ) ;
jdev_name = NULL ;
if ( reiserfs_parse_options ( s , ( char * ) data , & ( sbi - > s_mount_opt ) , & blocks , & jdev_name , & commit_max_age ) = = 0 ) {
goto error ;
}
if ( blocks ) {
SWARN ( silent , s , " jmacd-7: reiserfs_fill_super: resize option "
" for remount only " ) ;
goto error ;
}
/* try old format (undistributed bitmap, super block in 8-th 1k block of a device) */
if ( ! read_super_block ( s , REISERFS_OLD_DISK_OFFSET_IN_BYTES ) )
old_format = 1 ;
/* try new format (64-th 1k block), which can contain reiserfs super block */
else if ( read_super_block ( s , REISERFS_DISK_OFFSET_IN_BYTES ) ) {
SWARN ( silent , s , " sh-2021: reiserfs_fill_super: can not find reiserfs on %s " , reiserfs_bdevname ( s ) ) ;
goto error ;
}
rs = SB_DISK_SUPER_BLOCK ( s ) ;
/* Let's do basic sanity check to verify that underlying device is not
smaller than the filesystem . If the check fails then abort and scream ,
because bad stuff will happen otherwise . */
if ( s - > s_bdev & & s - > s_bdev - > bd_inode & & i_size_read ( s - > s_bdev - > bd_inode ) < sb_block_count ( rs ) * sb_blocksize ( rs ) ) {
SWARN ( silent , s , " Filesystem on %s cannot be mounted because it is bigger than the device " , reiserfs_bdevname ( s ) ) ;
SWARN ( silent , s , " You may need to run fsck or increase size of your LVM partition " ) ;
SWARN ( silent , s , " Or may be you forgot to reboot after fdisk when it told you to " ) ;
goto error ;
}
sbi - > s_mount_state = SB_REISERFS_STATE ( s ) ;
sbi - > s_mount_state = REISERFS_VALID_FS ;
if ( old_format ? read_old_bitmaps ( s ) : read_bitmaps ( s ) ) {
SWARN ( silent , s , " jmacd-8: reiserfs_fill_super: unable to read bitmap " ) ;
goto error ;
}
# ifdef CONFIG_REISERFS_CHECK
SWARN ( silent , s , " CONFIG_REISERFS_CHECK is set ON " ) ;
SWARN ( silent , s , " - it is slow mode for debugging. " ) ;
# endif
/* make data=ordered the default */
if ( ! reiserfs_data_log ( s ) & & ! reiserfs_data_ordered ( s ) & &
! reiserfs_data_writeback ( s ) )
{
REISERFS_SB ( s ) - > s_mount_opt | = ( 1 < < REISERFS_DATA_ORDERED ) ;
}
if ( reiserfs_data_log ( s ) ) {
reiserfs_info ( s , " using journaled data mode \n " ) ;
} else if ( reiserfs_data_ordered ( s ) ) {
reiserfs_info ( s , " using ordered data mode \n " ) ;
} else {
reiserfs_info ( s , " using writeback data mode \n " ) ;
}
if ( reiserfs_barrier_flush ( s ) ) {
printk ( " reiserfs: using flush barriers \n " ) ;
}
// set_device_ro(s->s_dev, 1) ;
if ( journal_init ( s , jdev_name , old_format , commit_max_age ) ) {
SWARN ( silent , s , " sh-2022: reiserfs_fill_super: unable to initialize journal space " ) ;
goto error ;
} else {
jinit_done = 1 ; /* once this is set, journal_release must be called
* * if we error out of the mount
*/
}
if ( reread_meta_blocks ( s ) ) {
SWARN ( silent , s , " jmacd-9: reiserfs_fill_super: unable to reread meta blocks after journal init " ) ;
goto error ;
}
if ( replay_only ( s ) )
goto error ;
if ( bdev_read_only ( s - > s_bdev ) & & ! ( s - > s_flags & MS_RDONLY ) ) {
SWARN ( silent , s , " clm-7000: Detected readonly device, marking FS readonly " ) ;
s - > s_flags | = MS_RDONLY ;
}
args . objectid = REISERFS_ROOT_OBJECTID ;
args . dirid = REISERFS_ROOT_PARENT_OBJECTID ;
root_inode = iget5_locked ( s , REISERFS_ROOT_OBJECTID , reiserfs_find_actor , reiserfs_init_locked_inode , ( void * ) ( & args ) ) ;
if ( ! root_inode ) {
SWARN ( silent , s , " jmacd-10: reiserfs_fill_super: get root inode failed " ) ;
goto error ;
}
if ( root_inode - > i_state & I_NEW ) {
reiserfs_read_locked_inode ( root_inode , & args ) ;
unlock_new_inode ( root_inode ) ;
}
s - > s_root = d_alloc_root ( root_inode ) ;
if ( ! s - > s_root ) {
iput ( root_inode ) ;
goto error ;
}
// define and initialize hash function
sbi - > s_hash_function = hash_function ( s ) ;
if ( sbi - > s_hash_function = = NULL ) {
dput ( s - > s_root ) ;
s - > s_root = NULL ;
goto error ;
}
if ( is_reiserfs_3_5 ( rs ) | | ( is_reiserfs_jr ( rs ) & & SB_VERSION ( s ) = = REISERFS_VERSION_1 ) )
set_bit ( REISERFS_3_5 , & ( sbi - > s_properties ) ) ;
else
set_bit ( REISERFS_3_6 , & ( sbi - > s_properties ) ) ;
if ( ! ( s - > s_flags & MS_RDONLY ) ) {
errval = journal_begin ( & th , s , 1 ) ;
if ( errval ) {
dput ( s - > s_root ) ;
s - > s_root = NULL ;
goto error ;
}
reiserfs_prepare_for_journal ( s , SB_BUFFER_WITH_SB ( s ) , 1 ) ;
set_sb_umount_state ( rs , REISERFS_ERROR_FS ) ;
set_sb_fs_state ( rs , 0 ) ;
if ( old_format_only ( s ) ) {
/* filesystem of format 3.5 either with standard or non-standard
journal */
if ( convert_reiserfs ( s ) ) {
/* and -o conv is given */
if ( ! silent )
reiserfs_info ( s , " converting 3.5 filesystem to the 3.6 format " ) ;
if ( is_reiserfs_3_5 ( rs ) )
/* put magic string of 3.6 format. 2.2 will not be able to
mount this filesystem anymore */
memcpy ( rs - > s_v1 . s_magic , reiserfs_3_6_magic_string ,
sizeof ( reiserfs_3_6_magic_string ) ) ;
set_sb_version ( rs , REISERFS_VERSION_2 ) ;
reiserfs_convert_objectid_map_v1 ( s ) ;
set_bit ( REISERFS_3_6 , & ( sbi - > s_properties ) ) ;
clear_bit ( REISERFS_3_5 , & ( sbi - > s_properties ) ) ;
} else if ( ! silent ) {
reiserfs_info ( s , " using 3.5.x disk format \n " ) ;
}
}
journal_mark_dirty ( & th , s , SB_BUFFER_WITH_SB ( s ) ) ;
errval = journal_end ( & th , s , 1 ) ;
if ( errval ) {
dput ( s - > s_root ) ;
s - > s_root = NULL ;
goto error ;
}
if ( ( errval = reiserfs_xattr_init ( s , s - > s_flags ) ) ) {
dput ( s - > s_root ) ;
s - > s_root = NULL ;
goto error ;
}
/* look for files which were to be removed in previous session */
finish_unfinished ( s ) ;
} else {
if ( old_format_only ( s ) & & ! silent ) {
reiserfs_info ( s , " using 3.5.x disk format \n " ) ;
}
if ( ( errval = reiserfs_xattr_init ( s , s - > s_flags ) ) ) {
dput ( s - > s_root ) ;
s - > s_root = NULL ;
goto error ;
}
}
// mark hash in super block: it could be unset. overwrite should be ok
set_sb_hash_function_code ( rs , function2code ( sbi - > s_hash_function ) ) ;
handle_attrs ( s ) ;
reiserfs_proc_info_init ( s ) ;
init_waitqueue_head ( & ( sbi - > s_wait ) ) ;
spin_lock_init ( & sbi - > bitmap_lock ) ;
return ( 0 ) ;
error :
if ( jinit_done ) { /* kill the commit thread, free journal ram */
journal_release_error ( NULL , s ) ;
}
if ( SB_DISK_SUPER_BLOCK ( s ) ) {
for ( j = 0 ; j < SB_BMAP_NR ( s ) ; j + + ) {
if ( SB_AP_BITMAP ( s ) )
brelse ( SB_AP_BITMAP ( s ) [ j ] . bh ) ;
}
if ( SB_AP_BITMAP ( s ) )
vfree ( SB_AP_BITMAP ( s ) ) ;
}
if ( SB_BUFFER_WITH_SB ( s ) )
brelse ( SB_BUFFER_WITH_SB ( s ) ) ;
# ifdef CONFIG_QUOTA
for ( j = 0 ; j < MAXQUOTAS ; j + + ) {
if ( sbi - > s_qf_names [ j ] )
kfree ( sbi - > s_qf_names [ j ] ) ;
}
# endif
if ( sbi ! = NULL ) {
kfree ( sbi ) ;
}
s - > s_fs_info = NULL ;
return errval ;
}
static int reiserfs_statfs ( struct super_block * s , struct kstatfs * buf )
{
struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK ( s ) ;
buf - > f_namelen = ( REISERFS_MAX_NAME ( s - > s_blocksize ) ) ;
buf - > f_bfree = sb_free_blocks ( rs ) ;
buf - > f_bavail = buf - > f_bfree ;
buf - > f_blocks = sb_block_count ( rs ) - sb_bmap_nr ( rs ) - 1 ;
buf - > f_bsize = s - > s_blocksize ;
/* changed to accommodate gcc folks.*/
buf - > f_type = REISERFS_SUPER_MAGIC ;
return 0 ;
}
# ifdef CONFIG_QUOTA
static int reiserfs_dquot_initialize ( struct inode * inode , int type )
{
struct reiserfs_transaction_handle th ;
2005-06-24 09:01:01 +04:00
int ret , err ;
2005-04-17 02:20:36 +04:00
/* We may create quota structure so we need to reserve enough blocks */
reiserfs_write_lock ( inode - > i_sb ) ;
2005-06-24 09:01:01 +04:00
ret = journal_begin ( & th , inode - > i_sb , 2 * REISERFS_QUOTA_INIT_BLOCKS ) ;
if ( ret )
goto out ;
2005-04-17 02:20:36 +04:00
ret = dquot_initialize ( inode , type ) ;
2005-06-24 09:01:01 +04:00
err = journal_end ( & th , inode - > i_sb , 2 * REISERFS_QUOTA_INIT_BLOCKS ) ;
if ( ! ret & & err )
ret = err ;
out :
2005-04-17 02:20:36 +04:00
reiserfs_write_unlock ( inode - > i_sb ) ;
return ret ;
}
static int reiserfs_dquot_drop ( struct inode * inode )
{
struct reiserfs_transaction_handle th ;
2005-06-24 09:01:01 +04:00
int ret , err ;
2005-04-17 02:20:36 +04:00
/* We may delete quota structure so we need to reserve enough blocks */
reiserfs_write_lock ( inode - > i_sb ) ;
2005-06-24 09:01:01 +04:00
ret = journal_begin ( & th , inode - > i_sb , 2 * REISERFS_QUOTA_INIT_BLOCKS ) ;
if ( ret )
goto out ;
2005-04-17 02:20:36 +04:00
ret = dquot_drop ( inode ) ;
2005-06-24 09:01:01 +04:00
err = journal_end ( & th , inode - > i_sb , 2 * REISERFS_QUOTA_INIT_BLOCKS ) ;
if ( ! ret & & err )
ret = err ;
out :
2005-04-17 02:20:36 +04:00
reiserfs_write_unlock ( inode - > i_sb ) ;
return ret ;
}
static int reiserfs_write_dquot ( struct dquot * dquot )
{
struct reiserfs_transaction_handle th ;
2005-06-24 09:01:01 +04:00
int ret , err ;
2005-04-17 02:20:36 +04:00
reiserfs_write_lock ( dquot - > dq_sb ) ;
2005-06-24 09:01:01 +04:00
ret = journal_begin ( & th , dquot - > dq_sb , REISERFS_QUOTA_TRANS_BLOCKS ) ;
if ( ret )
goto out ;
2005-04-17 02:20:36 +04:00
ret = dquot_commit ( dquot ) ;
2005-06-24 09:01:01 +04:00
err = journal_end ( & th , dquot - > dq_sb , REISERFS_QUOTA_TRANS_BLOCKS ) ;
if ( ! ret & & err )
ret = err ;
out :
2005-04-17 02:20:36 +04:00
reiserfs_write_unlock ( dquot - > dq_sb ) ;
return ret ;
}
static int reiserfs_acquire_dquot ( struct dquot * dquot )
{
struct reiserfs_transaction_handle th ;
2005-06-24 09:01:01 +04:00
int ret , err ;
2005-04-17 02:20:36 +04:00
reiserfs_write_lock ( dquot - > dq_sb ) ;
2005-06-24 09:01:01 +04:00
ret = journal_begin ( & th , dquot - > dq_sb , REISERFS_QUOTA_INIT_BLOCKS ) ;
if ( ret )
goto out ;
2005-04-17 02:20:36 +04:00
ret = dquot_acquire ( dquot ) ;
2005-06-24 09:01:01 +04:00
err = journal_end ( & th , dquot - > dq_sb , REISERFS_QUOTA_INIT_BLOCKS ) ;
if ( ! ret & & err )
ret = err ;
out :
2005-04-17 02:20:36 +04:00
reiserfs_write_unlock ( dquot - > dq_sb ) ;
return ret ;
}
static int reiserfs_release_dquot ( struct dquot * dquot )
{
struct reiserfs_transaction_handle th ;
2005-06-24 09:01:01 +04:00
int ret , err ;
2005-04-17 02:20:36 +04:00
reiserfs_write_lock ( dquot - > dq_sb ) ;
2005-06-24 09:01:01 +04:00
ret = journal_begin ( & th , dquot - > dq_sb , REISERFS_QUOTA_INIT_BLOCKS ) ;
if ( ret )
goto out ;
2005-04-17 02:20:36 +04:00
ret = dquot_release ( dquot ) ;
2005-06-24 09:01:01 +04:00
err = journal_end ( & th , dquot - > dq_sb , REISERFS_QUOTA_INIT_BLOCKS ) ;
if ( ! ret & & err )
ret = err ;
out :
2005-04-17 02:20:36 +04:00
reiserfs_write_unlock ( dquot - > dq_sb ) ;
return ret ;
}
static int reiserfs_mark_dquot_dirty ( struct dquot * dquot )
{
/* Are we journalling quotas? */
if ( REISERFS_SB ( dquot - > dq_sb ) - > s_qf_names [ USRQUOTA ] | |
REISERFS_SB ( dquot - > dq_sb ) - > s_qf_names [ GRPQUOTA ] ) {
dquot_mark_dquot_dirty ( dquot ) ;
return reiserfs_write_dquot ( dquot ) ;
}
else
return dquot_mark_dquot_dirty ( dquot ) ;
}
static int reiserfs_write_info ( struct super_block * sb , int type )
{
struct reiserfs_transaction_handle th ;
2005-06-24 09:01:01 +04:00
int ret , err ;
2005-04-17 02:20:36 +04:00
/* Data block + inode block */
reiserfs_write_lock ( sb ) ;
2005-06-24 09:01:01 +04:00
ret = journal_begin ( & th , sb , 2 ) ;
if ( ret )
goto out ;
2005-04-17 02:20:36 +04:00
ret = dquot_commit_info ( sb , type ) ;
2005-06-24 09:01:01 +04:00
err = journal_end ( & th , sb , 2 ) ;
if ( ! ret & & err )
ret = err ;
out :
2005-04-17 02:20:36 +04:00
reiserfs_write_unlock ( sb ) ;
return ret ;
}
/*
2005-06-23 11:09:16 +04:00
* Turn on quotas during mount time - we need to find the quota file and such . . .
2005-04-17 02:20:36 +04:00
*/
static int reiserfs_quota_on_mount ( struct super_block * sb , int type )
{
2005-06-23 11:09:16 +04:00
return vfs_quota_on_mount ( sb , REISERFS_SB ( sb ) - > s_qf_names [ type ] ,
REISERFS_SB ( sb ) - > s_jquota_fmt , type ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Standard function to be called on quota_on
*/
static int reiserfs_quota_on ( struct super_block * sb , int type , int format_id , char * path )
{
int err ;
struct nameidata nd ;
err = path_lookup ( path , LOOKUP_FOLLOW , & nd ) ;
if ( err )
return err ;
/* Quotafile not on the same filesystem? */
if ( nd . mnt - > mnt_sb ! = sb ) {
path_release ( & nd ) ;
return - EXDEV ;
}
/* We must not pack tails for quota files on reiserfs for quota IO to work */
if ( ! REISERFS_I ( nd . dentry - > d_inode ) - > i_flags & i_nopack_mask ) {
reiserfs_warning ( sb , " reiserfs: Quota file must have tail packing disabled. " ) ;
path_release ( & nd ) ;
return - EINVAL ;
}
/* Not journalling quota? No more tests needed... */
if ( ! REISERFS_SB ( sb ) - > s_qf_names [ USRQUOTA ] & &
! REISERFS_SB ( sb ) - > s_qf_names [ GRPQUOTA ] ) {
path_release ( & nd ) ;
return vfs_quota_on ( sb , type , format_id , path ) ;
}
/* Quotafile not of fs root? */
if ( nd . dentry - > d_parent - > d_inode ! = sb - > s_root - > d_inode )
reiserfs_warning ( sb , " reiserfs: Quota file not on filesystem root. "
" Journalled quota will not work. " ) ;
path_release ( & nd ) ;
return vfs_quota_on ( sb , type , format_id , path ) ;
}
/* Read data from quotafile - avoid pagecache and such because we cannot afford
* acquiring the locks . . . As quota files are never truncated and quota code
* itself serializes the operations ( and noone else should touch the files )
* we don ' t have to be afraid of races */
static ssize_t reiserfs_quota_read ( struct super_block * sb , int type , char * data ,
size_t len , loff_t off )
{
struct inode * inode = sb_dqopt ( sb ) - > files [ type ] ;
unsigned long blk = off > > sb - > s_blocksize_bits ;
int err = 0 , offset = off & ( sb - > s_blocksize - 1 ) , tocopy ;
size_t toread ;
struct buffer_head tmp_bh , * bh ;
loff_t i_size = i_size_read ( inode ) ;
if ( off > i_size )
return 0 ;
if ( off + len > i_size )
len = i_size - off ;
toread = len ;
while ( toread > 0 ) {
tocopy = sb - > s_blocksize - offset < toread ? sb - > s_blocksize - offset : toread ;
tmp_bh . b_state = 0 ;
/* Quota files are without tails so we can safely use this function */
reiserfs_write_lock ( sb ) ;
err = reiserfs_get_block ( inode , blk , & tmp_bh , 0 ) ;
reiserfs_write_unlock ( sb ) ;
if ( err )
return err ;
if ( ! buffer_mapped ( & tmp_bh ) ) /* A hole? */
memset ( data , 0 , tocopy ) ;
else {
bh = sb_bread ( sb , tmp_bh . b_blocknr ) ;
if ( ! bh )
return - EIO ;
memcpy ( data , bh - > b_data + offset , tocopy ) ;
brelse ( bh ) ;
}
offset = 0 ;
toread - = tocopy ;
data + = tocopy ;
blk + + ;
}
return len ;
}
/* Write to quotafile (we know the transaction is already started and has
* enough credits ) */
static ssize_t reiserfs_quota_write ( struct super_block * sb , int type ,
const char * data , size_t len , loff_t off )
{
struct inode * inode = sb_dqopt ( sb ) - > files [ type ] ;
unsigned long blk = off > > sb - > s_blocksize_bits ;
int err = 0 , offset = off & ( sb - > s_blocksize - 1 ) , tocopy ;
int journal_quota = REISERFS_SB ( sb ) - > s_qf_names [ type ] ! = NULL ;
size_t towrite = len ;
struct buffer_head tmp_bh , * bh ;
down ( & inode - > i_sem ) ;
while ( towrite > 0 ) {
tocopy = sb - > s_blocksize - offset < towrite ?
sb - > s_blocksize - offset : towrite ;
tmp_bh . b_state = 0 ;
err = reiserfs_get_block ( inode , blk , & tmp_bh , GET_BLOCK_CREATE ) ;
if ( err )
goto out ;
if ( offset | | tocopy ! = sb - > s_blocksize )
bh = sb_bread ( sb , tmp_bh . b_blocknr ) ;
else
bh = sb_getblk ( sb , tmp_bh . b_blocknr ) ;
if ( ! bh ) {
err = - EIO ;
goto out ;
}
lock_buffer ( bh ) ;
memcpy ( bh - > b_data + offset , data , tocopy ) ;
flush_dcache_page ( bh - > b_page ) ;
set_buffer_uptodate ( bh ) ;
unlock_buffer ( bh ) ;
reiserfs_prepare_for_journal ( sb , bh , 1 ) ;
journal_mark_dirty ( current - > journal_info , sb , bh ) ;
if ( ! journal_quota )
reiserfs_add_ordered_list ( inode , bh ) ;
brelse ( bh ) ;
offset = 0 ;
towrite - = tocopy ;
data + = tocopy ;
blk + + ;
}
out :
if ( len = = towrite )
return err ;
if ( inode - > i_size < off + len - towrite )
i_size_write ( inode , off + len - towrite ) ;
inode - > i_version + + ;
inode - > i_mtime = inode - > i_ctime = CURRENT_TIME ;
mark_inode_dirty ( inode ) ;
up ( & inode - > i_sem ) ;
return len - towrite ;
}
# endif
static struct super_block *
get_super_block ( struct file_system_type * fs_type , int flags ,
const char * dev_name , void * data )
{
return get_sb_bdev ( fs_type , flags , dev_name , data , reiserfs_fill_super ) ;
}
static int __init
init_reiserfs_fs ( void )
{
int ret ;
if ( ( ret = init_inodecache ( ) ) ) {
return ret ;
}
if ( ( ret = reiserfs_xattr_register_handlers ( ) ) )
goto failed_reiserfs_xattr_register_handlers ;
reiserfs_proc_info_global_init ( ) ;
reiserfs_proc_register_global ( " version " , reiserfs_global_version_in_proc ) ;
ret = register_filesystem ( & reiserfs_fs_type ) ;
if ( ret = = 0 ) {
return 0 ;
}
reiserfs_xattr_unregister_handlers ( ) ;
failed_reiserfs_xattr_register_handlers :
reiserfs_proc_unregister_global ( " version " ) ;
reiserfs_proc_info_global_done ( ) ;
destroy_inodecache ( ) ;
return ret ;
}
static void __exit
exit_reiserfs_fs ( void )
{
reiserfs_xattr_unregister_handlers ( ) ;
reiserfs_proc_unregister_global ( " version " ) ;
reiserfs_proc_info_global_done ( ) ;
unregister_filesystem ( & reiserfs_fs_type ) ;
destroy_inodecache ( ) ;
}
struct file_system_type reiserfs_fs_type = {
. owner = THIS_MODULE ,
. name = " reiserfs " ,
. get_sb = get_super_block ,
. kill_sb = kill_block_super ,
. fs_flags = FS_REQUIRES_DEV ,
} ;
MODULE_DESCRIPTION ( " ReiserFS journaled filesystem " ) ;
MODULE_AUTHOR ( " Hans Reiser <reiser@namesys.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( init_reiserfs_fs ) ;
module_exit ( exit_reiserfs_fs ) ;