2005-04-17 02:20:36 +04:00
/*
* linux / fs / hfsplus / super . c
*
* Copyright ( C ) 2001
* Brad Boyer ( flar @ allandria . com )
* ( C ) 2003 Ardis Technologies < roman @ ardistech . com >
*
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pagemap.h>
2010-11-23 16:38:21 +03:00
# include <linux/blkdev.h>
2005-04-17 02:20:36 +04:00
# include <linux/fs.h>
# include <linux/slab.h>
# include <linux/vfs.h>
# include <linux/nls.h>
static struct inode * hfsplus_alloc_inode ( struct super_block * sb ) ;
static void hfsplus_destroy_inode ( struct inode * inode ) ;
# include "hfsplus_fs.h"
2010-10-01 07:43:41 +04:00
static int hfsplus_system_read_inode ( struct inode * inode )
{
struct hfsplus_vh * vhdr = HFSPLUS_SB ( inode - > i_sb ) - > s_vhdr ;
switch ( inode - > i_ino ) {
case HFSPLUS_EXT_CNID :
hfsplus_inode_read_fork ( inode , & vhdr - > ext_file ) ;
inode - > i_mapping - > a_ops = & hfsplus_btree_aops ;
break ;
case HFSPLUS_CAT_CNID :
hfsplus_inode_read_fork ( inode , & vhdr - > cat_file ) ;
inode - > i_mapping - > a_ops = & hfsplus_btree_aops ;
break ;
case HFSPLUS_ALLOC_CNID :
hfsplus_inode_read_fork ( inode , & vhdr - > alloc_file ) ;
inode - > i_mapping - > a_ops = & hfsplus_aops ;
break ;
case HFSPLUS_START_CNID :
hfsplus_inode_read_fork ( inode , & vhdr - > start_file ) ;
break ;
case HFSPLUS_ATTR_CNID :
hfsplus_inode_read_fork ( inode , & vhdr - > attr_file ) ;
inode - > i_mapping - > a_ops = & hfsplus_btree_aops ;
break ;
default :
return - EIO ;
}
return 0 ;
}
2008-02-07 11:15:40 +03:00
struct inode * hfsplus_iget ( struct super_block * sb , unsigned long ino )
2005-04-17 02:20:36 +04:00
{
struct hfs_find_data fd ;
2008-02-07 11:15:40 +03:00
struct inode * inode ;
2010-10-01 07:43:41 +04:00
int err ;
2008-02-07 11:15:40 +03:00
inode = iget_locked ( sb , ino ) ;
if ( ! inode )
return ERR_PTR ( - ENOMEM ) ;
if ( ! ( inode - > i_state & I_NEW ) )
return inode ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:43:31 +04:00
INIT_LIST_HEAD ( & HFSPLUS_I ( inode ) - > open_dir_list ) ;
mutex_init ( & HFSPLUS_I ( inode ) - > extents_lock ) ;
HFSPLUS_I ( inode ) - > flags = 0 ;
2010-11-23 16:38:13 +03:00
HFSPLUS_I ( inode ) - > extent_state = 0 ;
2010-10-01 07:43:31 +04:00
HFSPLUS_I ( inode ) - > rsrc_inode = NULL ;
atomic_set ( & HFSPLUS_I ( inode ) - > opencnt , 0 ) ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:43:41 +04:00
if ( inode - > i_ino > = HFSPLUS_FIRSTUSER_CNID | |
inode - > i_ino = = HFSPLUS_ROOT_CNID ) {
2010-10-01 07:42:59 +04:00
hfs_find_init ( HFSPLUS_SB ( inode - > i_sb ) - > cat_tree , & fd ) ;
2005-04-17 02:20:36 +04:00
err = hfsplus_find_cat ( inode - > i_sb , inode - > i_ino , & fd ) ;
if ( ! err )
err = hfsplus_cat_read_inode ( inode , & fd ) ;
hfs_find_exit ( & fd ) ;
2010-10-01 07:43:41 +04:00
} else {
err = hfsplus_system_read_inode ( inode ) ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:43:41 +04:00
if ( err ) {
iget_failed ( inode ) ;
return ERR_PTR ( err ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:15:40 +03:00
unlock_new_inode ( inode ) ;
return inode ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:43:43 +04:00
static int hfsplus_system_write_inode ( struct inode * inode )
2005-04-17 02:20:36 +04:00
{
2010-10-01 07:42:59 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( inode - > i_sb ) ;
2010-10-01 07:43:43 +04:00
struct hfsplus_vh * vhdr = sbi - > s_vhdr ;
struct hfsplus_fork_raw * fork ;
struct hfs_btree * tree = NULL ;
2005-04-17 02:20:36 +04:00
switch ( inode - > i_ino ) {
case HFSPLUS_EXT_CNID :
2010-10-01 07:43:43 +04:00
fork = & vhdr - > ext_file ;
tree = sbi - > ext_tree ;
2005-04-17 02:20:36 +04:00
break ;
case HFSPLUS_CAT_CNID :
2010-10-01 07:43:43 +04:00
fork = & vhdr - > cat_file ;
tree = sbi - > cat_tree ;
2005-04-17 02:20:36 +04:00
break ;
case HFSPLUS_ALLOC_CNID :
2010-10-01 07:43:43 +04:00
fork = & vhdr - > alloc_file ;
2005-04-17 02:20:36 +04:00
break ;
case HFSPLUS_START_CNID :
2010-10-01 07:43:43 +04:00
fork = & vhdr - > start_file ;
2005-04-17 02:20:36 +04:00
break ;
case HFSPLUS_ATTR_CNID :
2010-10-01 07:43:43 +04:00
fork = & vhdr - > attr_file ;
tree = sbi - > attr_tree ;
default :
return - EIO ;
}
if ( fork - > total_size ! = cpu_to_be64 ( inode - > i_size ) ) {
2010-10-01 07:45:20 +04:00
set_bit ( HFSPLUS_SB_WRITEBACKUP , & sbi - > flags ) ;
2010-10-01 07:43:43 +04:00
inode - > i_sb - > s_dirt = 1 ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:43:43 +04:00
hfsplus_inode_write_fork ( inode , fork ) ;
if ( tree )
hfs_btree_write ( tree ) ;
return 0 ;
}
static int hfsplus_write_inode ( struct inode * inode ,
struct writeback_control * wbc )
{
dprint ( DBG_INODE , " hfsplus_write_inode: %lu \n " , inode - > i_ino ) ;
hfsplus_ext_write_extent ( inode ) ;
if ( inode - > i_ino > = HFSPLUS_FIRSTUSER_CNID | |
inode - > i_ino = = HFSPLUS_ROOT_CNID )
return hfsplus_cat_write_inode ( inode ) ;
else
return hfsplus_system_write_inode ( inode ) ;
2005-04-17 02:20:36 +04:00
}
2010-06-07 22:34:48 +04:00
static void hfsplus_evict_inode ( struct inode * inode )
2005-04-17 02:20:36 +04:00
{
2010-06-07 22:34:48 +04:00
dprint ( DBG_INODE , " hfsplus_evict_inode: %lu \n " , inode - > i_ino ) ;
truncate_inode_pages ( & inode - > i_data , 0 ) ;
end_writeback ( inode ) ;
2005-04-17 02:20:36 +04:00
if ( HFSPLUS_IS_RSRC ( inode ) ) {
2010-10-01 07:43:31 +04:00
HFSPLUS_I ( HFSPLUS_I ( inode ) - > rsrc_inode ) - > rsrc_inode = NULL ;
iput ( HFSPLUS_I ( inode ) - > rsrc_inode ) ;
2005-04-17 02:20:36 +04:00
}
}
2010-07-04 12:24:09 +04:00
int hfsplus_sync_fs ( struct super_block * sb , int wait )
2005-04-17 02:20:36 +04:00
{
2010-10-01 07:42:59 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( sb ) ;
struct hfsplus_vh * vhdr = sbi - > s_vhdr ;
2010-11-23 16:37:47 +03:00
int write_backup = 0 ;
int error , error2 ;
2005-04-17 02:20:36 +04:00
2010-11-23 16:38:02 +03:00
if ( ! wait )
return 0 ;
2005-04-17 02:20:36 +04:00
dprint ( DBG_SUPER , " hfsplus_write_super \n " ) ;
2009-05-12 01:35:03 +04:00
2005-04-17 02:20:36 +04:00
sb - > s_dirt = 0 ;
2010-11-23 16:37:57 +03:00
/*
* Explicitly write out the special metadata inodes .
*
* While these special inodes are marked as hashed and written
* out peridocically by the flusher threads we redirty them
* during writeout of normal inodes , and thus the life lock
* prevents us from getting the latest state to disk .
*/
error = filemap_write_and_wait ( sbi - > cat_tree - > inode - > i_mapping ) ;
error2 = filemap_write_and_wait ( sbi - > ext_tree - > inode - > i_mapping ) ;
if ( ! error )
error = error2 ;
error2 = filemap_write_and_wait ( sbi - > alloc_file - > i_mapping ) ;
if ( ! error )
error = error2 ;
2010-11-23 16:37:47 +03:00
mutex_lock ( & sbi - > vh_mutex ) ;
mutex_lock ( & sbi - > alloc_mutex ) ;
2010-10-01 07:42:59 +04:00
vhdr - > free_blocks = cpu_to_be32 ( sbi - > free_blocks ) ;
vhdr - > next_cnid = cpu_to_be32 ( sbi - > next_cnid ) ;
vhdr - > folder_count = cpu_to_be32 ( sbi - > folder_count ) ;
vhdr - > file_count = cpu_to_be32 ( sbi - > file_count ) ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:45:20 +04:00
if ( test_and_clear_bit ( HFSPLUS_SB_WRITEBACKUP , & sbi - > flags ) ) {
2010-11-23 16:37:47 +03:00
memcpy ( sbi - > s_backup_vhdr , sbi - > s_vhdr , sizeof ( * sbi - > s_vhdr ) ) ;
write_backup = 1 ;
2005-04-17 02:20:36 +04:00
}
2010-11-23 16:37:47 +03:00
2010-11-23 16:37:57 +03:00
error2 = hfsplus_submit_bio ( sb - > s_bdev ,
2010-11-23 16:37:47 +03:00
sbi - > part_start + HFSPLUS_VOLHEAD_SECTOR ,
sbi - > s_vhdr , WRITE_SYNC ) ;
2010-11-23 16:37:57 +03:00
if ( ! error )
error = error2 ;
2010-11-23 16:37:47 +03:00
if ( ! write_backup )
goto out ;
error2 = hfsplus_submit_bio ( sb - > s_bdev ,
sbi - > part_start + sbi - > sect_count - 2 ,
sbi - > s_backup_vhdr , WRITE_SYNC ) ;
if ( ! error )
error2 = error ;
out :
2010-10-01 07:42:59 +04:00
mutex_unlock ( & sbi - > alloc_mutex ) ;
2010-10-01 07:45:08 +04:00
mutex_unlock ( & sbi - > vh_mutex ) ;
2010-11-23 16:38:21 +03:00
if ( ! test_bit ( HFSPLUS_SB_NOBARRIER , & sbi - > flags ) )
blkdev_issue_flush ( sb - > s_bdev , GFP_KERNEL , NULL ) ;
2010-11-23 16:37:47 +03:00
return error ;
2009-06-08 12:05:12 +04:00
}
static void hfsplus_write_super ( struct super_block * sb )
{
if ( ! ( sb - > s_flags & MS_RDONLY ) )
hfsplus_sync_fs ( sb , 1 ) ;
else
sb - > s_dirt = 0 ;
2005-04-17 02:20:36 +04:00
}
static void hfsplus_put_super ( struct super_block * sb )
{
2010-10-01 07:42:59 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( sb ) ;
2005-04-17 02:20:36 +04:00
dprint ( DBG_SUPER , " hfsplus_put_super \n " ) ;
2010-10-01 07:42:59 +04:00
2005-05-01 19:59:16 +04:00
if ( ! sb - > s_fs_info )
return ;
push BKL down into ->put_super
Move BKL into ->put_super from the only caller. A couple of
filesystems had trivial enough ->put_super (only kfree and NULLing of
s_fs_info + stuff in there) to not get any locking: coda, cramfs, efs,
hugetlbfs, omfs, qnx4, shmem, all others got the full treatment. Most
of them probably don't need it, but I'd rather sort that out individually.
Preferably after all the other BKL pushdowns in that area.
[AV: original used to move lock_super() down as well; these changes are
removed since we don't do lock_super() at all in generic_shutdown_super()
now]
[AV: fuse, btrfs and xfs are known to need no damn BKL, exempt]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2009-05-05 17:40:36 +04:00
2010-10-01 07:42:59 +04:00
if ( ! ( sb - > s_flags & MS_RDONLY ) & & sbi - > s_vhdr ) {
struct hfsplus_vh * vhdr = sbi - > s_vhdr ;
2005-04-17 02:20:36 +04:00
vhdr - > modify_date = hfsp_now2mt ( ) ;
vhdr - > attributes | = cpu_to_be32 ( HFSPLUS_VOL_UNMNT ) ;
vhdr - > attributes & = cpu_to_be32 ( ~ HFSPLUS_VOL_INCNSTNT ) ;
2010-11-23 16:37:43 +03:00
hfsplus_sync_fs ( sb , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:42:59 +04:00
hfs_btree_close ( sbi - > cat_tree ) ;
hfs_btree_close ( sbi - > ext_tree ) ;
iput ( sbi - > alloc_file ) ;
iput ( sbi - > hidden_dir ) ;
2010-11-23 16:37:47 +03:00
kfree ( sbi - > s_vhdr ) ;
kfree ( sbi - > s_backup_vhdr ) ;
2010-10-01 07:42:59 +04:00
unload_nls ( sbi - > nls ) ;
2005-05-01 19:59:16 +04:00
kfree ( sb - > s_fs_info ) ;
sb - > s_fs_info = NULL ;
2005-04-17 02:20:36 +04:00
}
2006-06-23 13:02:58 +04:00
static int hfsplus_statfs ( struct dentry * dentry , struct kstatfs * buf )
2005-04-17 02:20:36 +04:00
{
2006-06-23 13:02:58 +04:00
struct super_block * sb = dentry - > d_sb ;
2010-10-01 07:42:59 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( sb ) ;
2009-04-03 03:59:36 +04:00
u64 id = huge_encode_dev ( sb - > s_bdev - > bd_dev ) ;
2006-06-23 13:02:58 +04:00
2005-04-17 02:20:36 +04:00
buf - > f_type = HFSPLUS_SUPER_MAGIC ;
buf - > f_bsize = sb - > s_blocksize ;
2010-10-01 07:42:59 +04:00
buf - > f_blocks = sbi - > total_blocks < < sbi - > fs_shift ;
buf - > f_bfree = sbi - > free_blocks < < sbi - > fs_shift ;
2005-04-17 02:20:36 +04:00
buf - > f_bavail = buf - > f_bfree ;
buf - > f_files = 0xFFFFFFFF ;
2010-10-01 07:42:59 +04:00
buf - > f_ffree = 0xFFFFFFFF - sbi - > next_cnid ;
2009-04-03 03:59:36 +04:00
buf - > f_fsid . val [ 0 ] = ( u32 ) id ;
buf - > f_fsid . val [ 1 ] = ( u32 ) ( id > > 32 ) ;
2005-04-17 02:20:36 +04:00
buf - > f_namelen = HFSPLUS_MAX_STRLEN ;
return 0 ;
}
static int hfsplus_remount ( struct super_block * sb , int * flags , char * data )
{
if ( ( * flags & MS_RDONLY ) = = ( sb - > s_flags & MS_RDONLY ) )
return 0 ;
if ( ! ( * flags & MS_RDONLY ) ) {
2010-10-01 07:42:59 +04:00
struct hfsplus_vh * vhdr = HFSPLUS_SB ( sb ) - > s_vhdr ;
2010-11-08 01:01:17 +03:00
int force = 0 ;
2005-11-30 06:34:41 +03:00
2010-11-08 01:01:17 +03:00
if ( ! hfsplus_parse_options_remount ( data , & force ) )
2005-11-30 06:34:41 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( ! ( vhdr - > attributes & cpu_to_be32 ( HFSPLUS_VOL_UNMNT ) ) ) {
2010-12-16 19:08:38 +03:00
printk ( KERN_WARNING " hfs: filesystem was "
" not cleanly unmounted, "
" running fsck.hfsplus is recommended. "
" leaving read-only. \n " ) ;
2005-04-17 02:20:36 +04:00
sb - > s_flags | = MS_RDONLY ;
* flags | = MS_RDONLY ;
2010-11-08 01:01:17 +03:00
} else if ( force ) {
2005-11-30 06:34:41 +03:00
/* nothing */
2010-12-16 19:08:38 +03:00
} else if ( vhdr - > attributes &
cpu_to_be32 ( HFSPLUS_VOL_SOFTLOCK ) ) {
printk ( KERN_WARNING " hfs: filesystem is marked locked, "
" leaving read-only. \n " ) ;
2005-04-17 02:20:36 +04:00
sb - > s_flags | = MS_RDONLY ;
* flags | = MS_RDONLY ;
2010-12-16 19:08:38 +03:00
} else if ( vhdr - > attributes &
cpu_to_be32 ( HFSPLUS_VOL_JOURNALED ) ) {
printk ( KERN_WARNING " hfs: filesystem is "
" marked journaled, "
" leaving read-only. \n " ) ;
2005-11-30 06:34:41 +03:00
sb - > s_flags | = MS_RDONLY ;
* flags | = MS_RDONLY ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}
2007-02-12 11:55:41 +03:00
static const struct super_operations hfsplus_sops = {
2005-04-17 02:20:36 +04:00
. alloc_inode = hfsplus_alloc_inode ,
. destroy_inode = hfsplus_destroy_inode ,
. write_inode = hfsplus_write_inode ,
2010-06-07 22:34:48 +04:00
. evict_inode = hfsplus_evict_inode ,
2005-04-17 02:20:36 +04:00
. put_super = hfsplus_put_super ,
. write_super = hfsplus_write_super ,
2009-06-08 12:05:12 +04:00
. sync_fs = hfsplus_sync_fs ,
2005-04-17 02:20:36 +04:00
. statfs = hfsplus_statfs ,
. remount_fs = hfsplus_remount ,
2005-09-07 02:18:48 +04:00
. show_options = hfsplus_show_options ,
2005-04-17 02:20:36 +04:00
} ;
static int hfsplus_fill_super ( struct super_block * sb , void * data , int silent )
{
struct hfsplus_vh * vhdr ;
struct hfsplus_sb_info * sbi ;
hfsplus_cat_entry entry ;
struct hfs_find_data fd ;
2008-02-07 11:15:40 +03:00
struct inode * root , * inode ;
2005-04-17 02:20:36 +04:00
struct qstr str ;
struct nls_table * nls = NULL ;
2011-02-02 19:32:39 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2011-02-02 19:32:39 +03:00
err = - EINVAL ;
2007-07-16 10:40:50 +04:00
sbi = kzalloc ( sizeof ( * sbi ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! sbi )
2011-02-02 19:32:39 +03:00
goto out ;
2005-04-17 02:20:36 +04:00
sb - > s_fs_info = sbi ;
2010-10-01 07:41:39 +04:00
mutex_init ( & sbi - > alloc_mutex ) ;
2010-10-01 07:45:08 +04:00
mutex_init ( & sbi - > vh_mutex ) ;
2005-09-07 02:18:48 +04:00
hfsplus_fill_defaults ( sbi ) ;
2011-02-02 19:32:39 +03:00
err = - EINVAL ;
2005-09-07 02:18:48 +04:00
if ( ! hfsplus_parse_options ( data , sbi ) ) {
2006-01-19 04:43:05 +03:00
printk ( KERN_ERR " hfs: unable to parse mount options \n " ) ;
2011-02-02 19:32:39 +03:00
goto out_unload_nls ;
2005-04-17 02:20:36 +04:00
}
/* temporarily use utf8 to correctly find the hidden dir below */
nls = sbi - > nls ;
sbi - > nls = load_nls ( " utf8 " ) ;
2006-01-06 11:09:45 +03:00
if ( ! sbi - > nls ) {
2006-01-19 04:43:05 +03:00
printk ( KERN_ERR " hfs: unable to load nls for utf8 \n " ) ;
2011-02-02 19:32:39 +03:00
goto out_unload_nls ;
2005-04-17 02:20:36 +04:00
}
/* Grab the volume header */
if ( hfsplus_read_wrapper ( sb ) ) {
if ( ! silent )
2006-01-19 04:43:05 +03:00
printk ( KERN_WARNING " hfs: unable to find HFS+ superblock \n " ) ;
2011-02-02 19:32:39 +03:00
goto out_unload_nls ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:42:59 +04:00
vhdr = sbi - > s_vhdr ;
2005-04-17 02:20:36 +04:00
/* Copy parts of the volume header into the superblock */
2006-01-19 04:43:08 +03:00
sb - > s_magic = HFSPLUS_VOLHEAD_SIG ;
if ( be16_to_cpu ( vhdr - > version ) < HFSPLUS_MIN_VERSION | |
be16_to_cpu ( vhdr - > version ) > HFSPLUS_CURRENT_VERSION ) {
2006-01-19 04:43:05 +03:00
printk ( KERN_ERR " hfs: wrong filesystem version \n " ) ;
2011-02-02 19:32:39 +03:00
goto out_free_vhdr ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:42:59 +04:00
sbi - > total_blocks = be32_to_cpu ( vhdr - > total_blocks ) ;
sbi - > free_blocks = be32_to_cpu ( vhdr - > free_blocks ) ;
sbi - > next_cnid = be32_to_cpu ( vhdr - > next_cnid ) ;
sbi - > file_count = be32_to_cpu ( vhdr - > file_count ) ;
sbi - > folder_count = be32_to_cpu ( vhdr - > folder_count ) ;
sbi - > data_clump_blocks =
be32_to_cpu ( vhdr - > data_clump_sz ) > > sbi - > alloc_blksz_shift ;
if ( ! sbi - > data_clump_blocks )
sbi - > data_clump_blocks = 1 ;
sbi - > rsrc_clump_blocks =
be32_to_cpu ( vhdr - > rsrc_clump_sz ) > > sbi - > alloc_blksz_shift ;
if ( ! sbi - > rsrc_clump_blocks )
sbi - > rsrc_clump_blocks = 1 ;
2005-04-17 02:20:36 +04:00
/* Set up operations so we can load metadata */
sb - > s_op = & hfsplus_sops ;
sb - > s_maxbytes = MAX_LFS_FILESIZE ;
if ( ! ( vhdr - > attributes & cpu_to_be32 ( HFSPLUS_VOL_UNMNT ) ) ) {
2010-12-16 19:08:38 +03:00
printk ( KERN_WARNING " hfs: Filesystem was "
" not cleanly unmounted, "
" running fsck.hfsplus is recommended. "
" mounting read-only. \n " ) ;
2005-04-17 02:20:36 +04:00
sb - > s_flags | = MS_RDONLY ;
2010-10-01 07:45:20 +04:00
} else if ( test_and_clear_bit ( HFSPLUS_SB_FORCE , & sbi - > flags ) ) {
2005-11-30 06:34:41 +03:00
/* nothing */
2005-04-17 02:20:36 +04:00
} else if ( vhdr - > attributes & cpu_to_be32 ( HFSPLUS_VOL_SOFTLOCK ) ) {
2006-01-19 04:43:05 +03:00
printk ( KERN_WARNING " hfs: Filesystem is marked locked, mounting read-only. \n " ) ;
2005-04-17 02:20:36 +04:00
sb - > s_flags | = MS_RDONLY ;
2010-12-16 19:08:38 +03:00
} else if ( ( vhdr - > attributes & cpu_to_be32 ( HFSPLUS_VOL_JOURNALED ) ) & &
! ( sb - > s_flags & MS_RDONLY ) ) {
printk ( KERN_WARNING " hfs: write access to "
" a journaled filesystem is not supported, "
" use the force option at your own risk, "
" mounting read-only. \n " ) ;
2005-11-30 06:34:41 +03:00
sb - > s_flags | = MS_RDONLY ;
2005-04-17 02:20:36 +04:00
}
/* Load metadata objects (B*Trees) */
2010-10-01 07:42:59 +04:00
sbi - > ext_tree = hfs_btree_open ( sb , HFSPLUS_EXT_CNID ) ;
if ( ! sbi - > ext_tree ) {
2006-01-19 04:43:05 +03:00
printk ( KERN_ERR " hfs: failed to load extents file \n " ) ;
2011-02-02 19:32:39 +03:00
goto out_free_vhdr ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:42:59 +04:00
sbi - > cat_tree = hfs_btree_open ( sb , HFSPLUS_CAT_CNID ) ;
if ( ! sbi - > cat_tree ) {
2006-01-19 04:43:05 +03:00
printk ( KERN_ERR " hfs: failed to load catalog file \n " ) ;
2011-02-02 19:32:39 +03:00
goto out_close_ext_tree ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:15:40 +03:00
inode = hfsplus_iget ( sb , HFSPLUS_ALLOC_CNID ) ;
if ( IS_ERR ( inode ) ) {
2006-01-19 04:43:05 +03:00
printk ( KERN_ERR " hfs: failed to load allocation file \n " ) ;
2008-02-07 11:15:40 +03:00
err = PTR_ERR ( inode ) ;
2011-02-02 19:32:39 +03:00
goto out_close_cat_tree ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:42:59 +04:00
sbi - > alloc_file = inode ;
2005-04-17 02:20:36 +04:00
/* Load the root directory */
2008-02-07 11:15:40 +03:00
root = hfsplus_iget ( sb , HFSPLUS_ROOT_CNID ) ;
if ( IS_ERR ( root ) ) {
printk ( KERN_ERR " hfs: failed to load root directory \n " ) ;
err = PTR_ERR ( root ) ;
2011-02-02 19:32:39 +03:00
goto out_put_alloc_file ;
2005-04-17 02:20:36 +04:00
}
str . len = sizeof ( HFSP_HIDDENDIR_NAME ) - 1 ;
str . name = HFSP_HIDDENDIR_NAME ;
2010-10-01 07:42:59 +04:00
hfs_find_init ( sbi - > cat_tree , & fd ) ;
2005-04-17 02:20:36 +04:00
hfsplus_cat_build_key ( sb , fd . search_key , HFSPLUS_ROOT_CNID , & str ) ;
if ( ! hfs_brec_read ( & fd , & entry , sizeof ( entry ) ) ) {
hfs_find_exit ( & fd ) ;
if ( entry . type ! = cpu_to_be16 ( HFSPLUS_FOLDER ) )
2011-02-02 19:32:39 +03:00
goto out_put_root ;
2008-02-07 11:15:40 +03:00
inode = hfsplus_iget ( sb , be32_to_cpu ( entry . folder . id ) ) ;
if ( IS_ERR ( inode ) ) {
err = PTR_ERR ( inode ) ;
2011-02-02 19:32:39 +03:00
goto out_put_root ;
2008-02-07 11:15:40 +03:00
}
2010-10-01 07:42:59 +04:00
sbi - > hidden_dir = inode ;
2005-04-17 02:20:36 +04:00
} else
hfs_find_exit ( & fd ) ;
2011-02-02 19:32:39 +03:00
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
/*
* H + LX = = hfsplusutils , H + Lx = = this driver , H + lx is unused
* all three are registered with Apple for our use
*/
vhdr - > last_mount_vers = cpu_to_be32 ( HFSP_MOUNT_VERSION ) ;
vhdr - > modify_date = hfsp_now2mt ( ) ;
be32_add_cpu ( & vhdr - > write_count , 1 ) ;
vhdr - > attributes & = cpu_to_be32 ( ~ HFSPLUS_VOL_UNMNT ) ;
vhdr - > attributes | = cpu_to_be32 ( HFSPLUS_VOL_INCNSTNT ) ;
hfsplus_sync_fs ( sb , 1 ) ;
2005-04-17 02:20:36 +04:00
2011-02-02 19:32:39 +03:00
if ( ! sbi - > hidden_dir ) {
mutex_lock ( & sbi - > vh_mutex ) ;
sbi - > hidden_dir = hfsplus_new_inode ( sb , S_IFDIR ) ;
hfsplus_create_cat ( sbi - > hidden_dir - > i_ino , root , & str ,
sbi - > hidden_dir ) ;
mutex_unlock ( & sbi - > vh_mutex ) ;
hfsplus_mark_inode_dirty ( sbi - > hidden_dir ,
HFSPLUS_I_CAT_DIRTY ) ;
}
2005-04-17 02:20:36 +04:00
}
2011-02-02 19:32:39 +03:00
sb - > s_d_op = & hfsplus_dentry_operations ;
sb - > s_root = d_alloc_root ( root ) ;
if ( ! sb - > s_root ) {
err = - ENOMEM ;
goto out_put_hidden_dir ;
}
2005-04-17 02:20:36 +04:00
unload_nls ( sbi - > nls ) ;
sbi - > nls = nls ;
return 0 ;
2011-02-02 19:32:39 +03:00
out_put_hidden_dir :
iput ( sbi - > hidden_dir ) ;
out_put_root :
iput ( sbi - > alloc_file ) ;
out_put_alloc_file :
iput ( sbi - > alloc_file ) ;
out_close_cat_tree :
hfs_btree_close ( sbi - > cat_tree ) ;
out_close_ext_tree :
hfs_btree_close ( sbi - > ext_tree ) ;
out_free_vhdr :
kfree ( sbi - > s_vhdr ) ;
kfree ( sbi - > s_backup_vhdr ) ;
out_unload_nls :
unload_nls ( sbi - > nls ) ;
2009-08-17 01:05:08 +04:00
unload_nls ( nls ) ;
2011-02-02 19:32:39 +03:00
kfree ( sbi ) ;
out :
2005-04-17 02:20:36 +04:00
return err ;
}
MODULE_AUTHOR ( " Brad Boyer " ) ;
MODULE_DESCRIPTION ( " Extended Macintosh Filesystem " ) ;
MODULE_LICENSE ( " GPL " ) ;
2006-12-07 07:33:20 +03:00
static struct kmem_cache * hfsplus_inode_cachep ;
2005-04-17 02:20:36 +04:00
static struct inode * hfsplus_alloc_inode ( struct super_block * sb )
{
struct hfsplus_inode_info * i ;
2006-12-07 07:33:17 +03:00
i = kmem_cache_alloc ( hfsplus_inode_cachep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
return i ? & i - > vfs_inode : NULL ;
}
2011-01-07 09:49:49 +03:00
static void hfsplus_i_callback ( struct rcu_head * head )
2005-04-17 02:20:36 +04:00
{
2011-01-07 09:49:49 +03:00
struct inode * inode = container_of ( head , struct inode , i_rcu ) ;
INIT_LIST_HEAD ( & inode - > i_dentry ) ;
2010-10-01 07:43:31 +04:00
kmem_cache_free ( hfsplus_inode_cachep , HFSPLUS_I ( inode ) ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-07 09:49:49 +03:00
static void hfsplus_destroy_inode ( struct inode * inode )
{
call_rcu ( & inode - > i_rcu , hfsplus_i_callback ) ;
}
2005-04-17 02:20:36 +04:00
# define HFSPLUS_INODE_SIZE sizeof(struct hfsplus_inode_info)
2010-07-25 00:46:55 +04:00
static struct dentry * hfsplus_mount ( struct file_system_type * fs_type ,
int flags , const char * dev_name , void * data )
2005-04-17 02:20:36 +04:00
{
2010-07-25 00:46:55 +04:00
return mount_bdev ( fs_type , flags , dev_name , data , hfsplus_fill_super ) ;
2005-04-17 02:20:36 +04:00
}
static struct file_system_type hfsplus_fs_type = {
. owner = THIS_MODULE ,
. name = " hfsplus " ,
2010-07-25 00:46:55 +04:00
. mount = hfsplus_mount ,
2005-04-17 02:20:36 +04:00
. kill_sb = kill_block_super ,
. fs_flags = FS_REQUIRES_DEV ,
} ;
2008-07-26 06:45:34 +04:00
static void hfsplus_init_once ( void * p )
2005-04-17 02:20:36 +04:00
{
struct hfsplus_inode_info * i = p ;
2007-05-17 09:10:57 +04:00
inode_init_once ( & i - > vfs_inode ) ;
2005-04-17 02:20:36 +04:00
}
static int __init init_hfsplus_fs ( void )
{
int err ;
hfsplus_inode_cachep = kmem_cache_create ( " hfsplus_icache " ,
HFSPLUS_INODE_SIZE , 0 , SLAB_HWCACHE_ALIGN ,
2007-07-20 05:11:58 +04:00
hfsplus_init_once ) ;
2005-04-17 02:20:36 +04:00
if ( ! hfsplus_inode_cachep )
return - ENOMEM ;
err = register_filesystem ( & hfsplus_fs_type ) ;
if ( err )
kmem_cache_destroy ( hfsplus_inode_cachep ) ;
return err ;
}
static void __exit exit_hfsplus_fs ( void )
{
unregister_filesystem ( & hfsplus_fs_type ) ;
2006-09-27 12:49:40 +04:00
kmem_cache_destroy ( hfsplus_inode_cachep ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( init_hfsplus_fs )
module_exit ( exit_hfsplus_fs )