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>
2015-05-23 00:13:32 +03:00
# include <linux/backing-dev.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"
2013-02-28 05:03:04 +04:00
# include "xattr.h"
2005-04-17 02:20:36 +04:00
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 ) {
2011-07-06 02:29:59 +04:00
err = hfs_find_init ( HFSPLUS_SB ( inode - > i_sb ) - > cat_tree , & fd ) ;
if ( ! err ) {
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 ;
2013-02-28 05:03:04 +04:00
break ;
2010-10-01 07:43:43 +04:00
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 ) ;
2012-07-12 18:26:31 +04:00
hfsplus_mark_mdb_dirty ( inode - > i_sb ) ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:43:43 +04:00
hfsplus_inode_write_fork ( inode , fork ) ;
2012-12-21 03:05:28 +04:00
if ( tree ) {
int err = hfs_btree_write ( tree ) ;
2014-06-07 01:36:31 +04:00
2012-12-21 03:05:28 +04:00
if ( err ) {
2013-05-01 02:27:55 +04:00
pr_err ( " b-tree write err: %d, ino %lu \n " ,
2014-06-07 01:36:31 +04:00
err , inode - > i_ino ) ;
2012-12-21 03:05:28 +04:00
return err ;
}
}
2010-10-01 07:43:43 +04:00
return 0 ;
}
static int hfsplus_write_inode ( struct inode * inode ,
struct writeback_control * wbc )
{
2011-07-06 02:30:00 +04:00
int err ;
2013-05-01 02:27:54 +04:00
hfs_dbg ( INODE , " hfsplus_write_inode: %lu \n " , inode - > i_ino ) ;
2010-10-01 07:43:43 +04:00
2011-07-06 02:30:00 +04:00
err = hfsplus_ext_write_extent ( inode ) ;
if ( err )
return err ;
2010-10-01 07:43:43 +04:00
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
{
2013-05-01 02:27:54 +04:00
hfs_dbg ( INODE , " hfsplus_evict_inode: %lu \n " , inode - > i_ino ) ;
2014-04-04 01:47:49 +04:00
truncate_inode_pages_final ( & inode - > i_data ) ;
2012-05-03 16:48:02 +04:00
clear_inode ( 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
}
}
2012-07-12 18:26:28 +04:00
static 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
2013-05-01 02:27:54 +04:00
hfs_dbg ( SUPER , " hfsplus_sync_fs \n " ) ;
2009-05-12 01:35:03 +04:00
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 ;
2013-02-28 05:03:04 +04:00
if ( sbi - > attr_tree ) {
error2 =
filemap_write_and_wait ( sbi - > attr_tree - > inode - > i_mapping ) ;
if ( ! error )
error = error2 ;
}
2010-11-23 16:37:57 +03:00
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
2011-07-18 19:06:23 +04:00
error2 = hfsplus_submit_bio ( sb ,
2010-11-23 16:37:47 +03:00
sbi - > part_start + HFSPLUS_VOLHEAD_SECTOR ,
2011-07-18 19:06:23 +04:00
sbi - > s_vhdr_buf , NULL , 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 ;
2011-07-18 19:06:23 +04:00
error2 = hfsplus_submit_bio ( sb ,
2010-11-23 16:37:47 +03:00
sbi - > part_start + sbi - > sect_count - 2 ,
2011-07-18 19:06:23 +04:00
sbi - > s_backup_vhdr_buf , NULL , WRITE_SYNC ) ;
2010-11-23 16:37:47 +03:00
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
}
2012-07-12 18:26:31 +04:00
static void delayed_sync_fs ( struct work_struct * work )
2009-06-08 12:05:12 +04:00
{
2012-12-21 03:05:29 +04:00
int err ;
2012-07-12 18:26:31 +04:00
struct hfsplus_sb_info * sbi ;
sbi = container_of ( work , struct hfsplus_sb_info , sync_work . work ) ;
spin_lock ( & sbi - > work_lock ) ;
sbi - > work_queued = 0 ;
spin_unlock ( & sbi - > work_lock ) ;
2012-12-21 03:05:29 +04:00
err = hfsplus_sync_fs ( sbi - > alloc_file - > i_sb , 1 ) ;
if ( err )
2013-05-01 02:27:55 +04:00
pr_err ( " delayed sync fs err %d \n " , err ) ;
2012-07-12 18:26:31 +04:00
}
void hfsplus_mark_mdb_dirty ( struct super_block * sb )
{
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( sb ) ;
unsigned long delay ;
if ( sb - > s_flags & MS_RDONLY )
return ;
spin_lock ( & sbi - > work_lock ) ;
if ( ! sbi - > work_queued ) {
delay = msecs_to_jiffies ( dirty_writeback_interval * 10 ) ;
queue_delayed_work ( system_long_wq , & sbi - > sync_work , delay ) ;
sbi - > work_queued = 1 ;
}
spin_unlock ( & sbi - > work_lock ) ;
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 ) ;
2013-05-01 02:27:54 +04:00
hfs_dbg ( SUPER , " hfsplus_put_super \n " ) ;
2010-10-01 07:42:59 +04:00
2012-07-12 18:26:31 +04:00
cancel_delayed_work_sync ( & sbi - > sync_work ) ;
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
}
2013-02-28 05:03:04 +04:00
hfs_btree_close ( sbi - > attr_tree ) ;
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 ) ;
2011-07-18 19:06:23 +04:00
kfree ( sbi - > s_vhdr_buf ) ;
kfree ( sbi - > s_backup_vhdr_buf ) ;
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 )
{
2014-03-13 18:14:33 +04:00
sync_filesystem ( sb ) ;
2005-04-17 02:20:36 +04:00
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 ) ) ) {
2013-05-01 02:27:55 +04:00
pr_warn ( " 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 ) ) {
2013-05-01 02:27:55 +04:00
pr_warn ( " 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 ) ) {
2013-05-01 02:27:55 +04:00
pr_warn ( " 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 ,
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-09-15 18:48:40 +04:00
u64 last_fs_block , last_fs_page ;
2011-02-02 19:32:39 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2012-07-31 01:42:11 +04:00
err = - ENOMEM ;
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 ) ;
2012-07-12 18:26:31 +04:00
spin_lock_init ( & sbi - > work_lock ) ;
INIT_DELAYED_WORK ( & sbi - > sync_work , delayed_sync_fs ) ;
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 ) ) {
2013-05-01 02:27:55 +04:00
pr_err ( " 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 ) {
2013-05-01 02:27:55 +04:00
pr_err ( " 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 )
2013-05-01 02:27:55 +04:00
pr_warn ( " 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 ) {
2013-05-01 02:27:55 +04:00
pr_err ( " 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
2011-09-15 18:48:40 +04:00
err = - EFBIG ;
last_fs_block = sbi - > total_blocks - 1 ;
last_fs_page = ( last_fs_block < < sbi - > alloc_blksz_shift ) > >
PAGE_CACHE_SHIFT ;
if ( ( last_fs_block > ( sector_t ) ( ~ 0ULL ) > > ( sbi - > alloc_blksz_shift - 9 ) ) | |
( last_fs_page > ( pgoff_t ) ( ~ 0ULL ) ) ) {
2013-05-01 02:27:55 +04:00
pr_err ( " filesystem size too large \n " ) ;
2011-02-16 11:34:26 +03:00
goto out_free_vhdr ;
}
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 ) ) ) {
2013-05-01 02:27:55 +04:00
pr_warn ( " 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 ) ) {
2013-05-01 02:27:55 +04:00
pr_warn ( " 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 ) ) {
2013-05-01 02:27:55 +04:00
pr_warn ( " 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
}
2011-02-16 11:34:26 +03:00
err = - EINVAL ;
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 ) {
2013-05-01 02:27:55 +04:00
pr_err ( " 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 ) {
2013-05-01 02:27:55 +04:00
pr_err ( " 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
}
2013-11-13 03:11:09 +04:00
atomic_set ( & sbi - > attr_tree_state , HFSPLUS_EMPTY_ATTR_TREE ) ;
2013-02-28 05:03:04 +04:00
if ( vhdr - > attr_file . total_blocks ! = 0 ) {
sbi - > attr_tree = hfs_btree_open ( sb , HFSPLUS_ATTR_CNID ) ;
if ( ! sbi - > attr_tree ) {
2013-05-01 02:27:55 +04:00
pr_err ( " failed to load attributes file \n " ) ;
2013-02-28 05:03:04 +04:00
goto out_close_cat_tree ;
}
2013-11-13 03:11:09 +04:00
atomic_set ( & sbi - > attr_tree_state , HFSPLUS_VALID_ATTR_TREE ) ;
2013-02-28 05:03:04 +04:00
}
sb - > s_xattr = hfsplus_xattr_handlers ;
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 ) ) {
2013-05-01 02:27:55 +04:00
pr_err ( " failed to load allocation file \n " ) ;
2008-02-07 11:15:40 +03:00
err = PTR_ERR ( inode ) ;
2013-02-28 05:03:04 +04:00
goto out_close_attr_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 ) ) {
2013-05-01 02:27:55 +04:00
pr_err ( " failed to load root directory \n " ) ;
2008-02-07 11:15:40 +03:00
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
}
2012-02-13 07:07:07 +04:00
sb - > s_d_op = & hfsplus_dentry_operations ;
sb - > s_root = d_make_root ( root ) ;
if ( ! sb - > s_root ) {
err = - ENOMEM ;
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 ;
2011-07-06 02:29:59 +04:00
err = hfs_find_init ( sbi - > cat_tree , & fd ) ;
if ( err )
goto out_put_root ;
2014-12-19 03:17:12 +03:00
err = hfsplus_cat_build_key ( sb , fd . search_key , HFSPLUS_ROOT_CNID , & str ) ;
if ( unlikely ( err < 0 ) )
goto out_put_root ;
2005-04-17 02:20:36 +04:00
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 ) ;
2012-01-11 02:48:52 +04:00
if ( ! sbi - > hidden_dir ) {
mutex_unlock ( & sbi - > vh_mutex ) ;
err = - ENOMEM ;
goto out_put_root ;
}
err = hfsplus_create_cat ( sbi - > hidden_dir - > i_ino , root ,
& str , sbi - > hidden_dir ) ;
2013-02-28 05:03:04 +04:00
if ( err ) {
mutex_unlock ( & sbi - > vh_mutex ) ;
goto out_put_hidden_dir ;
}
err = hfsplus_init_inode_security ( sbi - > hidden_dir ,
root , & str ) ;
if ( err = = - EOPNOTSUPP )
err = 0 ; /* Operation is not supported. */
else if ( err ) {
/*
* Try to delete anyway without
* error analysis .
*/
hfsplus_delete_cat ( sbi - > hidden_dir - > i_ino ,
root , & str ) ;
mutex_unlock ( & sbi - > vh_mutex ) ;
2012-01-11 02:48:52 +04:00
goto out_put_hidden_dir ;
2013-02-28 05:03:04 +04:00
}
2011-02-02 19:32:39 +03:00
2013-02-28 05:03:04 +04:00
mutex_unlock ( & sbi - > vh_mutex ) ;
2011-02-02 19:32:39 +03:00
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
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 :
2012-02-13 07:07:07 +04:00
dput ( sb - > s_root ) ;
sb - > s_root = NULL ;
2011-02-02 19:32:39 +03:00
out_put_alloc_file :
iput ( sbi - > alloc_file ) ;
2013-02-28 05:03:04 +04:00
out_close_attr_tree :
hfs_btree_close ( sbi - > attr_tree ) ;
2011-02-02 19:32:39 +03:00
out_close_cat_tree :
hfs_btree_close ( sbi - > cat_tree ) ;
out_close_ext_tree :
hfs_btree_close ( sbi - > ext_tree ) ;
out_free_vhdr :
2011-09-15 18:48:27 +04:00
kfree ( sbi - > s_vhdr_buf ) ;
kfree ( sbi - > s_backup_vhdr_buf ) ;
2011-02-02 19:32:39 +03:00
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 ) ;
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 ,
} ;
2013-03-03 07:39:14 +04:00
MODULE_ALIAS_FS ( " hfsplus " ) ;
2005-04-17 02:20:36 +04:00
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 " ,
2016-01-15 02:18:21 +03:00
HFSPLUS_INODE_SIZE , 0 , SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT ,
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 ;
2013-02-28 05:03:04 +04:00
err = hfsplus_create_attr_tree_cache ( ) ;
if ( err )
goto destroy_inode_cache ;
2005-04-17 02:20:36 +04:00
err = register_filesystem ( & hfsplus_fs_type ) ;
if ( err )
2013-02-28 05:03:04 +04:00
goto destroy_attr_tree_cache ;
return 0 ;
destroy_attr_tree_cache :
hfsplus_destroy_attr_tree_cache ( ) ;
destroy_inode_cache :
kmem_cache_destroy ( hfsplus_inode_cachep ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
static void __exit exit_hfsplus_fs ( void )
{
unregister_filesystem ( & hfsplus_fs_type ) ;
2012-09-26 05:33:07 +04:00
/*
* Make sure all delayed rcu free inodes are flushed before we
* destroy cache .
*/
rcu_barrier ( ) ;
2013-02-28 05:03:04 +04:00
hfsplus_destroy_attr_tree_cache ( ) ;
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 )