2005-04-17 02:20:36 +04:00
/*
* super . c - NTFS kernel super block handling . Part of the Linux - NTFS project .
*
2005-02-03 15:02:56 +03:00
* Copyright ( c ) 2001 - 2005 Anton Altaparmakov
2005-04-17 02:20:36 +04:00
* Copyright ( c ) 2001 , 2002 Richard Russon
*
* This program / include file is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program / include file is distributed in the hope that it will be
* useful , but WITHOUT ANY WARRANTY ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ( in the main directory of the Linux - NTFS
* distribution in the file COPYING ) ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/stddef.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/spinlock.h>
# include <linux/blkdev.h> /* For bdev_hardsect_size(). */
# include <linux/backing-dev.h>
# include <linux/buffer_head.h>
# include <linux/vfs.h>
# include <linux/moduleparam.h>
# include <linux/smp_lock.h>
# include "sysctl.h"
# include "logfile.h"
# include "quota.h"
2005-06-25 18:28:56 +04:00
# include "usnjrnl.h"
2005-04-17 02:20:36 +04:00
# include "dir.h"
# include "debug.h"
# include "index.h"
# include "aops.h"
2005-04-04 19:20:14 +04:00
# include "layout.h"
2005-04-17 02:20:36 +04:00
# include "malloc.h"
# include "ntfs.h"
2005-02-03 15:02:56 +03:00
/* Number of mounted filesystems which have compression enabled. */
2005-04-17 02:20:36 +04:00
static unsigned long ntfs_nr_compression_users ;
/* A global default upcase table and a corresponding reference count. */
static ntfschar * default_upcase = NULL ;
static unsigned long ntfs_nr_upcase_users = 0 ;
/* Error constants/strings used in inode.c::ntfs_show_options(). */
typedef enum {
/* One of these must be present, default is ON_ERRORS_CONTINUE. */
ON_ERRORS_PANIC = 0x01 ,
ON_ERRORS_REMOUNT_RO = 0x02 ,
ON_ERRORS_CONTINUE = 0x04 ,
/* Optional, can be combined with any of the above. */
ON_ERRORS_RECOVER = 0x10 ,
} ON_ERRORS_ACTIONS ;
const option_t on_errors_arr [ ] = {
{ ON_ERRORS_PANIC , " panic " } ,
{ ON_ERRORS_REMOUNT_RO , " remount-ro " , } ,
{ ON_ERRORS_CONTINUE , " continue " , } ,
{ ON_ERRORS_RECOVER , " recover " } ,
{ 0 , NULL }
} ;
/**
* simple_getbool -
*
* Copied from old ntfs driver ( which copied from vfat driver ) .
*/
static int simple_getbool ( char * s , BOOL * setval )
{
if ( s ) {
if ( ! strcmp ( s , " 1 " ) | | ! strcmp ( s , " yes " ) | | ! strcmp ( s , " true " ) )
* setval = TRUE ;
else if ( ! strcmp ( s , " 0 " ) | | ! strcmp ( s , " no " ) | |
! strcmp ( s , " false " ) )
* setval = FALSE ;
else
return 0 ;
} else
* setval = TRUE ;
return 1 ;
}
/**
* parse_options - parse the ( re ) mount options
* @ vol : ntfs volume
* @ opt : string containing the ( re ) mount options
*
* Parse the recognized options in @ opt for the ntfs volume described by @ vol .
*/
static BOOL parse_options ( ntfs_volume * vol , char * opt )
{
char * p , * v , * ov ;
static char * utf8 = " utf8 " ;
int errors = 0 , sloppy = 0 ;
uid_t uid = ( uid_t ) - 1 ;
gid_t gid = ( gid_t ) - 1 ;
mode_t fmask = ( mode_t ) - 1 , dmask = ( mode_t ) - 1 ;
int mft_zone_multiplier = - 1 , on_errors = - 1 ;
2005-02-03 15:02:56 +03:00
int show_sys_files = - 1 , case_sensitive = - 1 , disable_sparse = - 1 ;
2005-04-17 02:20:36 +04:00
struct nls_table * nls_map = NULL , * old_nls ;
/* I am lazy... (-8 */
# define NTFS_GETOPT_WITH_DEFAULT(option, variable, default_value) \
if ( ! strcmp ( p , option ) ) { \
if ( ! v | | ! * v ) \
variable = default_value ; \
else { \
variable = simple_strtoul ( ov = v , & v , 0 ) ; \
if ( * v ) \
goto needs_val ; \
} \
}
# define NTFS_GETOPT(option, variable) \
if ( ! strcmp ( p , option ) ) { \
if ( ! v | | ! * v ) \
goto needs_arg ; \
variable = simple_strtoul ( ov = v , & v , 0 ) ; \
if ( * v ) \
goto needs_val ; \
}
# define NTFS_GETOPT_BOOL(option, variable) \
if ( ! strcmp ( p , option ) ) { \
BOOL val ; \
if ( ! simple_getbool ( v , & val ) ) \
goto needs_bool ; \
variable = val ; \
}
# define NTFS_GETOPT_OPTIONS_ARRAY(option, variable, opt_array) \
if ( ! strcmp ( p , option ) ) { \
int _i ; \
if ( ! v | | ! * v ) \
goto needs_arg ; \
ov = v ; \
if ( variable = = - 1 ) \
variable = 0 ; \
for ( _i = 0 ; opt_array [ _i ] . str & & * opt_array [ _i ] . str ; _i + + ) \
if ( ! strcmp ( opt_array [ _i ] . str , v ) ) { \
variable | = opt_array [ _i ] . val ; \
break ; \
} \
if ( ! opt_array [ _i ] . str | | ! * opt_array [ _i ] . str ) \
goto needs_val ; \
}
if ( ! opt | | ! * opt )
goto no_mount_options ;
ntfs_debug ( " Entering with mount options string: %s " , opt ) ;
while ( ( p = strsep ( & opt , " , " ) ) ) {
if ( ( v = strchr ( p , ' = ' ) ) )
* v + + = 0 ;
NTFS_GETOPT ( " uid " , uid )
else NTFS_GETOPT ( " gid " , gid )
else NTFS_GETOPT ( " umask " , fmask = dmask )
else NTFS_GETOPT ( " fmask " , fmask )
else NTFS_GETOPT ( " dmask " , dmask )
else NTFS_GETOPT ( " mft_zone_multiplier " , mft_zone_multiplier )
else NTFS_GETOPT_WITH_DEFAULT ( " sloppy " , sloppy , TRUE )
else NTFS_GETOPT_BOOL ( " show_sys_files " , show_sys_files )
else NTFS_GETOPT_BOOL ( " case_sensitive " , case_sensitive )
2005-02-03 15:02:56 +03:00
else NTFS_GETOPT_BOOL ( " disable_sparse " , disable_sparse )
2005-04-17 02:20:36 +04:00
else NTFS_GETOPT_OPTIONS_ARRAY ( " errors " , on_errors ,
on_errors_arr )
else if ( ! strcmp ( p , " posix " ) | | ! strcmp ( p , " show_inodes " ) )
ntfs_warning ( vol - > sb , " Ignoring obsolete option %s. " ,
p ) ;
else if ( ! strcmp ( p , " nls " ) | | ! strcmp ( p , " iocharset " ) ) {
if ( ! strcmp ( p , " iocharset " ) )
ntfs_warning ( vol - > sb , " Option iocharset is "
" deprecated. Please use "
" option nls=<charsetname> in "
" the future. " ) ;
if ( ! v | | ! * v )
goto needs_arg ;
use_utf8 :
old_nls = nls_map ;
nls_map = load_nls ( v ) ;
if ( ! nls_map ) {
if ( ! old_nls ) {
ntfs_error ( vol - > sb , " NLS character set "
" %s not found. " , v ) ;
return FALSE ;
}
ntfs_error ( vol - > sb , " NLS character set %s not "
" found. Using previous one %s. " ,
v , old_nls - > charset ) ;
nls_map = old_nls ;
} else /* nls_map */ {
if ( old_nls )
unload_nls ( old_nls ) ;
}
} else if ( ! strcmp ( p , " utf8 " ) ) {
BOOL val = FALSE ;
ntfs_warning ( vol - > sb , " Option utf8 is no longer "
" supported, using option nls=utf8. Please "
" use option nls=utf8 in the future and "
" make sure utf8 is compiled either as a "
" module or into the kernel. " ) ;
if ( ! v | | ! * v )
val = TRUE ;
else if ( ! simple_getbool ( v , & val ) )
goto needs_bool ;
if ( val ) {
v = utf8 ;
goto use_utf8 ;
}
} else {
ntfs_error ( vol - > sb , " Unrecognized mount option %s. " , p ) ;
if ( errors < INT_MAX )
errors + + ;
}
# undef NTFS_GETOPT_OPTIONS_ARRAY
# undef NTFS_GETOPT_BOOL
# undef NTFS_GETOPT
# undef NTFS_GETOPT_WITH_DEFAULT
}
no_mount_options :
if ( errors & & ! sloppy )
return FALSE ;
if ( sloppy )
ntfs_warning ( vol - > sb , " Sloppy option given. Ignoring "
" unrecognized mount option(s) and continuing. " ) ;
/* Keep this first! */
if ( on_errors ! = - 1 ) {
if ( ! on_errors ) {
ntfs_error ( vol - > sb , " Invalid errors option argument "
" or bug in options parser. " ) ;
return FALSE ;
}
}
if ( nls_map ) {
if ( vol - > nls_map & & vol - > nls_map ! = nls_map ) {
ntfs_error ( vol - > sb , " Cannot change NLS character set "
" on remount. " ) ;
return FALSE ;
} /* else (!vol->nls_map) */
ntfs_debug ( " Using NLS character set %s. " , nls_map - > charset ) ;
vol - > nls_map = nls_map ;
} else /* (!nls_map) */ {
if ( ! vol - > nls_map ) {
vol - > nls_map = load_nls_default ( ) ;
if ( ! vol - > nls_map ) {
ntfs_error ( vol - > sb , " Failed to load default "
" NLS character set. " ) ;
return FALSE ;
}
ntfs_debug ( " Using default NLS character set (%s). " ,
vol - > nls_map - > charset ) ;
}
}
if ( mft_zone_multiplier ! = - 1 ) {
if ( vol - > mft_zone_multiplier & & vol - > mft_zone_multiplier ! =
mft_zone_multiplier ) {
ntfs_error ( vol - > sb , " Cannot change mft_zone_multiplier "
" on remount. " ) ;
return FALSE ;
}
if ( mft_zone_multiplier < 1 | | mft_zone_multiplier > 4 ) {
ntfs_error ( vol - > sb , " Invalid mft_zone_multiplier. "
" Using default value, i.e. 1. " ) ;
mft_zone_multiplier = 1 ;
}
vol - > mft_zone_multiplier = mft_zone_multiplier ;
}
if ( ! vol - > mft_zone_multiplier )
vol - > mft_zone_multiplier = 1 ;
if ( on_errors ! = - 1 )
vol - > on_errors = on_errors ;
if ( ! vol - > on_errors | | vol - > on_errors = = ON_ERRORS_RECOVER )
vol - > on_errors | = ON_ERRORS_CONTINUE ;
if ( uid ! = ( uid_t ) - 1 )
vol - > uid = uid ;
if ( gid ! = ( gid_t ) - 1 )
vol - > gid = gid ;
if ( fmask ! = ( mode_t ) - 1 )
vol - > fmask = fmask ;
if ( dmask ! = ( mode_t ) - 1 )
vol - > dmask = dmask ;
if ( show_sys_files ! = - 1 ) {
if ( show_sys_files )
NVolSetShowSystemFiles ( vol ) ;
else
NVolClearShowSystemFiles ( vol ) ;
}
if ( case_sensitive ! = - 1 ) {
if ( case_sensitive )
NVolSetCaseSensitive ( vol ) ;
else
NVolClearCaseSensitive ( vol ) ;
}
2005-02-03 15:02:56 +03:00
if ( disable_sparse ! = - 1 ) {
if ( disable_sparse )
NVolClearSparseEnabled ( vol ) ;
else {
if ( ! NVolSparseEnabled ( vol ) & &
vol - > major_ver & & vol - > major_ver < 3 )
ntfs_warning ( vol - > sb , " Not enabling sparse "
" support due to NTFS volume "
" version %i.%i (need at least "
" version 3.0). " , vol - > major_ver ,
vol - > minor_ver ) ;
else
NVolSetSparseEnabled ( vol ) ;
}
}
2005-04-17 02:20:36 +04:00
return TRUE ;
needs_arg :
ntfs_error ( vol - > sb , " The %s option requires an argument. " , p ) ;
return FALSE ;
needs_bool :
ntfs_error ( vol - > sb , " The %s option requires a boolean argument. " , p ) ;
return FALSE ;
needs_val :
ntfs_error ( vol - > sb , " Invalid %s option argument: %s " , p , ov ) ;
return FALSE ;
}
# ifdef NTFS_RW
/**
* ntfs_write_volume_flags - write new flags to the volume information flags
* @ vol : ntfs volume on which to modify the flags
* @ flags : new flags value for the volume information flags
*
* Internal function . You probably want to use ntfs_ { set , clear } _volume_flags ( )
* instead ( see below ) .
*
* Replace the volume information flags on the volume @ vol with the value
* supplied in @ flags . Note , this overwrites the volume information flags , so
* make sure to combine the flags you want to modify with the old flags and use
* the result when calling ntfs_write_volume_flags ( ) .
*
* Return 0 on success and - errno on error .
*/
static int ntfs_write_volume_flags ( ntfs_volume * vol , const VOLUME_FLAGS flags )
{
ntfs_inode * ni = NTFS_I ( vol - > vol_ino ) ;
MFT_RECORD * m ;
VOLUME_INFORMATION * vi ;
ntfs_attr_search_ctx * ctx ;
int err ;
ntfs_debug ( " Entering, old flags = 0x%x, new flags = 0x%x. " ,
le16_to_cpu ( vol - > vol_flags ) , le16_to_cpu ( flags ) ) ;
if ( vol - > vol_flags = = flags )
goto done ;
BUG_ON ( ! ni ) ;
m = map_mft_record ( ni ) ;
if ( IS_ERR ( m ) ) {
err = PTR_ERR ( m ) ;
goto err_out ;
}
ctx = ntfs_attr_get_search_ctx ( ni , m ) ;
if ( ! ctx ) {
err = - ENOMEM ;
goto put_unm_err_out ;
}
err = ntfs_attr_lookup ( AT_VOLUME_INFORMATION , NULL , 0 , 0 , 0 , NULL , 0 ,
ctx ) ;
if ( err )
goto put_unm_err_out ;
vi = ( VOLUME_INFORMATION * ) ( ( u8 * ) ctx - > attr +
le16_to_cpu ( ctx - > attr - > data . resident . value_offset ) ) ;
vol - > vol_flags = vi - > flags = flags ;
flush_dcache_mft_record_page ( ctx - > ntfs_ino ) ;
mark_mft_record_dirty ( ctx - > ntfs_ino ) ;
ntfs_attr_put_search_ctx ( ctx ) ;
unmap_mft_record ( ni ) ;
done :
ntfs_debug ( " Done. " ) ;
return 0 ;
put_unm_err_out :
if ( ctx )
ntfs_attr_put_search_ctx ( ctx ) ;
unmap_mft_record ( ni ) ;
err_out :
ntfs_error ( vol - > sb , " Failed with error code %i. " , - err ) ;
return err ;
}
/**
* ntfs_set_volume_flags - set bits in the volume information flags
* @ vol : ntfs volume on which to modify the flags
* @ flags : flags to set on the volume
*
* Set the bits in @ flags in the volume information flags on the volume @ vol .
*
* Return 0 on success and - errno on error .
*/
static inline int ntfs_set_volume_flags ( ntfs_volume * vol , VOLUME_FLAGS flags )
{
flags & = VOLUME_FLAGS_MASK ;
return ntfs_write_volume_flags ( vol , vol - > vol_flags | flags ) ;
}
/**
* ntfs_clear_volume_flags - clear bits in the volume information flags
* @ vol : ntfs volume on which to modify the flags
* @ flags : flags to clear on the volume
*
* Clear the bits in @ flags in the volume information flags on the volume @ vol .
*
* Return 0 on success and - errno on error .
*/
static inline int ntfs_clear_volume_flags ( ntfs_volume * vol , VOLUME_FLAGS flags )
{
flags & = VOLUME_FLAGS_MASK ;
flags = vol - > vol_flags & cpu_to_le16 ( ~ le16_to_cpu ( flags ) ) ;
return ntfs_write_volume_flags ( vol , flags ) ;
}
# endif /* NTFS_RW */
/**
* ntfs_remount - change the mount options of a mounted ntfs filesystem
* @ sb : superblock of mounted ntfs filesystem
* @ flags : remount flags
* @ opt : remount options string
*
* Change the mount options of an already mounted ntfs filesystem .
*
* NOTE : The VFS sets the @ sb - > s_flags remount flags to @ flags after
* ntfs_remount ( ) returns successfully ( i . e . returns 0 ) . Otherwise ,
* @ sb - > s_flags are not changed .
*/
static int ntfs_remount ( struct super_block * sb , int * flags , char * opt )
{
ntfs_volume * vol = NTFS_SB ( sb ) ;
ntfs_debug ( " Entering with remount options string: %s " , opt ) ;
# ifndef NTFS_RW
/* For read-only compiled driver, enforce all read-only flags. */
* flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
# else /* NTFS_RW */
/*
* For the read - write compiled driver , if we are remounting read - write ,
* make sure there are no volume errors and that no unsupported volume
* flags are set . Also , empty the logfile journal as it would become
* stale as soon as something is written to the volume and mark the
* volume dirty so that chkdsk is run if the volume is not umounted
* cleanly . Finally , mark the quotas out of date so Windows rescans
* the volume on boot and updates them .
*
* When remounting read - only , mark the volume clean if no volume errors
* have occured .
*/
if ( ( sb - > s_flags & MS_RDONLY ) & & ! ( * flags & MS_RDONLY ) ) {
static const char * es = " . Cannot remount read-write. " ;
/* Remounting read-write. */
if ( NVolErrors ( vol ) ) {
ntfs_error ( sb , " Volume has errors and is read-only%s " ,
es ) ;
return - EROFS ;
}
if ( vol - > vol_flags & VOLUME_IS_DIRTY ) {
ntfs_error ( sb , " Volume is dirty and read-only%s " , es ) ;
return - EROFS ;
}
if ( vol - > vol_flags & VOLUME_MUST_MOUNT_RO_MASK ) {
ntfs_error ( sb , " Volume has unsupported flags set and "
" is read-only%s " , es ) ;
return - EROFS ;
}
if ( ntfs_set_volume_flags ( vol , VOLUME_IS_DIRTY ) ) {
ntfs_error ( sb , " Failed to set dirty bit in volume "
" information flags%s " , es ) ;
return - EROFS ;
}
#if 0
// TODO: Enable this code once we start modifying anything that
// is different between NTFS 1.2 and 3.x...
/* Set NT4 compatibility flag on newer NTFS version volumes. */
if ( ( vol - > major_ver > 1 ) ) {
if ( ntfs_set_volume_flags ( vol , VOLUME_MOUNTED_ON_NT4 ) ) {
ntfs_error ( sb , " Failed to set NT4 "
" compatibility flag%s " , es ) ;
NVolSetErrors ( vol ) ;
return - EROFS ;
}
}
# endif
if ( ! ntfs_empty_logfile ( vol - > logfile_ino ) ) {
ntfs_error ( sb , " Failed to empty journal $LogFile%s " ,
es ) ;
NVolSetErrors ( vol ) ;
return - EROFS ;
}
if ( ! ntfs_mark_quotas_out_of_date ( vol ) ) {
ntfs_error ( sb , " Failed to mark quotas out of date%s " ,
es ) ;
NVolSetErrors ( vol ) ;
return - EROFS ;
}
2005-06-25 18:28:56 +04:00
if ( ! ntfs_stamp_usnjrnl ( vol ) ) {
ntfs_error ( sb , " Failed to stamp transation log "
" ($UsnJrnl)%s " , es ) ;
NVolSetErrors ( vol ) ;
return - EROFS ;
}
2005-04-17 02:20:36 +04:00
} else if ( ! ( sb - > s_flags & MS_RDONLY ) & & ( * flags & MS_RDONLY ) ) {
/* Remounting read-only. */
if ( ! NVolErrors ( vol ) ) {
if ( ntfs_clear_volume_flags ( vol , VOLUME_IS_DIRTY ) )
ntfs_warning ( sb , " Failed to clear dirty bit "
" in volume information "
" flags. Run chkdsk. " ) ;
}
}
# endif /* NTFS_RW */
// TODO: Deal with *flags.
if ( ! parse_options ( vol , opt ) )
return - EINVAL ;
ntfs_debug ( " Done. " ) ;
return 0 ;
}
/**
* is_boot_sector_ntfs - check whether a boot sector is a valid NTFS boot sector
* @ sb : Super block of the device to which @ b belongs .
* @ b : Boot sector of device @ sb to check .
* @ silent : If TRUE , all output will be silenced .
*
* is_boot_sector_ntfs ( ) checks whether the boot sector @ b is a valid NTFS boot
* sector . Returns TRUE if it is valid and FALSE if not .
*
* @ sb is only needed for warning / error output , i . e . it can be NULL when silent
* is TRUE .
*/
static BOOL is_boot_sector_ntfs ( const struct super_block * sb ,
const NTFS_BOOT_SECTOR * b , const BOOL silent )
{
/*
* Check that checksum = = sum of u32 values from b to the checksum
2005-04-04 19:20:14 +04:00
* field . If checksum is zero , no checking is done . We will work when
* the checksum test fails , since some utilities update the boot sector
* ignoring the checksum which leaves the checksum out - of - date . We
* report a warning if this is the case .
2005-04-17 02:20:36 +04:00
*/
2005-04-04 19:20:14 +04:00
if ( ( void * ) b < ( void * ) & b - > checksum & & b - > checksum & & ! silent ) {
2005-04-17 02:20:36 +04:00
le32 * u ;
u32 i ;
for ( i = 0 , u = ( le32 * ) b ; u < ( le32 * ) ( & b - > checksum ) ; + + u )
i + = le32_to_cpup ( u ) ;
if ( le32_to_cpu ( b - > checksum ) ! = i )
2005-04-04 19:20:14 +04:00
ntfs_warning ( sb , " Invalid boot sector checksum. " ) ;
2005-04-17 02:20:36 +04:00
}
/* Check OEMidentifier is "NTFS " */
if ( b - > oem_id ! = magicNTFS )
goto not_ntfs ;
/* Check bytes per sector value is between 256 and 4096. */
if ( le16_to_cpu ( b - > bpb . bytes_per_sector ) < 0x100 | |
le16_to_cpu ( b - > bpb . bytes_per_sector ) > 0x1000 )
goto not_ntfs ;
/* Check sectors per cluster value is valid. */
switch ( b - > bpb . sectors_per_cluster ) {
case 1 : case 2 : case 4 : case 8 : case 16 : case 32 : case 64 : case 128 :
break ;
default :
goto not_ntfs ;
}
2005-04-06 19:09:21 +04:00
/* Check the cluster size is not above the maximum (64kiB). */
2005-04-17 02:20:36 +04:00
if ( ( u32 ) le16_to_cpu ( b - > bpb . bytes_per_sector ) *
2005-04-06 19:09:21 +04:00
b - > bpb . sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE )
2005-04-17 02:20:36 +04:00
goto not_ntfs ;
/* Check reserved/unused fields are really zero. */
if ( le16_to_cpu ( b - > bpb . reserved_sectors ) | |
le16_to_cpu ( b - > bpb . root_entries ) | |
le16_to_cpu ( b - > bpb . sectors ) | |
le16_to_cpu ( b - > bpb . sectors_per_fat ) | |
le32_to_cpu ( b - > bpb . large_sectors ) | | b - > bpb . fats )
goto not_ntfs ;
/* Check clusters per file mft record value is valid. */
if ( ( u8 ) b - > clusters_per_mft_record < 0xe1 | |
( u8 ) b - > clusters_per_mft_record > 0xf7 )
switch ( b - > clusters_per_mft_record ) {
case 1 : case 2 : case 4 : case 8 : case 16 : case 32 : case 64 :
break ;
default :
goto not_ntfs ;
}
/* Check clusters per index block value is valid. */
if ( ( u8 ) b - > clusters_per_index_record < 0xe1 | |
( u8 ) b - > clusters_per_index_record > 0xf7 )
switch ( b - > clusters_per_index_record ) {
case 1 : case 2 : case 4 : case 8 : case 16 : case 32 : case 64 :
break ;
default :
goto not_ntfs ;
}
/*
* Check for valid end of sector marker . We will work without it , but
* many BIOSes will refuse to boot from a bootsector if the magic is
* incorrect , so we emit a warning .
*/
2005-04-04 19:20:14 +04:00
if ( ! silent & & b - > end_of_sector_marker ! = const_cpu_to_le16 ( 0xaa55 ) )
2005-04-17 02:20:36 +04:00
ntfs_warning ( sb , " Invalid end of sector marker. " ) ;
return TRUE ;
not_ntfs :
return FALSE ;
}
/**
* read_ntfs_boot_sector - read the NTFS boot sector of a device
* @ sb : super block of device to read the boot sector from
* @ silent : if true , suppress all output
*
* Reads the boot sector from the device and validates it . If that fails , tries
* to read the backup boot sector , first from the end of the device a - la NT4 and
* later and then from the middle of the device a - la NT3 .51 and before .
*
* If a valid boot sector is found but it is not the primary boot sector , we
* repair the primary boot sector silently ( unless the device is read - only or
* the primary boot sector is not accessible ) .
*
* NOTE : To call this function , @ sb must have the fields s_dev , the ntfs super
* block ( u . ntfs_sb ) , nr_blocks and the device flags ( s_flags ) initialized
* to their respective values .
*
* Return the unlocked buffer head containing the boot sector or NULL on error .
*/
static struct buffer_head * read_ntfs_boot_sector ( struct super_block * sb ,
const int silent )
{
const char * read_err_str = " Unable to read %s boot sector. " ;
struct buffer_head * bh_primary , * bh_backup ;
long nr_blocks = NTFS_SB ( sb ) - > nr_blocks ;
/* Try to read primary boot sector. */
if ( ( bh_primary = sb_bread ( sb , 0 ) ) ) {
if ( is_boot_sector_ntfs ( sb , ( NTFS_BOOT_SECTOR * )
bh_primary - > b_data , silent ) )
return bh_primary ;
if ( ! silent )
ntfs_error ( sb , " Primary boot sector is invalid. " ) ;
} else if ( ! silent )
ntfs_error ( sb , read_err_str , " primary " ) ;
if ( ! ( NTFS_SB ( sb ) - > on_errors & ON_ERRORS_RECOVER ) ) {
if ( bh_primary )
brelse ( bh_primary ) ;
if ( ! silent )
ntfs_error ( sb , " Mount option errors=recover not used. "
" Aborting without trying to recover. " ) ;
return NULL ;
}
/* Try to read NT4+ backup boot sector. */
if ( ( bh_backup = sb_bread ( sb , nr_blocks - 1 ) ) ) {
if ( is_boot_sector_ntfs ( sb , ( NTFS_BOOT_SECTOR * )
bh_backup - > b_data , silent ) )
goto hotfix_primary_boot_sector ;
brelse ( bh_backup ) ;
} else if ( ! silent )
ntfs_error ( sb , read_err_str , " backup " ) ;
/* Try to read NT3.51- backup boot sector. */
if ( ( bh_backup = sb_bread ( sb , nr_blocks > > 1 ) ) ) {
if ( is_boot_sector_ntfs ( sb , ( NTFS_BOOT_SECTOR * )
bh_backup - > b_data , silent ) )
goto hotfix_primary_boot_sector ;
if ( ! silent )
ntfs_error ( sb , " Could not find a valid backup boot "
" sector. " ) ;
brelse ( bh_backup ) ;
} else if ( ! silent )
ntfs_error ( sb , read_err_str , " backup " ) ;
/* We failed. Cleanup and return. */
if ( bh_primary )
brelse ( bh_primary ) ;
return NULL ;
hotfix_primary_boot_sector :
if ( bh_primary ) {
/*
* If we managed to read sector zero and the volume is not
* read - only , copy the found , valid backup boot sector to the
* primary boot sector .
*/
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
ntfs_warning ( sb , " Hot-fix: Recovering invalid primary "
" boot sector from backup copy. " ) ;
memcpy ( bh_primary - > b_data , bh_backup - > b_data ,
sb - > s_blocksize ) ;
mark_buffer_dirty ( bh_primary ) ;
sync_dirty_buffer ( bh_primary ) ;
if ( buffer_uptodate ( bh_primary ) ) {
brelse ( bh_backup ) ;
return bh_primary ;
}
ntfs_error ( sb , " Hot-fix: Device write error while "
" recovering primary boot sector. " ) ;
} else {
ntfs_warning ( sb , " Hot-fix: Recovery of primary boot "
" sector failed: Read-only mount. " ) ;
}
brelse ( bh_primary ) ;
}
ntfs_warning ( sb , " Using backup boot sector. " ) ;
return bh_backup ;
}
/**
* parse_ntfs_boot_sector - parse the boot sector and store the data in @ vol
* @ vol : volume structure to initialise with data from boot sector
* @ b : boot sector to parse
*
* Parse the ntfs boot sector @ b and store all imporant information therein in
* the ntfs super block @ vol . Return TRUE on success and FALSE on error .
*/
static BOOL parse_ntfs_boot_sector ( ntfs_volume * vol , const NTFS_BOOT_SECTOR * b )
{
unsigned int sectors_per_cluster_bits , nr_hidden_sects ;
int clusters_per_mft_record , clusters_per_index_record ;
s64 ll ;
vol - > sector_size = le16_to_cpu ( b - > bpb . bytes_per_sector ) ;
vol - > sector_size_bits = ffs ( vol - > sector_size ) - 1 ;
ntfs_debug ( " vol->sector_size = %i (0x%x) " , vol - > sector_size ,
vol - > sector_size ) ;
ntfs_debug ( " vol->sector_size_bits = %i (0x%x) " , vol - > sector_size_bits ,
vol - > sector_size_bits ) ;
if ( vol - > sector_size ! = vol - > sb - > s_blocksize )
ntfs_warning ( vol - > sb , " The boot sector indicates a sector size "
" different from the device sector size. " ) ;
ntfs_debug ( " sectors_per_cluster = 0x%x " , b - > bpb . sectors_per_cluster ) ;
sectors_per_cluster_bits = ffs ( b - > bpb . sectors_per_cluster ) - 1 ;
ntfs_debug ( " sectors_per_cluster_bits = 0x%x " ,
sectors_per_cluster_bits ) ;
nr_hidden_sects = le32_to_cpu ( b - > bpb . hidden_sectors ) ;
ntfs_debug ( " number of hidden sectors = 0x%x " , nr_hidden_sects ) ;
vol - > cluster_size = vol - > sector_size < < sectors_per_cluster_bits ;
vol - > cluster_size_mask = vol - > cluster_size - 1 ;
vol - > cluster_size_bits = ffs ( vol - > cluster_size ) - 1 ;
ntfs_debug ( " vol->cluster_size = %i (0x%x) " , vol - > cluster_size ,
vol - > cluster_size ) ;
ntfs_debug ( " vol->cluster_size_mask = 0x%x " , vol - > cluster_size_mask ) ;
ntfs_debug ( " vol->cluster_size_bits = %i (0x%x) " ,
vol - > cluster_size_bits , vol - > cluster_size_bits ) ;
if ( vol - > sector_size > vol - > cluster_size ) {
ntfs_error ( vol - > sb , " Sector sizes above the cluster size are "
" not supported. Sorry. " ) ;
return FALSE ;
}
if ( vol - > sb - > s_blocksize > vol - > cluster_size ) {
ntfs_error ( vol - > sb , " Cluster sizes smaller than the device "
" sector size are not supported. Sorry. " ) ;
return FALSE ;
}
clusters_per_mft_record = b - > clusters_per_mft_record ;
ntfs_debug ( " clusters_per_mft_record = %i (0x%x) " ,
clusters_per_mft_record , clusters_per_mft_record ) ;
if ( clusters_per_mft_record > 0 )
vol - > mft_record_size = vol - > cluster_size < <
( ffs ( clusters_per_mft_record ) - 1 ) ;
else
/*
* When mft_record_size < cluster_size , clusters_per_mft_record
* = - log2 ( mft_record_size ) bytes . mft_record_size normaly is
* 1024 bytes , which is encoded as 0xF6 ( - 10 in decimal ) .
*/
vol - > mft_record_size = 1 < < - clusters_per_mft_record ;
vol - > mft_record_size_mask = vol - > mft_record_size - 1 ;
vol - > mft_record_size_bits = ffs ( vol - > mft_record_size ) - 1 ;
ntfs_debug ( " vol->mft_record_size = %i (0x%x) " , vol - > mft_record_size ,
vol - > mft_record_size ) ;
ntfs_debug ( " vol->mft_record_size_mask = 0x%x " ,
vol - > mft_record_size_mask ) ;
ntfs_debug ( " vol->mft_record_size_bits = %i (0x%x) " ,
vol - > mft_record_size_bits , vol - > mft_record_size_bits ) ;
/*
* We cannot support mft record sizes above the PAGE_CACHE_SIZE since
* we store $ MFT / $ DATA , the table of mft records in the page cache .
*/
if ( vol - > mft_record_size > PAGE_CACHE_SIZE ) {
ntfs_error ( vol - > sb , " Mft record size %i (0x%x) exceeds the "
" page cache size on your system %lu (0x%lx). "
" This is not supported. Sorry. " ,
vol - > mft_record_size , vol - > mft_record_size ,
PAGE_CACHE_SIZE , PAGE_CACHE_SIZE ) ;
return FALSE ;
}
clusters_per_index_record = b - > clusters_per_index_record ;
ntfs_debug ( " clusters_per_index_record = %i (0x%x) " ,
clusters_per_index_record , clusters_per_index_record ) ;
if ( clusters_per_index_record > 0 )
vol - > index_record_size = vol - > cluster_size < <
( ffs ( clusters_per_index_record ) - 1 ) ;
else
/*
* When index_record_size < cluster_size ,
* clusters_per_index_record = - log2 ( index_record_size ) bytes .
* index_record_size normaly equals 4096 bytes , which is
* encoded as 0xF4 ( - 12 in decimal ) .
*/
vol - > index_record_size = 1 < < - clusters_per_index_record ;
vol - > index_record_size_mask = vol - > index_record_size - 1 ;
vol - > index_record_size_bits = ffs ( vol - > index_record_size ) - 1 ;
ntfs_debug ( " vol->index_record_size = %i (0x%x) " ,
vol - > index_record_size , vol - > index_record_size ) ;
ntfs_debug ( " vol->index_record_size_mask = 0x%x " ,
vol - > index_record_size_mask ) ;
ntfs_debug ( " vol->index_record_size_bits = %i (0x%x) " ,
vol - > index_record_size_bits ,
vol - > index_record_size_bits ) ;
/*
* Get the size of the volume in clusters and check for 64 - bit - ness .
* Windows currently only uses 32 bits to save the clusters so we do
* the same as it is much faster on 32 - bit CPUs .
*/
ll = sle64_to_cpu ( b - > number_of_sectors ) > > sectors_per_cluster_bits ;
if ( ( u64 ) ll > = 1ULL < < 32 ) {
ntfs_error ( vol - > sb , " Cannot handle 64-bit clusters. Sorry. " ) ;
return FALSE ;
}
vol - > nr_clusters = ll ;
ntfs_debug ( " vol->nr_clusters = 0x%llx " , ( long long ) vol - > nr_clusters ) ;
/*
* On an architecture where unsigned long is 32 - bits , we restrict the
* volume size to 2 TiB ( 2 ^ 41 ) . On a 64 - bit architecture , the compiler
* will hopefully optimize the whole check away .
*/
if ( sizeof ( unsigned long ) < 8 ) {
if ( ( ll < < vol - > cluster_size_bits ) > = ( 1ULL < < 41 ) ) {
ntfs_error ( vol - > sb , " Volume size (%lluTiB) is too "
" large for this architecture. "
" Maximum supported is 2TiB. Sorry. " ,
( unsigned long long ) ll > > ( 40 -
vol - > cluster_size_bits ) ) ;
return FALSE ;
}
}
ll = sle64_to_cpu ( b - > mft_lcn ) ;
if ( ll > = vol - > nr_clusters ) {
ntfs_error ( vol - > sb , " MFT LCN is beyond end of volume. Weird. " ) ;
return FALSE ;
}
vol - > mft_lcn = ll ;
ntfs_debug ( " vol->mft_lcn = 0x%llx " , ( long long ) vol - > mft_lcn ) ;
ll = sle64_to_cpu ( b - > mftmirr_lcn ) ;
if ( ll > = vol - > nr_clusters ) {
ntfs_error ( vol - > sb , " MFTMirr LCN is beyond end of volume. "
" Weird. " ) ;
return FALSE ;
}
vol - > mftmirr_lcn = ll ;
ntfs_debug ( " vol->mftmirr_lcn = 0x%llx " , ( long long ) vol - > mftmirr_lcn ) ;
# ifdef NTFS_RW
/*
* Work out the size of the mft mirror in number of mft records . If the
* cluster size is less than or equal to the size taken by four mft
* records , the mft mirror stores the first four mft records . If the
* cluster size is bigger than the size taken by four mft records , the
* mft mirror contains as many mft records as will fit into one
* cluster .
*/
if ( vol - > cluster_size < = ( 4 < < vol - > mft_record_size_bits ) )
vol - > mftmirr_size = 4 ;
else
vol - > mftmirr_size = vol - > cluster_size > >
vol - > mft_record_size_bits ;
ntfs_debug ( " vol->mftmirr_size = %i " , vol - > mftmirr_size ) ;
# endif /* NTFS_RW */
vol - > serial_no = le64_to_cpu ( b - > volume_serial_number ) ;
ntfs_debug ( " vol->serial_no = 0x%llx " ,
( unsigned long long ) vol - > serial_no ) ;
return TRUE ;
}
/**
* ntfs_setup_allocators - initialize the cluster and mft allocators
* @ vol : volume structure for which to setup the allocators
*
* Setup the cluster ( lcn ) and mft allocators to the starting values .
*/
static void ntfs_setup_allocators ( ntfs_volume * vol )
{
# ifdef NTFS_RW
LCN mft_zone_size , mft_lcn ;
# endif /* NTFS_RW */
ntfs_debug ( " vol->mft_zone_multiplier = 0x%x " ,
vol - > mft_zone_multiplier ) ;
# ifdef NTFS_RW
/* Determine the size of the MFT zone. */
mft_zone_size = vol - > nr_clusters ;
switch ( vol - > mft_zone_multiplier ) { /* % of volume size in clusters */
case 4 :
mft_zone_size > > = 1 ; /* 50% */
break ;
case 3 :
mft_zone_size = ( mft_zone_size +
( mft_zone_size > > 1 ) ) > > 2 ; /* 37.5% */
break ;
case 2 :
mft_zone_size > > = 2 ; /* 25% */
break ;
/* case 1: */
default :
mft_zone_size > > = 3 ; /* 12.5% */
break ;
}
/* Setup the mft zone. */
vol - > mft_zone_start = vol - > mft_zone_pos = vol - > mft_lcn ;
ntfs_debug ( " vol->mft_zone_pos = 0x%llx " ,
( unsigned long long ) vol - > mft_zone_pos ) ;
/*
* Calculate the mft_lcn for an unmodified NTFS volume ( see mkntfs
* source ) and if the actual mft_lcn is in the expected place or even
* further to the front of the volume , extend the mft_zone to cover the
* beginning of the volume as well . This is in order to protect the
* area reserved for the mft bitmap as well within the mft_zone itself .
* On non - standard volumes we do not protect it as the overhead would
* be higher than the speed increase we would get by doing it .
*/
mft_lcn = ( 8192 + 2 * vol - > cluster_size - 1 ) / vol - > cluster_size ;
if ( mft_lcn * vol - > cluster_size < 16 * 1024 )
mft_lcn = ( 16 * 1024 + vol - > cluster_size - 1 ) /
vol - > cluster_size ;
if ( vol - > mft_zone_start < = mft_lcn )
vol - > mft_zone_start = 0 ;
ntfs_debug ( " vol->mft_zone_start = 0x%llx " ,
( unsigned long long ) vol - > mft_zone_start ) ;
/*
* Need to cap the mft zone on non - standard volumes so that it does
* not point outside the boundaries of the volume . We do this by
* halving the zone size until we are inside the volume .
*/
vol - > mft_zone_end = vol - > mft_lcn + mft_zone_size ;
while ( vol - > mft_zone_end > = vol - > nr_clusters ) {
mft_zone_size > > = 1 ;
vol - > mft_zone_end = vol - > mft_lcn + mft_zone_size ;
}
ntfs_debug ( " vol->mft_zone_end = 0x%llx " ,
( unsigned long long ) vol - > mft_zone_end ) ;
/*
* Set the current position within each data zone to the start of the
* respective zone .
*/
vol - > data1_zone_pos = vol - > mft_zone_end ;
ntfs_debug ( " vol->data1_zone_pos = 0x%llx " ,
( unsigned long long ) vol - > data1_zone_pos ) ;
vol - > data2_zone_pos = 0 ;
ntfs_debug ( " vol->data2_zone_pos = 0x%llx " ,
( unsigned long long ) vol - > data2_zone_pos ) ;
/* Set the mft data allocation position to mft record 24. */
vol - > mft_data_pos = 24 ;
ntfs_debug ( " vol->mft_data_pos = 0x%llx " ,
( unsigned long long ) vol - > mft_data_pos ) ;
# endif /* NTFS_RW */
}
# ifdef NTFS_RW
/**
* load_and_init_mft_mirror - load and setup the mft mirror inode for a volume
* @ vol : ntfs super block describing device whose mft mirror to load
*
* Return TRUE on success or FALSE on error .
*/
static BOOL load_and_init_mft_mirror ( ntfs_volume * vol )
{
struct inode * tmp_ino ;
ntfs_inode * tmp_ni ;
ntfs_debug ( " Entering. " ) ;
/* Get mft mirror inode. */
tmp_ino = ntfs_iget ( vol - > sb , FILE_MFTMirr ) ;
if ( IS_ERR ( tmp_ino ) | | is_bad_inode ( tmp_ino ) ) {
if ( ! IS_ERR ( tmp_ino ) )
iput ( tmp_ino ) ;
/* Caller will display error message. */
return FALSE ;
}
/*
* Re - initialize some specifics about $ MFTMirr ' s inode as
* ntfs_read_inode ( ) will have set up the default ones .
*/
/* Set uid and gid to root. */
tmp_ino - > i_uid = tmp_ino - > i_gid = 0 ;
/* Regular file. No access for anyone. */
tmp_ino - > i_mode = S_IFREG ;
/* No VFS initiated operations allowed for $MFTMirr. */
tmp_ino - > i_op = & ntfs_empty_inode_ops ;
tmp_ino - > i_fop = & ntfs_empty_file_ops ;
/* Put in our special address space operations. */
tmp_ino - > i_mapping - > a_ops = & ntfs_mst_aops ;
tmp_ni = NTFS_I ( tmp_ino ) ;
/* The $MFTMirr, like the $MFT is multi sector transfer protected. */
NInoSetMstProtected ( tmp_ni ) ;
2005-02-03 15:02:56 +03:00
NInoSetSparseDisabled ( tmp_ni ) ;
2005-04-17 02:20:36 +04:00
/*
* Set up our little cheat allowing us to reuse the async read io
* completion handler for directories .
*/
tmp_ni - > itype . index . block_size = vol - > mft_record_size ;
tmp_ni - > itype . index . block_size_bits = vol - > mft_record_size_bits ;
vol - > mftmirr_ino = tmp_ino ;
ntfs_debug ( " Done. " ) ;
return TRUE ;
}
/**
* check_mft_mirror - compare contents of the mft mirror with the mft
* @ vol : ntfs super block describing device whose mft mirror to check
*
* Return TRUE on success or FALSE on error .
*
* Note , this function also results in the mft mirror runlist being completely
* mapped into memory . The mft mirror write code requires this and will BUG ( )
* should it find an unmapped runlist element .
*/
static BOOL check_mft_mirror ( ntfs_volume * vol )
{
struct super_block * sb = vol - > sb ;
ntfs_inode * mirr_ni ;
struct page * mft_page , * mirr_page ;
u8 * kmft , * kmirr ;
runlist_element * rl , rl2 [ 2 ] ;
2004-11-18 23:34:59 +03:00
pgoff_t index ;
2005-04-17 02:20:36 +04:00
int mrecs_per_page , i ;
ntfs_debug ( " Entering. " ) ;
/* Compare contents of $MFT and $MFTMirr. */
mrecs_per_page = PAGE_CACHE_SIZE / vol - > mft_record_size ;
BUG_ON ( ! mrecs_per_page ) ;
BUG_ON ( ! vol - > mftmirr_size ) ;
mft_page = mirr_page = NULL ;
kmft = kmirr = NULL ;
index = i = 0 ;
do {
u32 bytes ;
/* Switch pages if necessary. */
if ( ! ( i % mrecs_per_page ) ) {
if ( index ) {
ntfs_unmap_page ( mft_page ) ;
ntfs_unmap_page ( mirr_page ) ;
}
/* Get the $MFT page. */
mft_page = ntfs_map_page ( vol - > mft_ino - > i_mapping ,
index ) ;
if ( IS_ERR ( mft_page ) ) {
ntfs_error ( sb , " Failed to read $MFT. " ) ;
return FALSE ;
}
kmft = page_address ( mft_page ) ;
/* Get the $MFTMirr page. */
mirr_page = ntfs_map_page ( vol - > mftmirr_ino - > i_mapping ,
index ) ;
if ( IS_ERR ( mirr_page ) ) {
ntfs_error ( sb , " Failed to read $MFTMirr. " ) ;
goto mft_unmap_out ;
}
kmirr = page_address ( mirr_page ) ;
+ + index ;
}
/* Make sure the record is ok. */
if ( ntfs_is_baad_recordp ( ( le32 * ) kmft ) ) {
ntfs_error ( sb , " Incomplete multi sector transfer "
" detected in mft record %i. " , i ) ;
mm_unmap_out :
ntfs_unmap_page ( mirr_page ) ;
mft_unmap_out :
ntfs_unmap_page ( mft_page ) ;
return FALSE ;
}
if ( ntfs_is_baad_recordp ( ( le32 * ) kmirr ) ) {
ntfs_error ( sb , " Incomplete multi sector transfer "
" detected in mft mirror record %i. " , i ) ;
goto mm_unmap_out ;
}
/* Get the amount of data in the current record. */
bytes = le32_to_cpu ( ( ( MFT_RECORD * ) kmft ) - > bytes_in_use ) ;
if ( ! bytes | | bytes > vol - > mft_record_size ) {
bytes = le32_to_cpu ( ( ( MFT_RECORD * ) kmirr ) - > bytes_in_use ) ;
if ( ! bytes | | bytes > vol - > mft_record_size )
bytes = vol - > mft_record_size ;
}
/* Compare the two records. */
if ( memcmp ( kmft , kmirr , bytes ) ) {
ntfs_error ( sb , " $MFT and $MFTMirr (record %i) do not "
" match. Run ntfsfix or chkdsk. " , i ) ;
goto mm_unmap_out ;
}
kmft + = vol - > mft_record_size ;
kmirr + = vol - > mft_record_size ;
} while ( + + i < vol - > mftmirr_size ) ;
/* Release the last pages. */
ntfs_unmap_page ( mft_page ) ;
ntfs_unmap_page ( mirr_page ) ;
/* Construct the mft mirror runlist by hand. */
rl2 [ 0 ] . vcn = 0 ;
rl2 [ 0 ] . lcn = vol - > mftmirr_lcn ;
rl2 [ 0 ] . length = ( vol - > mftmirr_size * vol - > mft_record_size +
vol - > cluster_size - 1 ) / vol - > cluster_size ;
rl2 [ 1 ] . vcn = rl2 [ 0 ] . length ;
rl2 [ 1 ] . lcn = LCN_ENOENT ;
rl2 [ 1 ] . length = 0 ;
/*
* Because we have just read all of the mft mirror , we know we have
* mapped the full runlist for it .
*/
mirr_ni = NTFS_I ( vol - > mftmirr_ino ) ;
down_read ( & mirr_ni - > runlist . lock ) ;
rl = mirr_ni - > runlist . rl ;
/* Compare the two runlists. They must be identical. */
i = 0 ;
do {
if ( rl2 [ i ] . vcn ! = rl [ i ] . vcn | | rl2 [ i ] . lcn ! = rl [ i ] . lcn | |
rl2 [ i ] . length ! = rl [ i ] . length ) {
ntfs_error ( sb , " $MFTMirr location mismatch. "
" Run chkdsk. " ) ;
up_read ( & mirr_ni - > runlist . lock ) ;
return FALSE ;
}
} while ( rl2 [ i + + ] . length ) ;
up_read ( & mirr_ni - > runlist . lock ) ;
ntfs_debug ( " Done. " ) ;
return TRUE ;
}
/**
* load_and_check_logfile - load and check the logfile inode for a volume
* @ vol : ntfs super block describing device whose logfile to load
*
* Return TRUE on success or FALSE on error .
*/
2005-09-08 19:12:28 +04:00
static BOOL load_and_check_logfile ( ntfs_volume * vol ,
RESTART_PAGE_HEADER * * rp )
2005-04-17 02:20:36 +04:00
{
struct inode * tmp_ino ;
ntfs_debug ( " Entering. " ) ;
tmp_ino = ntfs_iget ( vol - > sb , FILE_LogFile ) ;
if ( IS_ERR ( tmp_ino ) | | is_bad_inode ( tmp_ino ) ) {
if ( ! IS_ERR ( tmp_ino ) )
iput ( tmp_ino ) ;
/* Caller will display error message. */
return FALSE ;
}
2005-09-08 19:12:28 +04:00
if ( ! ntfs_check_logfile ( tmp_ino , rp ) ) {
2005-04-17 02:20:36 +04:00
iput ( tmp_ino ) ;
/* ntfs_check_logfile() will have displayed error output. */
return FALSE ;
}
2005-02-03 15:02:56 +03:00
NInoSetSparseDisabled ( NTFS_I ( tmp_ino ) ) ;
2005-04-17 02:20:36 +04:00
vol - > logfile_ino = tmp_ino ;
ntfs_debug ( " Done. " ) ;
return TRUE ;
}
2005-06-25 19:31:27 +04:00
# define NTFS_HIBERFIL_HEADER_SIZE 4096
/**
* check_windows_hibernation_status - check if Windows is suspended on a volume
* @ vol : ntfs super block of device to check
*
* Check if Windows is hibernated on the ntfs volume @ vol . This is done by
* looking for the file hiberfil . sys in the root directory of the volume . If
* the file is not present Windows is definitely not suspended .
*
* If hiberfil . sys exists and is less than 4 kiB in size it means Windows is
* definitely suspended ( this volume is not the system volume ) . Caveat : on a
* system with many volumes it is possible that the < 4 kiB check is bogus but
* for now this should do fine .
*
* If hiberfil . sys exists and is larger than 4 kiB in size , we need to read the
* hiberfil header ( which is the first 4 kiB ) . If this begins with " hibr " ,
* Windows is definitely suspended . If it is completely full of zeroes ,
* Windows is definitely not hibernated . Any other case is treated as if
* Windows is suspended . This caters for the above mentioned caveat of a
* system with many volumes where no " hibr " magic would be present and there is
* no zero header .
*
* Return 0 if Windows is not hibernated on the volume , > 0 if Windows is
* hibernated on the volume , and - errno on error .
*/
static int check_windows_hibernation_status ( ntfs_volume * vol )
{
MFT_REF mref ;
struct inode * vi ;
ntfs_inode * ni ;
struct page * page ;
u32 * kaddr , * kend ;
ntfs_name * name = NULL ;
int ret = 1 ;
static const ntfschar hiberfil [ 13 ] = { const_cpu_to_le16 ( ' h ' ) ,
const_cpu_to_le16 ( ' i ' ) , const_cpu_to_le16 ( ' b ' ) ,
const_cpu_to_le16 ( ' e ' ) , const_cpu_to_le16 ( ' r ' ) ,
const_cpu_to_le16 ( ' f ' ) , const_cpu_to_le16 ( ' i ' ) ,
const_cpu_to_le16 ( ' l ' ) , const_cpu_to_le16 ( ' . ' ) ,
const_cpu_to_le16 ( ' s ' ) , const_cpu_to_le16 ( ' y ' ) ,
const_cpu_to_le16 ( ' s ' ) , 0 } ;
ntfs_debug ( " Entering. " ) ;
/*
* Find the inode number for the hibernation file by looking up the
* filename hiberfil . sys in the root directory .
*/
down ( & vol - > root_ino - > i_sem ) ;
mref = ntfs_lookup_inode_by_name ( NTFS_I ( vol - > root_ino ) , hiberfil , 12 ,
& name ) ;
up ( & vol - > root_ino - > i_sem ) ;
if ( IS_ERR_MREF ( mref ) ) {
ret = MREF_ERR ( mref ) ;
/* If the file does not exist, Windows is not hibernated. */
if ( ret = = - ENOENT ) {
ntfs_debug ( " hiberfil.sys not present. Windows is not "
" hibernated on the volume. " ) ;
return 0 ;
}
/* A real error occured. */
ntfs_error ( vol - > sb , " Failed to find inode number for "
" hiberfil.sys. " ) ;
return ret ;
}
/* We do not care for the type of match that was found. */
kfree ( name ) ;
/* Get the inode. */
vi = ntfs_iget ( vol - > sb , MREF ( mref ) ) ;
if ( IS_ERR ( vi ) | | is_bad_inode ( vi ) ) {
if ( ! IS_ERR ( vi ) )
iput ( vi ) ;
ntfs_error ( vol - > sb , " Failed to load hiberfil.sys. " ) ;
return IS_ERR ( vi ) ? PTR_ERR ( vi ) : - EIO ;
}
if ( unlikely ( i_size_read ( vi ) < NTFS_HIBERFIL_HEADER_SIZE ) ) {
ntfs_debug ( " hiberfil.sys is smaller than 4kiB (0x%llx). "
" Windows is hibernated on the volume. This "
" is not the system volume. " , i_size_read ( vi ) ) ;
goto iput_out ;
}
ni = NTFS_I ( vi ) ;
page = ntfs_map_page ( vi - > i_mapping , 0 ) ;
if ( IS_ERR ( page ) ) {
ntfs_error ( vol - > sb , " Failed to read from hiberfil.sys. " ) ;
ret = PTR_ERR ( page ) ;
goto iput_out ;
}
kaddr = ( u32 * ) page_address ( page ) ;
if ( * ( le32 * ) kaddr = = const_cpu_to_le32 ( 0x72626968 ) /*'hibr'*/ ) {
ntfs_debug ( " Magic \" hibr \" found in hiberfil.sys. Windows is "
" hibernated on the volume. This is the "
" system volume. " ) ;
goto unm_iput_out ;
}
kend = kaddr + NTFS_HIBERFIL_HEADER_SIZE / sizeof ( * kaddr ) ;
do {
if ( unlikely ( * kaddr ) ) {
ntfs_debug ( " hiberfil.sys is larger than 4kiB "
" (0x%llx), does not contain the "
" \" hibr \" magic, and does not have a "
" zero header. Windows is hibernated "
" on the volume. This is not the "
" system volume. " , i_size_read ( vi ) ) ;
goto unm_iput_out ;
}
} while ( + + kaddr < kend ) ;
ntfs_debug ( " hiberfil.sys contains a zero header. Windows is not "
" hibernated on the volume. This is the system "
" volume. " ) ;
ret = 0 ;
unm_iput_out :
ntfs_unmap_page ( page ) ;
iput_out :
iput ( vi ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
/**
* load_and_init_quota - load and setup the quota file for a volume if present
* @ vol : ntfs super block describing device whose quota file to load
*
* Return TRUE on success or FALSE on error . If $ Quota is not present , we
* leave vol - > quota_ino as NULL and return success .
*/
static BOOL load_and_init_quota ( ntfs_volume * vol )
{
MFT_REF mref ;
struct inode * tmp_ino ;
ntfs_name * name = NULL ;
static const ntfschar Quota [ 7 ] = { const_cpu_to_le16 ( ' $ ' ) ,
const_cpu_to_le16 ( ' Q ' ) , const_cpu_to_le16 ( ' u ' ) ,
const_cpu_to_le16 ( ' o ' ) , const_cpu_to_le16 ( ' t ' ) ,
const_cpu_to_le16 ( ' a ' ) , 0 } ;
static ntfschar Q [ 3 ] = { const_cpu_to_le16 ( ' $ ' ) ,
const_cpu_to_le16 ( ' Q ' ) , 0 } ;
ntfs_debug ( " Entering. " ) ;
/*
* Find the inode number for the quota file by looking up the filename
* $ Quota in the extended system files directory $ Extend .
*/
down ( & vol - > extend_ino - > i_sem ) ;
mref = ntfs_lookup_inode_by_name ( NTFS_I ( vol - > extend_ino ) , Quota , 6 ,
& name ) ;
up ( & vol - > extend_ino - > i_sem ) ;
if ( IS_ERR_MREF ( mref ) ) {
/*
* If the file does not exist , quotas are disabled and have
* never been enabled on this volume , just return success .
*/
if ( MREF_ERR ( mref ) = = - ENOENT ) {
ntfs_debug ( " $Quota not present. Volume does not have "
" quotas enabled. " ) ;
/*
* No need to try to set quotas out of date if they are
* not enabled .
*/
NVolSetQuotaOutOfDate ( vol ) ;
return TRUE ;
}
/* A real error occured. */
ntfs_error ( vol - > sb , " Failed to find inode number for $Quota. " ) ;
return FALSE ;
}
/* We do not care for the type of match that was found. */
2005-04-04 17:59:56 +04:00
kfree ( name ) ;
2005-04-17 02:20:36 +04:00
/* Get the inode. */
tmp_ino = ntfs_iget ( vol - > sb , MREF ( mref ) ) ;
if ( IS_ERR ( tmp_ino ) | | is_bad_inode ( tmp_ino ) ) {
if ( ! IS_ERR ( tmp_ino ) )
iput ( tmp_ino ) ;
ntfs_error ( vol - > sb , " Failed to load $Quota. " ) ;
return FALSE ;
}
vol - > quota_ino = tmp_ino ;
/* Get the $Q index allocation attribute. */
tmp_ino = ntfs_index_iget ( vol - > quota_ino , Q , 2 ) ;
if ( IS_ERR ( tmp_ino ) ) {
ntfs_error ( vol - > sb , " Failed to load $Quota/$Q index. " ) ;
return FALSE ;
}
vol - > quota_q_ino = tmp_ino ;
ntfs_debug ( " Done. " ) ;
return TRUE ;
}
2005-06-25 18:28:56 +04:00
/**
* load_and_init_usnjrnl - load and setup the transaction log if present
* @ vol : ntfs super block describing device whose usnjrnl file to load
*
* Return TRUE on success or FALSE on error .
*
* If $ UsnJrnl is not present or in the process of being disabled , we set
* NVolUsnJrnlStamped ( ) and return success .
*
* If the $ UsnJrnl $ DATA / $ J attribute has a size equal to the lowest valid usn ,
* i . e . transaction logging has only just been enabled or the journal has been
* stamped and nothing has been logged since , we also set NVolUsnJrnlStamped ( )
* and return success .
*/
static BOOL load_and_init_usnjrnl ( ntfs_volume * vol )
{
MFT_REF mref ;
struct inode * tmp_ino ;
ntfs_inode * tmp_ni ;
struct page * page ;
ntfs_name * name = NULL ;
USN_HEADER * uh ;
static const ntfschar UsnJrnl [ 9 ] = { const_cpu_to_le16 ( ' $ ' ) ,
const_cpu_to_le16 ( ' U ' ) , const_cpu_to_le16 ( ' s ' ) ,
const_cpu_to_le16 ( ' n ' ) , const_cpu_to_le16 ( ' J ' ) ,
const_cpu_to_le16 ( ' r ' ) , const_cpu_to_le16 ( ' n ' ) ,
const_cpu_to_le16 ( ' l ' ) , 0 } ;
static ntfschar Max [ 5 ] = { const_cpu_to_le16 ( ' $ ' ) ,
const_cpu_to_le16 ( ' M ' ) , const_cpu_to_le16 ( ' a ' ) ,
const_cpu_to_le16 ( ' x ' ) , 0 } ;
static ntfschar J [ 3 ] = { const_cpu_to_le16 ( ' $ ' ) ,
const_cpu_to_le16 ( ' J ' ) , 0 } ;
ntfs_debug ( " Entering. " ) ;
/*
* Find the inode number for the transaction log file by looking up the
* filename $ UsnJrnl in the extended system files directory $ Extend .
*/
down ( & vol - > extend_ino - > i_sem ) ;
mref = ntfs_lookup_inode_by_name ( NTFS_I ( vol - > extend_ino ) , UsnJrnl , 8 ,
& name ) ;
up ( & vol - > extend_ino - > i_sem ) ;
if ( IS_ERR_MREF ( mref ) ) {
/*
* If the file does not exist , transaction logging is disabled ,
* just return success .
*/
if ( MREF_ERR ( mref ) = = - ENOENT ) {
ntfs_debug ( " $UsnJrnl not present. Volume does not "
" have transaction logging enabled. " ) ;
not_enabled :
/*
* No need to try to stamp the transaction log if
* transaction logging is not enabled .
*/
NVolSetUsnJrnlStamped ( vol ) ;
return TRUE ;
}
/* A real error occured. */
ntfs_error ( vol - > sb , " Failed to find inode number for "
" $UsnJrnl. " ) ;
return FALSE ;
}
/* We do not care for the type of match that was found. */
kfree ( name ) ;
/* Get the inode. */
tmp_ino = ntfs_iget ( vol - > sb , MREF ( mref ) ) ;
if ( unlikely ( IS_ERR ( tmp_ino ) | | is_bad_inode ( tmp_ino ) ) ) {
if ( ! IS_ERR ( tmp_ino ) )
iput ( tmp_ino ) ;
ntfs_error ( vol - > sb , " Failed to load $UsnJrnl. " ) ;
return FALSE ;
}
vol - > usnjrnl_ino = tmp_ino ;
/*
* If the transaction log is in the process of being deleted , we can
* ignore it .
*/
if ( unlikely ( vol - > vol_flags & VOLUME_DELETE_USN_UNDERWAY ) ) {
ntfs_debug ( " $UsnJrnl in the process of being disabled. "
" Volume does not have transaction logging "
" enabled. " ) ;
goto not_enabled ;
}
/* Get the $DATA/$Max attribute. */
tmp_ino = ntfs_attr_iget ( vol - > usnjrnl_ino , AT_DATA , Max , 4 ) ;
if ( IS_ERR ( tmp_ino ) ) {
ntfs_error ( vol - > sb , " Failed to load $UsnJrnl/$DATA/$Max "
" attribute. " ) ;
return FALSE ;
}
vol - > usnjrnl_max_ino = tmp_ino ;
if ( unlikely ( i_size_read ( tmp_ino ) < sizeof ( USN_HEADER ) ) ) {
ntfs_error ( vol - > sb , " Found corrupt $UsnJrnl/$DATA/$Max "
" attribute (size is 0x%llx but should be at "
" least 0x%x bytes). " , i_size_read ( tmp_ino ) ,
sizeof ( USN_HEADER ) ) ;
return FALSE ;
}
/* Get the $DATA/$J attribute. */
tmp_ino = ntfs_attr_iget ( vol - > usnjrnl_ino , AT_DATA , J , 2 ) ;
if ( IS_ERR ( tmp_ino ) ) {
ntfs_error ( vol - > sb , " Failed to load $UsnJrnl/$DATA/$J "
" attribute. " ) ;
return FALSE ;
}
vol - > usnjrnl_j_ino = tmp_ino ;
/* Verify $J is non-resident and sparse. */
tmp_ni = NTFS_I ( vol - > usnjrnl_j_ino ) ;
if ( unlikely ( ! NInoNonResident ( tmp_ni ) | | ! NInoSparse ( tmp_ni ) ) ) {
ntfs_error ( vol - > sb , " $UsnJrnl/$DATA/$J attribute is resident "
" and/or not sparse. " ) ;
return FALSE ;
}
/* Read the USN_HEADER from $DATA/$Max. */
page = ntfs_map_page ( vol - > usnjrnl_max_ino - > i_mapping , 0 ) ;
if ( IS_ERR ( page ) ) {
ntfs_error ( vol - > sb , " Failed to read from $UsnJrnl/$DATA/$Max "
" attribute. " ) ;
return FALSE ;
}
uh = ( USN_HEADER * ) page_address ( page ) ;
/* Sanity check the $Max. */
if ( unlikely ( sle64_to_cpu ( uh - > allocation_delta ) >
sle64_to_cpu ( uh - > maximum_size ) ) ) {
ntfs_error ( vol - > sb , " Allocation delta (0x%llx) exceeds "
" maximum size (0x%llx). $UsnJrnl is corrupt. " ,
( long long ) sle64_to_cpu ( uh - > allocation_delta ) ,
( long long ) sle64_to_cpu ( uh - > maximum_size ) ) ;
ntfs_unmap_page ( page ) ;
return FALSE ;
}
/*
* If the transaction log has been stamped and nothing has been written
* to it since , we do not need to stamp it .
*/
if ( unlikely ( sle64_to_cpu ( uh - > lowest_valid_usn ) > =
i_size_read ( vol - > usnjrnl_j_ino ) ) ) {
if ( likely ( sle64_to_cpu ( uh - > lowest_valid_usn ) = =
i_size_read ( vol - > usnjrnl_j_ino ) ) ) {
ntfs_unmap_page ( page ) ;
ntfs_debug ( " $UsnJrnl is enabled but nothing has been "
" logged since it was last stamped. "
" Treating this as if the volume does "
" not have transaction logging "
" enabled. " ) ;
goto not_enabled ;
}
ntfs_error ( vol - > sb , " $UsnJrnl has lowest valid usn (0x%llx) "
" which is out of bounds (0x%llx). $UsnJrnl "
" is corrupt. " ,
( long long ) sle64_to_cpu ( uh - > lowest_valid_usn ) ,
i_size_read ( vol - > usnjrnl_j_ino ) ) ;
ntfs_unmap_page ( page ) ;
return FALSE ;
}
ntfs_unmap_page ( page ) ;
ntfs_debug ( " Done. " ) ;
return TRUE ;
}
2005-04-17 02:20:36 +04:00
/**
* load_and_init_attrdef - load the attribute definitions table for a volume
* @ vol : ntfs super block describing device whose attrdef to load
*
* Return TRUE on success or FALSE on error .
*/
static BOOL load_and_init_attrdef ( ntfs_volume * vol )
{
2004-11-18 23:34:59 +03:00
loff_t i_size ;
2005-04-17 02:20:36 +04:00
struct super_block * sb = vol - > sb ;
struct inode * ino ;
struct page * page ;
2004-11-18 23:34:59 +03:00
pgoff_t index , max_index ;
2005-04-17 02:20:36 +04:00
unsigned int size ;
ntfs_debug ( " Entering. " ) ;
/* Read attrdef table and setup vol->attrdef and vol->attrdef_size. */
ino = ntfs_iget ( sb , FILE_AttrDef ) ;
if ( IS_ERR ( ino ) | | is_bad_inode ( ino ) ) {
if ( ! IS_ERR ( ino ) )
iput ( ino ) ;
goto failed ;
}
2005-02-03 15:02:56 +03:00
NInoSetSparseDisabled ( NTFS_I ( ino ) ) ;
2005-04-17 02:20:36 +04:00
/* The size of FILE_AttrDef must be above 0 and fit inside 31 bits. */
2004-11-18 23:34:59 +03:00
i_size = i_size_read ( ino ) ;
if ( i_size < = 0 | | i_size > 0x7fffffff )
2005-04-17 02:20:36 +04:00
goto iput_failed ;
2004-11-18 23:34:59 +03:00
vol - > attrdef = ( ATTR_DEF * ) ntfs_malloc_nofs ( i_size ) ;
2005-04-17 02:20:36 +04:00
if ( ! vol - > attrdef )
goto iput_failed ;
index = 0 ;
2004-11-18 23:34:59 +03:00
max_index = i_size > > PAGE_CACHE_SHIFT ;
2005-04-17 02:20:36 +04:00
size = PAGE_CACHE_SIZE ;
while ( index < max_index ) {
/* Read the attrdef table and copy it into the linear buffer. */
read_partial_attrdef_page :
page = ntfs_map_page ( ino - > i_mapping , index ) ;
if ( IS_ERR ( page ) )
goto free_iput_failed ;
memcpy ( ( u8 * ) vol - > attrdef + ( index + + < < PAGE_CACHE_SHIFT ) ,
page_address ( page ) , size ) ;
ntfs_unmap_page ( page ) ;
} ;
if ( size = = PAGE_CACHE_SIZE ) {
2004-11-18 23:34:59 +03:00
size = i_size & ~ PAGE_CACHE_MASK ;
2005-04-17 02:20:36 +04:00
if ( size )
goto read_partial_attrdef_page ;
}
2004-11-18 23:34:59 +03:00
vol - > attrdef_size = i_size ;
ntfs_debug ( " Read %llu bytes from $AttrDef. " , i_size ) ;
2005-04-17 02:20:36 +04:00
iput ( ino ) ;
return TRUE ;
free_iput_failed :
ntfs_free ( vol - > attrdef ) ;
vol - > attrdef = NULL ;
iput_failed :
iput ( ino ) ;
failed :
ntfs_error ( sb , " Failed to initialize attribute definition table. " ) ;
return FALSE ;
}
# endif /* NTFS_RW */
/**
* load_and_init_upcase - load the upcase table for an ntfs volume
* @ vol : ntfs super block describing device whose upcase to load
*
* Return TRUE on success or FALSE on error .
*/
static BOOL load_and_init_upcase ( ntfs_volume * vol )
{
2004-11-18 23:34:59 +03:00
loff_t i_size ;
2005-04-17 02:20:36 +04:00
struct super_block * sb = vol - > sb ;
struct inode * ino ;
struct page * page ;
2004-11-18 23:34:59 +03:00
pgoff_t index , max_index ;
2005-04-17 02:20:36 +04:00
unsigned int size ;
int i , max ;
ntfs_debug ( " Entering. " ) ;
/* Read upcase table and setup vol->upcase and vol->upcase_len. */
ino = ntfs_iget ( sb , FILE_UpCase ) ;
if ( IS_ERR ( ino ) | | is_bad_inode ( ino ) ) {
if ( ! IS_ERR ( ino ) )
iput ( ino ) ;
goto upcase_failed ;
}
/*
* The upcase size must not be above 64 k Unicode characters , must not
* be zero and must be a multiple of sizeof ( ntfschar ) .
*/
2004-11-18 23:34:59 +03:00
i_size = i_size_read ( ino ) ;
if ( ! i_size | | i_size & ( sizeof ( ntfschar ) - 1 ) | |
i_size > 64ULL * 1024 * sizeof ( ntfschar ) )
2005-04-17 02:20:36 +04:00
goto iput_upcase_failed ;
2004-11-18 23:34:59 +03:00
vol - > upcase = ( ntfschar * ) ntfs_malloc_nofs ( i_size ) ;
2005-04-17 02:20:36 +04:00
if ( ! vol - > upcase )
goto iput_upcase_failed ;
index = 0 ;
2004-11-18 23:34:59 +03:00
max_index = i_size > > PAGE_CACHE_SHIFT ;
2005-04-17 02:20:36 +04:00
size = PAGE_CACHE_SIZE ;
while ( index < max_index ) {
/* Read the upcase table and copy it into the linear buffer. */
read_partial_upcase_page :
page = ntfs_map_page ( ino - > i_mapping , index ) ;
if ( IS_ERR ( page ) )
goto iput_upcase_failed ;
memcpy ( ( char * ) vol - > upcase + ( index + + < < PAGE_CACHE_SHIFT ) ,
page_address ( page ) , size ) ;
ntfs_unmap_page ( page ) ;
} ;
if ( size = = PAGE_CACHE_SIZE ) {
2004-11-18 23:34:59 +03:00
size = i_size & ~ PAGE_CACHE_MASK ;
2005-04-17 02:20:36 +04:00
if ( size )
goto read_partial_upcase_page ;
}
2004-11-18 23:34:59 +03:00
vol - > upcase_len = i_size > > UCHAR_T_SIZE_BITS ;
2005-04-17 02:20:36 +04:00
ntfs_debug ( " Read %llu bytes from $UpCase (expected %zu bytes). " ,
2004-11-18 23:34:59 +03:00
i_size , 64 * 1024 * sizeof ( ntfschar ) ) ;
2005-04-17 02:20:36 +04:00
iput ( ino ) ;
down ( & ntfs_lock ) ;
if ( ! default_upcase ) {
ntfs_debug ( " Using volume specified $UpCase since default is "
" not present. " ) ;
up ( & ntfs_lock ) ;
return TRUE ;
}
max = default_upcase_len ;
if ( max > vol - > upcase_len )
max = vol - > upcase_len ;
for ( i = 0 ; i < max ; i + + )
if ( vol - > upcase [ i ] ! = default_upcase [ i ] )
break ;
if ( i = = max ) {
ntfs_free ( vol - > upcase ) ;
vol - > upcase = default_upcase ;
vol - > upcase_len = max ;
ntfs_nr_upcase_users + + ;
up ( & ntfs_lock ) ;
ntfs_debug ( " Volume specified $UpCase matches default. Using "
" default. " ) ;
return TRUE ;
}
up ( & ntfs_lock ) ;
ntfs_debug ( " Using volume specified $UpCase since it does not match "
" the default. " ) ;
return TRUE ;
iput_upcase_failed :
iput ( ino ) ;
ntfs_free ( vol - > upcase ) ;
vol - > upcase = NULL ;
upcase_failed :
down ( & ntfs_lock ) ;
if ( default_upcase ) {
vol - > upcase = default_upcase ;
vol - > upcase_len = default_upcase_len ;
ntfs_nr_upcase_users + + ;
up ( & ntfs_lock ) ;
ntfs_error ( sb , " Failed to load $UpCase from the volume. Using "
" default. " ) ;
return TRUE ;
}
up ( & ntfs_lock ) ;
ntfs_error ( sb , " Failed to initialize upcase table. " ) ;
return FALSE ;
}
/**
* load_system_files - open the system files using normal functions
* @ vol : ntfs super block describing device whose system files to load
*
* Open the system files with normal access functions and complete setting up
* the ntfs super block @ vol .
*
* Return TRUE on success or FALSE on error .
*/
static BOOL load_system_files ( ntfs_volume * vol )
{
struct super_block * sb = vol - > sb ;
MFT_RECORD * m ;
VOLUME_INFORMATION * vi ;
ntfs_attr_search_ctx * ctx ;
2005-06-25 19:31:27 +04:00
# ifdef NTFS_RW
2005-09-09 02:01:16 +04:00
RESTART_PAGE_HEADER * rp ;
2005-06-25 19:31:27 +04:00
int err ;
# endif /* NTFS_RW */
2005-04-17 02:20:36 +04:00
ntfs_debug ( " Entering. " ) ;
# ifdef NTFS_RW
/* Get mft mirror inode compare the contents of $MFT and $MFTMirr. */
if ( ! load_and_init_mft_mirror ( vol ) | | ! check_mft_mirror ( vol ) ) {
static const char * es1 = " Failed to load $MFTMirr " ;
static const char * es2 = " $MFTMirr does not match $MFT " ;
static const char * es3 = " . Run ntfsfix and/or chkdsk. " ;
/* If a read-write mount, convert it to a read-only mount. */
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors= "
" continue nor on_errors= "
" remount-ro was specified%s " ,
! vol - > mftmirr_ino ? es1 : es2 ,
es3 ) ;
goto iput_mirr_err_out ;
}
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
ntfs_error ( sb , " %s. Mounting read-only%s " ,
! vol - > mftmirr_ino ? es1 : es2 , es3 ) ;
} else
ntfs_warning ( sb , " %s. Will not be able to remount "
" read-write%s " ,
! vol - > mftmirr_ino ? es1 : es2 , es3 ) ;
/* This will prevent a read-write remount. */
NVolSetErrors ( vol ) ;
}
# endif /* NTFS_RW */
/* Get mft bitmap attribute inode. */
vol - > mftbmp_ino = ntfs_attr_iget ( vol - > mft_ino , AT_BITMAP , NULL , 0 ) ;
if ( IS_ERR ( vol - > mftbmp_ino ) ) {
ntfs_error ( sb , " Failed to load $MFT/$BITMAP attribute. " ) ;
goto iput_mirr_err_out ;
}
/* Read upcase table and setup @vol->upcase and @vol->upcase_len. */
if ( ! load_and_init_upcase ( vol ) )
goto iput_mftbmp_err_out ;
# ifdef NTFS_RW
/*
* Read attribute definitions table and setup @ vol - > attrdef and
* @ vol - > attrdef_size .
*/
if ( ! load_and_init_attrdef ( vol ) )
goto iput_upcase_err_out ;
# endif /* NTFS_RW */
/*
* Get the cluster allocation bitmap inode and verify the size , no
* need for any locking at this stage as we are already running
* exclusively as we are mount in progress task .
*/
vol - > lcnbmp_ino = ntfs_iget ( sb , FILE_Bitmap ) ;
if ( IS_ERR ( vol - > lcnbmp_ino ) | | is_bad_inode ( vol - > lcnbmp_ino ) ) {
if ( ! IS_ERR ( vol - > lcnbmp_ino ) )
iput ( vol - > lcnbmp_ino ) ;
goto bitmap_failed ;
}
2005-02-03 15:02:56 +03:00
NInoSetSparseDisabled ( NTFS_I ( vol - > lcnbmp_ino ) ) ;
2004-11-18 23:34:59 +03:00
if ( ( vol - > nr_clusters + 7 ) > > 3 > i_size_read ( vol - > lcnbmp_ino ) ) {
2005-04-17 02:20:36 +04:00
iput ( vol - > lcnbmp_ino ) ;
bitmap_failed :
ntfs_error ( sb , " Failed to load $Bitmap. " ) ;
goto iput_attrdef_err_out ;
}
/*
* Get the volume inode and setup our cache of the volume flags and
* version .
*/
vol - > vol_ino = ntfs_iget ( sb , FILE_Volume ) ;
if ( IS_ERR ( vol - > vol_ino ) | | is_bad_inode ( vol - > vol_ino ) ) {
if ( ! IS_ERR ( vol - > vol_ino ) )
iput ( vol - > vol_ino ) ;
volume_failed :
ntfs_error ( sb , " Failed to load $Volume. " ) ;
goto iput_lcnbmp_err_out ;
}
m = map_mft_record ( NTFS_I ( vol - > vol_ino ) ) ;
if ( IS_ERR ( m ) ) {
iput_volume_failed :
iput ( vol - > vol_ino ) ;
goto volume_failed ;
}
if ( ! ( ctx = ntfs_attr_get_search_ctx ( NTFS_I ( vol - > vol_ino ) , m ) ) ) {
ntfs_error ( sb , " Failed to get attribute search context. " ) ;
goto get_ctx_vol_failed ;
}
if ( ntfs_attr_lookup ( AT_VOLUME_INFORMATION , NULL , 0 , 0 , 0 , NULL , 0 ,
ctx ) | | ctx - > attr - > non_resident | | ctx - > attr - > flags ) {
err_put_vol :
ntfs_attr_put_search_ctx ( ctx ) ;
get_ctx_vol_failed :
unmap_mft_record ( NTFS_I ( vol - > vol_ino ) ) ;
goto iput_volume_failed ;
}
vi = ( VOLUME_INFORMATION * ) ( ( char * ) ctx - > attr +
le16_to_cpu ( ctx - > attr - > data . resident . value_offset ) ) ;
/* Some bounds checks. */
if ( ( u8 * ) vi < ( u8 * ) ctx - > attr | | ( u8 * ) vi +
le32_to_cpu ( ctx - > attr - > data . resident . value_length ) >
( u8 * ) ctx - > attr + le32_to_cpu ( ctx - > attr - > length ) )
goto err_put_vol ;
/* Copy the volume flags and version to the ntfs_volume structure. */
vol - > vol_flags = vi - > flags ;
vol - > major_ver = vi - > major_ver ;
vol - > minor_ver = vi - > minor_ver ;
ntfs_attr_put_search_ctx ( ctx ) ;
unmap_mft_record ( NTFS_I ( vol - > vol_ino ) ) ;
printk ( KERN_INFO " NTFS volume version %i.%i. \n " , vol - > major_ver ,
vol - > minor_ver ) ;
2005-02-03 15:02:56 +03:00
if ( vol - > major_ver < 3 & & NVolSparseEnabled ( vol ) ) {
ntfs_warning ( vol - > sb , " Disabling sparse support due to NTFS "
" volume version %i.%i (need at least version "
" 3.0). " , vol - > major_ver , vol - > minor_ver ) ;
NVolClearSparseEnabled ( vol ) ;
}
2005-04-17 02:20:36 +04:00
# ifdef NTFS_RW
/* Make sure that no unsupported volume flags are set. */
if ( vol - > vol_flags & VOLUME_MUST_MOUNT_RO_MASK ) {
static const char * es1a = " Volume is dirty " ;
static const char * es1b = " Volume has unsupported flags set " ;
static const char * es2 = " . Run chkdsk and mount in Windows. " ;
const char * es1 ;
es1 = vol - > vol_flags & VOLUME_IS_DIRTY ? es1a : es1b ;
/* If a read-write mount, convert it to a read-only mount. */
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors= "
" continue nor on_errors= "
" remount-ro was specified%s " ,
es1 , es2 ) ;
goto iput_vol_err_out ;
}
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
} else
ntfs_warning ( sb , " %s. Will not be able to remount "
" read-write%s " , es1 , es2 ) ;
/*
* Do not set NVolErrors ( ) because ntfs_remount ( ) re - checks the
* flags which we need to do in case any flags have changed .
*/
}
/*
* Get the inode for the logfile , check it and determine if the volume
* was shutdown cleanly .
*/
2005-09-08 19:12:28 +04:00
rp = NULL ;
if ( ! load_and_check_logfile ( vol , & rp ) | |
! ntfs_is_logfile_clean ( vol - > logfile_ino , rp ) ) {
2005-04-17 02:20:36 +04:00
static const char * es1a = " Failed to load $LogFile " ;
static const char * es1b = " $LogFile is not clean " ;
static const char * es2 = " . Mount in Windows. " ;
const char * es1 ;
es1 = ! vol - > logfile_ino ? es1a : es1b ;
/* If a read-write mount, convert it to a read-only mount. */
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors= "
" continue nor on_errors= "
" remount-ro was specified%s " ,
es1 , es2 ) ;
2005-09-08 19:12:28 +04:00
if ( vol - > logfile_ino ) {
BUG_ON ( ! rp ) ;
ntfs_free ( rp ) ;
}
2005-04-17 02:20:36 +04:00
goto iput_logfile_err_out ;
}
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
} else
ntfs_warning ( sb , " %s. Will not be able to remount "
" read-write%s " , es1 , es2 ) ;
/* This will prevent a read-write remount. */
NVolSetErrors ( vol ) ;
}
2005-09-08 19:12:28 +04:00
ntfs_free ( rp ) ;
2005-06-25 19:31:27 +04:00
# endif /* NTFS_RW */
/* Get the root directory inode so we can do path lookups. */
vol - > root_ino = ntfs_iget ( sb , FILE_root ) ;
if ( IS_ERR ( vol - > root_ino ) | | is_bad_inode ( vol - > root_ino ) ) {
if ( ! IS_ERR ( vol - > root_ino ) )
iput ( vol - > root_ino ) ;
ntfs_error ( sb , " Failed to load root directory. " ) ;
goto iput_logfile_err_out ;
}
# ifdef NTFS_RW
/*
* Check if Windows is suspended to disk on the target volume . If it
* is hibernated , we must not write * anything * to the disk so set
* NVolErrors ( ) without setting the dirty volume flag and mount
* read - only . This will prevent read - write remounting and it will also
* prevent all writes .
*/
err = check_windows_hibernation_status ( vol ) ;
if ( unlikely ( err ) ) {
static const char * es1a = " Failed to determine if Windows is "
" hibernated " ;
static const char * es1b = " Windows is hibernated " ;
static const char * es2 = " . Run chkdsk. " ;
const char * es1 ;
es1 = err < 0 ? es1a : es1b ;
/* If a read-write mount, convert it to a read-only mount. */
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors= "
" continue nor on_errors= "
" remount-ro was specified%s " ,
es1 , es2 ) ;
goto iput_root_err_out ;
}
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
} else
ntfs_warning ( sb , " %s. Will not be able to remount "
" read-write%s " , es1 , es2 ) ;
/* This will prevent a read-write remount. */
NVolSetErrors ( vol ) ;
}
2005-04-17 02:20:36 +04:00
/* If (still) a read-write mount, mark the volume dirty. */
if ( ! ( sb - > s_flags & MS_RDONLY ) & &
ntfs_set_volume_flags ( vol , VOLUME_IS_DIRTY ) ) {
static const char * es1 = " Failed to set dirty bit in volume "
" information flags " ;
static const char * es2 = " . Run chkdsk. " ;
/* Convert to a read-only mount. */
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors=continue nor "
" on_errors=remount-ro was specified%s " ,
es1 , es2 ) ;
2005-06-25 19:31:27 +04:00
goto iput_root_err_out ;
2005-04-17 02:20:36 +04:00
}
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
/*
* Do not set NVolErrors ( ) because ntfs_remount ( ) might manage
* to set the dirty flag in which case all would be well .
*/
}
#if 0
// TODO: Enable this code once we start modifying anything that is
// different between NTFS 1.2 and 3.x...
/*
* If ( still ) a read - write mount , set the NT4 compatibility flag on
* newer NTFS version volumes .
*/
if ( ! ( sb - > s_flags & MS_RDONLY ) & & ( vol - > major_ver > 1 ) & &
ntfs_set_volume_flags ( vol , VOLUME_MOUNTED_ON_NT4 ) ) {
static const char * es1 = " Failed to set NT4 compatibility flag " ;
static const char * es2 = " . Run chkdsk. " ;
/* Convert to a read-only mount. */
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors=continue nor "
" on_errors=remount-ro was specified%s " ,
es1 , es2 ) ;
2005-06-25 19:31:27 +04:00
goto iput_root_err_out ;
2005-04-17 02:20:36 +04:00
}
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
NVolSetErrors ( vol ) ;
}
# endif
/* If (still) a read-write mount, empty the logfile. */
if ( ! ( sb - > s_flags & MS_RDONLY ) & &
! ntfs_empty_logfile ( vol - > logfile_ino ) ) {
static const char * es1 = " Failed to empty $LogFile " ;
static const char * es2 = " . Mount in Windows. " ;
/* Convert to a read-only mount. */
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors=continue nor "
" on_errors=remount-ro was specified%s " ,
es1 , es2 ) ;
2005-06-25 19:31:27 +04:00
goto iput_root_err_out ;
2005-04-17 02:20:36 +04:00
}
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
NVolSetErrors ( vol ) ;
}
# endif /* NTFS_RW */
/* If on NTFS versions before 3.0, we are done. */
2005-06-25 18:28:56 +04:00
if ( unlikely ( vol - > major_ver < 3 ) )
2005-04-17 02:20:36 +04:00
return TRUE ;
/* NTFS 3.0+ specific initialization. */
/* Get the security descriptors inode. */
vol - > secure_ino = ntfs_iget ( sb , FILE_Secure ) ;
if ( IS_ERR ( vol - > secure_ino ) | | is_bad_inode ( vol - > secure_ino ) ) {
if ( ! IS_ERR ( vol - > secure_ino ) )
iput ( vol - > secure_ino ) ;
ntfs_error ( sb , " Failed to load $Secure. " ) ;
goto iput_root_err_out ;
}
2005-06-25 18:28:56 +04:00
// TODO: Initialize security.
2005-04-17 02:20:36 +04:00
/* Get the extended system files' directory inode. */
vol - > extend_ino = ntfs_iget ( sb , FILE_Extend ) ;
if ( IS_ERR ( vol - > extend_ino ) | | is_bad_inode ( vol - > extend_ino ) ) {
if ( ! IS_ERR ( vol - > extend_ino ) )
iput ( vol - > extend_ino ) ;
ntfs_error ( sb , " Failed to load $Extend. " ) ;
goto iput_sec_err_out ;
}
# ifdef NTFS_RW
/* Find the quota file, load it if present, and set it up. */
if ( ! load_and_init_quota ( vol ) ) {
static const char * es1 = " Failed to load $Quota " ;
static const char * es2 = " . Run chkdsk. " ;
/* If a read-write mount, convert it to a read-only mount. */
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors= "
" continue nor on_errors= "
" remount-ro was specified%s " ,
es1 , es2 ) ;
goto iput_quota_err_out ;
}
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
} else
ntfs_warning ( sb , " %s. Will not be able to remount "
" read-write%s " , es1 , es2 ) ;
/* This will prevent a read-write remount. */
NVolSetErrors ( vol ) ;
}
/* If (still) a read-write mount, mark the quotas out of date. */
if ( ! ( sb - > s_flags & MS_RDONLY ) & &
! ntfs_mark_quotas_out_of_date ( vol ) ) {
static const char * es1 = " Failed to mark quotas out of date " ;
static const char * es2 = " . Run chkdsk. " ;
/* Convert to a read-only mount. */
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors=continue nor "
" on_errors=remount-ro was specified%s " ,
es1 , es2 ) ;
goto iput_quota_err_out ;
}
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
NVolSetErrors ( vol ) ;
}
2005-06-25 18:28:56 +04:00
/*
* Find the transaction log file ( $ UsnJrnl ) , load it if present , check
* it , and set it up .
*/
if ( ! load_and_init_usnjrnl ( vol ) ) {
static const char * es1 = " Failed to load $UsnJrnl " ;
static const char * es2 = " . Run chkdsk. " ;
/* If a read-write mount, convert it to a read-only mount. */
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors= "
" continue nor on_errors= "
" remount-ro was specified%s " ,
es1 , es2 ) ;
goto iput_usnjrnl_err_out ;
}
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
} else
ntfs_warning ( sb , " %s. Will not be able to remount "
" read-write%s " , es1 , es2 ) ;
/* This will prevent a read-write remount. */
NVolSetErrors ( vol ) ;
}
/* If (still) a read-write mount, stamp the transaction log. */
if ( ! ( sb - > s_flags & MS_RDONLY ) & & ! ntfs_stamp_usnjrnl ( vol ) ) {
static const char * es1 = " Failed to stamp transaction log "
" ($UsnJrnl) " ;
static const char * es2 = " . Run chkdsk. " ;
/* Convert to a read-only mount. */
if ( ! ( vol - > on_errors & ( ON_ERRORS_REMOUNT_RO |
ON_ERRORS_CONTINUE ) ) ) {
ntfs_error ( sb , " %s and neither on_errors=continue nor "
" on_errors=remount-ro was specified%s " ,
es1 , es2 ) ;
goto iput_usnjrnl_err_out ;
}
ntfs_error ( sb , " %s. Mounting read-only%s " , es1 , es2 ) ;
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
NVolSetErrors ( vol ) ;
}
2005-04-17 02:20:36 +04:00
# endif /* NTFS_RW */
return TRUE ;
# ifdef NTFS_RW
2005-06-25 18:28:56 +04:00
iput_usnjrnl_err_out :
if ( vol - > usnjrnl_j_ino )
iput ( vol - > usnjrnl_j_ino ) ;
if ( vol - > usnjrnl_max_ino )
iput ( vol - > usnjrnl_max_ino ) ;
if ( vol - > usnjrnl_ino )
iput ( vol - > usnjrnl_ino ) ;
2005-04-17 02:20:36 +04:00
iput_quota_err_out :
if ( vol - > quota_q_ino )
iput ( vol - > quota_q_ino ) ;
if ( vol - > quota_ino )
iput ( vol - > quota_ino ) ;
iput ( vol - > extend_ino ) ;
# endif /* NTFS_RW */
iput_sec_err_out :
iput ( vol - > secure_ino ) ;
iput_root_err_out :
iput ( vol - > root_ino ) ;
iput_logfile_err_out :
# ifdef NTFS_RW
if ( vol - > logfile_ino )
iput ( vol - > logfile_ino ) ;
iput_vol_err_out :
# endif /* NTFS_RW */
iput ( vol - > vol_ino ) ;
iput_lcnbmp_err_out :
iput ( vol - > lcnbmp_ino ) ;
iput_attrdef_err_out :
vol - > attrdef_size = 0 ;
if ( vol - > attrdef ) {
ntfs_free ( vol - > attrdef ) ;
vol - > attrdef = NULL ;
}
# ifdef NTFS_RW
iput_upcase_err_out :
# endif /* NTFS_RW */
vol - > upcase_len = 0 ;
down ( & ntfs_lock ) ;
if ( vol - > upcase = = default_upcase ) {
ntfs_nr_upcase_users - - ;
vol - > upcase = NULL ;
}
up ( & ntfs_lock ) ;
if ( vol - > upcase ) {
ntfs_free ( vol - > upcase ) ;
vol - > upcase = NULL ;
}
iput_mftbmp_err_out :
iput ( vol - > mftbmp_ino ) ;
iput_mirr_err_out :
# ifdef NTFS_RW
if ( vol - > mftmirr_ino )
iput ( vol - > mftmirr_ino ) ;
# endif /* NTFS_RW */
return FALSE ;
}
/**
* ntfs_put_super - called by the vfs to unmount a volume
* @ sb : vfs superblock of volume to unmount
*
* ntfs_put_super ( ) is called by the VFS ( from fs / super . c : : do_umount ( ) ) when
* the volume is being unmounted ( umount system call has been invoked ) and it
* releases all inodes and memory belonging to the NTFS specific part of the
* super block .
*/
static void ntfs_put_super ( struct super_block * sb )
{
ntfs_volume * vol = NTFS_SB ( sb ) ;
ntfs_debug ( " Entering. " ) ;
# ifdef NTFS_RW
/*
* Commit all inodes while they are still open in case some of them
* cause others to be dirtied .
*/
ntfs_commit_inode ( vol - > vol_ino ) ;
/* NTFS 3.0+ specific. */
if ( vol - > major_ver > = 3 ) {
2005-06-25 18:28:56 +04:00
if ( vol - > usnjrnl_j_ino )
ntfs_commit_inode ( vol - > usnjrnl_j_ino ) ;
if ( vol - > usnjrnl_max_ino )
ntfs_commit_inode ( vol - > usnjrnl_max_ino ) ;
if ( vol - > usnjrnl_ino )
ntfs_commit_inode ( vol - > usnjrnl_ino ) ;
2005-04-17 02:20:36 +04:00
if ( vol - > quota_q_ino )
ntfs_commit_inode ( vol - > quota_q_ino ) ;
if ( vol - > quota_ino )
ntfs_commit_inode ( vol - > quota_ino ) ;
if ( vol - > extend_ino )
ntfs_commit_inode ( vol - > extend_ino ) ;
if ( vol - > secure_ino )
ntfs_commit_inode ( vol - > secure_ino ) ;
}
ntfs_commit_inode ( vol - > root_ino ) ;
down_write ( & vol - > lcnbmp_lock ) ;
ntfs_commit_inode ( vol - > lcnbmp_ino ) ;
up_write ( & vol - > lcnbmp_lock ) ;
down_write ( & vol - > mftbmp_lock ) ;
ntfs_commit_inode ( vol - > mftbmp_ino ) ;
up_write ( & vol - > mftbmp_lock ) ;
if ( vol - > logfile_ino )
ntfs_commit_inode ( vol - > logfile_ino ) ;
if ( vol - > mftmirr_ino )
ntfs_commit_inode ( vol - > mftmirr_ino ) ;
ntfs_commit_inode ( vol - > mft_ino ) ;
/*
* If a read - write mount and no volume errors have occured , mark the
* volume clean . Also , re - commit all affected inodes .
*/
if ( ! ( sb - > s_flags & MS_RDONLY ) ) {
if ( ! NVolErrors ( vol ) ) {
if ( ntfs_clear_volume_flags ( vol , VOLUME_IS_DIRTY ) )
ntfs_warning ( sb , " Failed to clear dirty bit "
" in volume information "
" flags. Run chkdsk. " ) ;
ntfs_commit_inode ( vol - > vol_ino ) ;
ntfs_commit_inode ( vol - > root_ino ) ;
if ( vol - > mftmirr_ino )
ntfs_commit_inode ( vol - > mftmirr_ino ) ;
ntfs_commit_inode ( vol - > mft_ino ) ;
} else {
ntfs_warning ( sb , " Volume has errors. Leaving volume "
" marked dirty. Run chkdsk. " ) ;
}
}
# endif /* NTFS_RW */
iput ( vol - > vol_ino ) ;
vol - > vol_ino = NULL ;
/* NTFS 3.0+ specific clean up. */
if ( vol - > major_ver > = 3 ) {
# ifdef NTFS_RW
2005-06-25 18:28:56 +04:00
if ( vol - > usnjrnl_j_ino ) {
iput ( vol - > usnjrnl_j_ino ) ;
vol - > usnjrnl_j_ino = NULL ;
}
if ( vol - > usnjrnl_max_ino ) {
iput ( vol - > usnjrnl_max_ino ) ;
vol - > usnjrnl_max_ino = NULL ;
}
if ( vol - > usnjrnl_ino ) {
iput ( vol - > usnjrnl_ino ) ;
vol - > usnjrnl_ino = NULL ;
}
2005-04-17 02:20:36 +04:00
if ( vol - > quota_q_ino ) {
iput ( vol - > quota_q_ino ) ;
vol - > quota_q_ino = NULL ;
}
if ( vol - > quota_ino ) {
iput ( vol - > quota_ino ) ;
vol - > quota_ino = NULL ;
}
# endif /* NTFS_RW */
if ( vol - > extend_ino ) {
iput ( vol - > extend_ino ) ;
vol - > extend_ino = NULL ;
}
if ( vol - > secure_ino ) {
iput ( vol - > secure_ino ) ;
vol - > secure_ino = NULL ;
}
}
iput ( vol - > root_ino ) ;
vol - > root_ino = NULL ;
down_write ( & vol - > lcnbmp_lock ) ;
iput ( vol - > lcnbmp_ino ) ;
vol - > lcnbmp_ino = NULL ;
up_write ( & vol - > lcnbmp_lock ) ;
down_write ( & vol - > mftbmp_lock ) ;
iput ( vol - > mftbmp_ino ) ;
vol - > mftbmp_ino = NULL ;
up_write ( & vol - > mftbmp_lock ) ;
# ifdef NTFS_RW
if ( vol - > logfile_ino ) {
iput ( vol - > logfile_ino ) ;
vol - > logfile_ino = NULL ;
}
if ( vol - > mftmirr_ino ) {
/* Re-commit the mft mirror and mft just in case. */
ntfs_commit_inode ( vol - > mftmirr_ino ) ;
ntfs_commit_inode ( vol - > mft_ino ) ;
iput ( vol - > mftmirr_ino ) ;
vol - > mftmirr_ino = NULL ;
}
/*
* If any dirty inodes are left , throw away all mft data page cache
* pages to allow a clean umount . This should never happen any more
* due to mft . c : : ntfs_mft_writepage ( ) cleaning all the dirty pages as
* the underlying mft records are written out and cleaned . If it does ,
* happen anyway , we want to know . . .
*/
ntfs_commit_inode ( vol - > mft_ino ) ;
write_inode_now ( vol - > mft_ino , 1 ) ;
if ( ! list_empty ( & sb - > s_dirty ) ) {
const char * s1 , * s2 ;
down ( & vol - > mft_ino - > i_sem ) ;
truncate_inode_pages ( vol - > mft_ino - > i_mapping , 0 ) ;
up ( & vol - > mft_ino - > i_sem ) ;
write_inode_now ( vol - > mft_ino , 1 ) ;
if ( ! list_empty ( & sb - > s_dirty ) ) {
static const char * _s1 = " inodes " ;
static const char * _s2 = " " ;
s1 = _s1 ;
s2 = _s2 ;
} else {
static const char * _s1 = " mft pages " ;
static const char * _s2 = " They have been thrown "
" away. " ;
s1 = _s1 ;
s2 = _s2 ;
}
ntfs_error ( sb , " Dirty %s found at umount time. %sYou should "
" run chkdsk. Please email "
" linux-ntfs-dev@lists.sourceforge.net and say "
" that you saw this message. Thank you. " , s1 ,
s2 ) ;
}
# endif /* NTFS_RW */
iput ( vol - > mft_ino ) ;
vol - > mft_ino = NULL ;
/* Throw away the table of attribute definitions. */
vol - > attrdef_size = 0 ;
if ( vol - > attrdef ) {
ntfs_free ( vol - > attrdef ) ;
vol - > attrdef = NULL ;
}
vol - > upcase_len = 0 ;
/*
* Destroy the global default upcase table if necessary . Also decrease
* the number of upcase users if we are a user .
*/
down ( & ntfs_lock ) ;
if ( vol - > upcase = = default_upcase ) {
ntfs_nr_upcase_users - - ;
vol - > upcase = NULL ;
}
if ( ! ntfs_nr_upcase_users & & default_upcase ) {
ntfs_free ( default_upcase ) ;
default_upcase = NULL ;
}
if ( vol - > cluster_size < = 4096 & & ! - - ntfs_nr_compression_users )
free_compression_buffers ( ) ;
up ( & ntfs_lock ) ;
if ( vol - > upcase ) {
ntfs_free ( vol - > upcase ) ;
vol - > upcase = NULL ;
}
if ( vol - > nls_map ) {
unload_nls ( vol - > nls_map ) ;
vol - > nls_map = NULL ;
}
sb - > s_fs_info = NULL ;
kfree ( vol ) ;
return ;
}
/**
* get_nr_free_clusters - return the number of free clusters on a volume
* @ vol : ntfs volume for which to obtain free cluster count
*
* Calculate the number of free clusters on the mounted NTFS volume @ vol . We
* actually calculate the number of clusters in use instead because this
* allows us to not care about partial pages as these will be just zero filled
* and hence not be counted as allocated clusters .
*
* The only particularity is that clusters beyond the end of the logical ntfs
* volume will be marked as allocated to prevent errors which means we have to
* discount those at the end . This is important as the cluster bitmap always
* has a size in multiples of 8 bytes , i . e . up to 63 clusters could be outside
* the logical volume and marked in use when they are not as they do not exist .
*
* If any pages cannot be read we assume all clusters in the erroring pages are
* in use . This means we return an underestimate on errors which is better than
* an overestimate .
*/
static s64 get_nr_free_clusters ( ntfs_volume * vol )
{
s64 nr_free = vol - > nr_clusters ;
u32 * kaddr ;
struct address_space * mapping = vol - > lcnbmp_ino - > i_mapping ;
filler_t * readpage = ( filler_t * ) mapping - > a_ops - > readpage ;
struct page * page ;
2004-11-18 23:34:59 +03:00
pgoff_t index , max_index ;
2005-04-17 02:20:36 +04:00
ntfs_debug ( " Entering. " ) ;
/* Serialize accesses to the cluster bitmap. */
down_read ( & vol - > lcnbmp_lock ) ;
/*
* Convert the number of bits into bytes rounded up , then convert into
* multiples of PAGE_CACHE_SIZE , rounding up so that if we have one
* full and one partial page max_index = 2.
*/
max_index = ( ( ( vol - > nr_clusters + 7 ) > > 3 ) + PAGE_CACHE_SIZE - 1 ) > >
PAGE_CACHE_SHIFT ;
2004-11-18 23:34:59 +03:00
/* Use multiples of 4 bytes, thus max_size is PAGE_CACHE_SIZE / 4. */
ntfs_debug ( " Reading $Bitmap, max_index = 0x%lx, max_size = 0x%lx. " ,
max_index , PAGE_CACHE_SIZE / 4 ) ;
for ( index = 0 ; index < max_index ; index + + ) {
2005-04-17 02:20:36 +04:00
unsigned int i ;
/*
* Read the page from page cache , getting it from backing store
* if necessary , and increment the use count .
*/
page = read_cache_page ( mapping , index , ( filler_t * ) readpage ,
NULL ) ;
/* Ignore pages which errored synchronously. */
if ( IS_ERR ( page ) ) {
ntfs_debug ( " Sync read_cache_page() error. Skipping "
" page (index 0x%lx). " , index ) ;
nr_free - = PAGE_CACHE_SIZE * 8 ;
continue ;
}
wait_on_page_locked ( page ) ;
/* Ignore pages which errored asynchronously. */
if ( ! PageUptodate ( page ) ) {
ntfs_debug ( " Async read_cache_page() error. Skipping "
" page (index 0x%lx). " , index ) ;
page_cache_release ( page ) ;
nr_free - = PAGE_CACHE_SIZE * 8 ;
continue ;
}
kaddr = ( u32 * ) kmap_atomic ( page , KM_USER0 ) ;
/*
* For each 4 bytes , subtract the number of set bits . If this
* is the last page and it is partial we don ' t really care as
* it just means we do a little extra work but it won ' t affect
* the result as all out of range bytes are set to zero by
* ntfs_readpage ( ) .
*/
2004-11-18 23:34:59 +03:00
for ( i = 0 ; i < PAGE_CACHE_SIZE / 4 ; i + + )
2005-04-17 02:20:36 +04:00
nr_free - = ( s64 ) hweight32 ( kaddr [ i ] ) ;
kunmap_atomic ( kaddr , KM_USER0 ) ;
page_cache_release ( page ) ;
}
ntfs_debug ( " Finished reading $Bitmap, last index = 0x%lx. " , index - 1 ) ;
/*
* Fixup for eventual bits outside logical ntfs volume ( see function
* description above ) .
*/
if ( vol - > nr_clusters & 63 )
nr_free + = 64 - ( vol - > nr_clusters & 63 ) ;
up_read ( & vol - > lcnbmp_lock ) ;
/* If errors occured we may well have gone below zero, fix this. */
if ( nr_free < 0 )
nr_free = 0 ;
ntfs_debug ( " Exiting. " ) ;
return nr_free ;
}
/**
* __get_nr_free_mft_records - return the number of free inodes on a volume
* @ vol : ntfs volume for which to obtain free inode count
2005-02-03 15:02:56 +03:00
* @ nr_free : number of mft records in filesystem
2004-11-18 23:34:59 +03:00
* @ max_index : maximum number of pages containing set bits
2005-04-17 02:20:36 +04:00
*
* Calculate the number of free mft records ( inodes ) on the mounted NTFS
* volume @ vol . We actually calculate the number of mft records in use instead
* because this allows us to not care about partial pages as these will be just
* zero filled and hence not be counted as allocated mft record .
*
* If any pages cannot be read we assume all mft records in the erroring pages
* are in use . This means we return an underestimate on errors which is better
* than an overestimate .
*
* NOTE : Caller must hold mftbmp_lock rw_semaphore for reading or writing .
*/
2004-11-18 23:34:59 +03:00
static unsigned long __get_nr_free_mft_records ( ntfs_volume * vol ,
s64 nr_free , const pgoff_t max_index )
2005-04-17 02:20:36 +04:00
{
u32 * kaddr ;
struct address_space * mapping = vol - > mftbmp_ino - > i_mapping ;
filler_t * readpage = ( filler_t * ) mapping - > a_ops - > readpage ;
struct page * page ;
2004-11-18 23:34:59 +03:00
pgoff_t index ;
2005-04-17 02:20:36 +04:00
ntfs_debug ( " Entering. " ) ;
2004-11-18 23:34:59 +03:00
/* Use multiples of 4 bytes, thus max_size is PAGE_CACHE_SIZE / 4. */
2005-04-17 02:20:36 +04:00
ntfs_debug ( " Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = "
2004-11-18 23:34:59 +03:00
" 0x%lx. " , max_index , PAGE_CACHE_SIZE / 4 ) ;
for ( index = 0 ; index < max_index ; index + + ) {
2005-04-17 02:20:36 +04:00
unsigned int i ;
/*
* Read the page from page cache , getting it from backing store
* if necessary , and increment the use count .
*/
page = read_cache_page ( mapping , index , ( filler_t * ) readpage ,
NULL ) ;
/* Ignore pages which errored synchronously. */
if ( IS_ERR ( page ) ) {
ntfs_debug ( " Sync read_cache_page() error. Skipping "
" page (index 0x%lx). " , index ) ;
nr_free - = PAGE_CACHE_SIZE * 8 ;
continue ;
}
wait_on_page_locked ( page ) ;
/* Ignore pages which errored asynchronously. */
if ( ! PageUptodate ( page ) ) {
ntfs_debug ( " Async read_cache_page() error. Skipping "
" page (index 0x%lx). " , index ) ;
page_cache_release ( page ) ;
nr_free - = PAGE_CACHE_SIZE * 8 ;
continue ;
}
kaddr = ( u32 * ) kmap_atomic ( page , KM_USER0 ) ;
/*
* For each 4 bytes , subtract the number of set bits . If this
* is the last page and it is partial we don ' t really care as
* it just means we do a little extra work but it won ' t affect
* the result as all out of range bytes are set to zero by
* ntfs_readpage ( ) .
*/
2004-11-18 23:34:59 +03:00
for ( i = 0 ; i < PAGE_CACHE_SIZE / 4 ; i + + )
2005-04-17 02:20:36 +04:00
nr_free - = ( s64 ) hweight32 ( kaddr [ i ] ) ;
kunmap_atomic ( kaddr , KM_USER0 ) ;
page_cache_release ( page ) ;
}
ntfs_debug ( " Finished reading $MFT/$BITMAP, last index = 0x%lx. " ,
index - 1 ) ;
/* If errors occured we may well have gone below zero, fix this. */
if ( nr_free < 0 )
nr_free = 0 ;
ntfs_debug ( " Exiting. " ) ;
return nr_free ;
}
/**
* ntfs_statfs - return information about mounted NTFS volume
* @ sb : super block of mounted volume
* @ sfs : statfs structure in which to return the information
*
* Return information about the mounted NTFS volume @ sb in the statfs structure
* pointed to by @ sfs ( this is initialized with zeros before ntfs_statfs is
* called ) . We interpret the values to be correct of the moment in time at
* which we are called . Most values are variable otherwise and this isn ' t just
* the free values but the totals as well . For example we can increase the
* total number of file nodes if we run out and we can keep doing this until
* there is no more space on the volume left at all .
*
* Called from vfs_statfs which is used to handle the statfs , fstatfs , and
* ustat system calls .
*
* Return 0 on success or - errno on error .
*/
static int ntfs_statfs ( struct super_block * sb , struct kstatfs * sfs )
{
s64 size ;
2004-11-18 23:34:59 +03:00
ntfs_volume * vol = NTFS_SB ( sb ) ;
ntfs_inode * mft_ni = NTFS_I ( vol - > mft_ino ) ;
pgoff_t max_index ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
ntfs_debug ( " Entering. " ) ;
/* Type of filesystem. */
sfs - > f_type = NTFS_SB_MAGIC ;
/* Optimal transfer block size. */
sfs - > f_bsize = PAGE_CACHE_SIZE ;
/*
2005-02-03 15:02:56 +03:00
* Total data blocks in filesystem in units of f_bsize and since
2005-04-17 02:20:36 +04:00
* inodes are also stored in data blocs ( $ MFT is a file ) this is just
* the total clusters .
*/
sfs - > f_blocks = vol - > nr_clusters < < vol - > cluster_size_bits > >
PAGE_CACHE_SHIFT ;
2005-02-03 15:02:56 +03:00
/* Free data blocks in filesystem in units of f_bsize. */
2005-04-17 02:20:36 +04:00
size = get_nr_free_clusters ( vol ) < < vol - > cluster_size_bits > >
PAGE_CACHE_SHIFT ;
if ( size < 0LL )
size = 0LL ;
/* Free blocks avail to non-superuser, same as above on NTFS. */
sfs - > f_bavail = sfs - > f_bfree = size ;
/* Serialize accesses to the inode bitmap. */
down_read ( & vol - > mftbmp_lock ) ;
2004-11-18 23:34:59 +03:00
read_lock_irqsave ( & mft_ni - > size_lock , flags ) ;
size = i_size_read ( vol - > mft_ino ) > > vol - > mft_record_size_bits ;
/*
* Convert the maximum number of set bits into bytes rounded up , then
* convert into multiples of PAGE_CACHE_SIZE , rounding up so that if we
* have one full and one partial page max_index = 2.
*/
max_index = ( ( ( ( mft_ni - > initialized_size > > vol - > mft_record_size_bits )
+ 7 ) > > 3 ) + PAGE_CACHE_SIZE - 1 ) > > PAGE_CACHE_SHIFT ;
read_unlock_irqrestore ( & mft_ni - > size_lock , flags ) ;
2005-02-03 15:02:56 +03:00
/* Number of inodes in filesystem (at this point in time). */
2004-11-18 23:34:59 +03:00
sfs - > f_files = size ;
2005-04-17 02:20:36 +04:00
/* Free inodes in fs (based on current total count). */
2004-11-18 23:34:59 +03:00
sfs - > f_ffree = __get_nr_free_mft_records ( vol , size , max_index ) ;
2005-04-17 02:20:36 +04:00
up_read ( & vol - > mftbmp_lock ) ;
/*
* File system id . This is extremely * nix flavour dependent and even
* within Linux itself all fs do their own thing . I interpret this to
* mean a unique id associated with the mounted fs and not the id
2005-02-03 15:02:56 +03:00
* associated with the filesystem driver , the latter is already given
* by the filesystem type in sfs - > f_type . Thus we use the 64 - bit
2005-04-17 02:20:36 +04:00
* volume serial number splitting it into two 32 - bit parts . We enter
* the least significant 32 - bits in f_fsid [ 0 ] and the most significant
* 32 - bits in f_fsid [ 1 ] .
*/
sfs - > f_fsid . val [ 0 ] = vol - > serial_no & 0xffffffff ;
sfs - > f_fsid . val [ 1 ] = ( vol - > serial_no > > 32 ) & 0xffffffff ;
/* Maximum length of filenames. */
sfs - > f_namelen = NTFS_MAX_NAME_LEN ;
return 0 ;
}
/**
* The complete super operations .
*/
static struct super_operations ntfs_sops = {
. alloc_inode = ntfs_alloc_big_inode , /* VFS: Allocate new inode. */
. destroy_inode = ntfs_destroy_big_inode , /* VFS: Deallocate inode. */
. put_inode = ntfs_put_inode , /* VFS: Called just before
the inode reference count
is decreased . */
# ifdef NTFS_RW
//.dirty_inode = NULL, /* VFS: Called from
// __mark_inode_dirty(). */
. write_inode = ntfs_write_inode , /* VFS: Write dirty inode to
disk . */
//.drop_inode = NULL, /* VFS: Called just after the
// inode reference count has
// been decreased to zero.
// NOTE: The inode lock is
// held. See fs/inode.c::
// generic_drop_inode(). */
//.delete_inode = NULL, /* VFS: Delete inode from disk.
// Called when i_count becomes
// 0 and i_nlink is also 0. */
//.write_super = NULL, /* Flush dirty super block to
// disk. */
//.sync_fs = NULL, /* ? */
//.write_super_lockfs = NULL, /* ? */
//.unlockfs = NULL, /* ? */
# endif /* NTFS_RW */
. put_super = ntfs_put_super , /* Syscall: umount. */
. statfs = ntfs_statfs , /* Syscall: statfs */
. remount_fs = ntfs_remount , /* Syscall: mount -o remount. */
. clear_inode = ntfs_clear_big_inode , /* VFS: Called when an inode is
removed from memory . */
//.umount_begin = NULL, /* Forced umount. */
. show_options = ntfs_show_options , /* Show mount options in
proc . */
} ;
/**
2005-02-03 15:02:56 +03:00
* ntfs_fill_super - mount an ntfs filesystem
* @ sb : super block of ntfs filesystem to mount
2005-04-17 02:20:36 +04:00
* @ opt : string containing the mount options
* @ silent : silence error output
*
* ntfs_fill_super ( ) is called by the VFS to mount the device described by @ sb
2005-02-03 15:02:56 +03:00
* with the mount otions in @ data with the NTFS filesystem .
2005-04-17 02:20:36 +04:00
*
* If @ silent is true , remain silent even if errors are detected . This is used
2005-02-03 15:02:56 +03:00
* during bootup , when the kernel tries to mount the root filesystem with all
* registered filesystems one after the other until one succeeds . This implies
* that all filesystems except the correct one will quite correctly and
2005-04-17 02:20:36 +04:00
* expectedly return an error , but nobody wants to see error messages when in
* fact this is what is supposed to happen .
*
* NOTE : @ sb - > s_flags contains the mount options flags .
*/
static int ntfs_fill_super ( struct super_block * sb , void * opt , const int silent )
{
ntfs_volume * vol ;
struct buffer_head * bh ;
struct inode * tmp_ino ;
int result ;
ntfs_debug ( " Entering. " ) ;
# ifndef NTFS_RW
sb - > s_flags | = MS_RDONLY | MS_NOATIME | MS_NODIRATIME ;
# endif /* ! NTFS_RW */
/* Allocate a new ntfs_volume and place it in sb->s_fs_info. */
sb - > s_fs_info = kmalloc ( sizeof ( ntfs_volume ) , GFP_NOFS ) ;
vol = NTFS_SB ( sb ) ;
if ( ! vol ) {
if ( ! silent )
ntfs_error ( sb , " Allocation of NTFS volume structure "
" failed. Aborting mount... " ) ;
return - ENOMEM ;
}
/* Initialize ntfs_volume structure. */
2005-05-27 19:42:56 +04:00
* vol = ( ntfs_volume ) {
. sb = sb ,
/*
* Default is group and other don ' t have any access to files or
* directories while owner has full access . Further , files by
* default are not executable but directories are of course
* browseable .
*/
. fmask = 0177 ,
. dmask = 0077 ,
} ;
2005-04-17 02:20:36 +04:00
init_rwsem ( & vol - > mftbmp_lock ) ;
init_rwsem ( & vol - > lcnbmp_lock ) ;
unlock_kernel ( ) ;
2005-02-03 15:02:56 +03:00
/* By default, enable sparse support. */
NVolSetSparseEnabled ( vol ) ;
2005-04-17 02:20:36 +04:00
/* Important to get the mount options dealt with now. */
if ( ! parse_options ( vol , ( char * ) opt ) )
goto err_out_now ;
/*
* TODO : Fail safety check . In the future we should really be able to
* cope with this being the case , but for now just bail out .
*/
if ( bdev_hardsect_size ( sb - > s_bdev ) > NTFS_BLOCK_SIZE ) {
if ( ! silent )
ntfs_error ( sb , " Device has unsupported hardsect_size. " ) ;
goto err_out_now ;
}
/* Setup the device access block size to NTFS_BLOCK_SIZE. */
if ( sb_set_blocksize ( sb , NTFS_BLOCK_SIZE ) ! = NTFS_BLOCK_SIZE ) {
if ( ! silent )
ntfs_error ( sb , " Unable to set block size. " ) ;
goto err_out_now ;
}
/* Get the size of the device in units of NTFS_BLOCK_SIZE bytes. */
2004-11-18 23:34:59 +03:00
vol - > nr_blocks = i_size_read ( sb - > s_bdev - > bd_inode ) > >
NTFS_BLOCK_SIZE_BITS ;
2005-04-17 02:20:36 +04:00
/* Read the boot sector and return unlocked buffer head to it. */
if ( ! ( bh = read_ntfs_boot_sector ( sb , silent ) ) ) {
if ( ! silent )
ntfs_error ( sb , " Not an NTFS volume. " ) ;
goto err_out_now ;
}
/*
* Extract the data from the boot sector and setup the ntfs super block
* using it .
*/
result = parse_ntfs_boot_sector ( vol , ( NTFS_BOOT_SECTOR * ) bh - > b_data ) ;
/* Initialize the cluster and mft allocators. */
ntfs_setup_allocators ( vol ) ;
brelse ( bh ) ;
if ( ! result ) {
if ( ! silent )
ntfs_error ( sb , " Unsupported NTFS filesystem. " ) ;
goto err_out_now ;
}
/*
* TODO : When we start coping with sector sizes different from
* NTFS_BLOCK_SIZE , we now probably need to set the blocksize of the
* device ( probably to NTFS_BLOCK_SIZE ) .
*/
/* Setup remaining fields in the super block. */
sb - > s_magic = NTFS_SB_MAGIC ;
/*
* Ntfs allows 63 bits for the file size , i . e . correct would be :
* sb - > s_maxbytes = ~ 0ULL > > 1 ;
* But the kernel uses a long as the page cache page index which on
* 32 - bit architectures is only 32 - bits . MAX_LFS_FILESIZE is kernel
* defined to the maximum the page cache page index can cope with
* without overflowing the index or to 2 ^ 63 - 1 , whichever is smaller .
*/
sb - > s_maxbytes = MAX_LFS_FILESIZE ;
sb - > s_time_gran = 100 ;
/*
* Now load the metadata required for the page cache and our address
* space operations to function . We do this by setting up a specialised
* read_inode method and then just calling the normal iget ( ) to obtain
* the inode for $ MFT which is sufficient to allow our normal inode
* operations and associated address space operations to function .
*/
sb - > s_op = & ntfs_sops ;
tmp_ino = new_inode ( sb ) ;
if ( ! tmp_ino ) {
if ( ! silent )
ntfs_error ( sb , " Failed to load essential metadata. " ) ;
goto err_out_now ;
}
tmp_ino - > i_ino = FILE_MFT ;
insert_inode_hash ( tmp_ino ) ;
if ( ntfs_read_inode_mount ( tmp_ino ) < 0 ) {
if ( ! silent )
ntfs_error ( sb , " Failed to load essential metadata. " ) ;
goto iput_tmp_ino_err_out_now ;
}
down ( & ntfs_lock ) ;
/*
* The current mount is a compression user if the cluster size is
* less than or equal 4 kiB .
*/
if ( vol - > cluster_size < = 4096 & & ! ntfs_nr_compression_users + + ) {
result = allocate_compression_buffers ( ) ;
if ( result ) {
ntfs_error ( NULL , " Failed to allocate buffers "
" for compression engine. " ) ;
ntfs_nr_compression_users - - ;
up ( & ntfs_lock ) ;
goto iput_tmp_ino_err_out_now ;
}
}
/*
* Generate the global default upcase table if necessary . Also
* temporarily increment the number of upcase users to avoid race
* conditions with concurrent ( u ) mounts .
*/
if ( ! default_upcase )
default_upcase = generate_default_upcase ( ) ;
ntfs_nr_upcase_users + + ;
up ( & ntfs_lock ) ;
/*
* From now on , ignore @ silent parameter . If we fail below this line ,
* it will be due to a corrupt fs or a system error , so we report it .
*/
/*
* Open the system files with normal access functions and complete
* setting up the ntfs super block .
*/
if ( ! load_system_files ( vol ) ) {
ntfs_error ( sb , " Failed to load system files. " ) ;
goto unl_upcase_iput_tmp_ino_err_out_now ;
}
if ( ( sb - > s_root = d_alloc_root ( vol - > root_ino ) ) ) {
/* We increment i_count simulating an ntfs_iget(). */
atomic_inc ( & vol - > root_ino - > i_count ) ;
ntfs_debug ( " Exiting, status successful. " ) ;
/* Release the default upcase if it has no users. */
down ( & ntfs_lock ) ;
if ( ! - - ntfs_nr_upcase_users & & default_upcase ) {
ntfs_free ( default_upcase ) ;
default_upcase = NULL ;
}
up ( & ntfs_lock ) ;
sb - > s_export_op = & ntfs_export_ops ;
lock_kernel ( ) ;
return 0 ;
}
ntfs_error ( sb , " Failed to allocate root directory. " ) ;
/* Clean up after the successful load_system_files() call from above. */
// TODO: Use ntfs_put_super() instead of repeating all this code...
// FIXME: Should mark the volume clean as the error is most likely
// -ENOMEM.
iput ( vol - > vol_ino ) ;
vol - > vol_ino = NULL ;
/* NTFS 3.0+ specific clean up. */
if ( vol - > major_ver > = 3 ) {
# ifdef NTFS_RW
2005-06-25 18:28:56 +04:00
if ( vol - > usnjrnl_j_ino ) {
iput ( vol - > usnjrnl_j_ino ) ;
vol - > usnjrnl_j_ino = NULL ;
}
if ( vol - > usnjrnl_max_ino ) {
iput ( vol - > usnjrnl_max_ino ) ;
vol - > usnjrnl_max_ino = NULL ;
}
if ( vol - > usnjrnl_ino ) {
iput ( vol - > usnjrnl_ino ) ;
vol - > usnjrnl_ino = NULL ;
}
2005-04-17 02:20:36 +04:00
if ( vol - > quota_q_ino ) {
iput ( vol - > quota_q_ino ) ;
vol - > quota_q_ino = NULL ;
}
if ( vol - > quota_ino ) {
iput ( vol - > quota_ino ) ;
vol - > quota_ino = NULL ;
}
# endif /* NTFS_RW */
if ( vol - > extend_ino ) {
iput ( vol - > extend_ino ) ;
vol - > extend_ino = NULL ;
}
if ( vol - > secure_ino ) {
iput ( vol - > secure_ino ) ;
vol - > secure_ino = NULL ;
}
}
iput ( vol - > root_ino ) ;
vol - > root_ino = NULL ;
iput ( vol - > lcnbmp_ino ) ;
vol - > lcnbmp_ino = NULL ;
iput ( vol - > mftbmp_ino ) ;
vol - > mftbmp_ino = NULL ;
# ifdef NTFS_RW
if ( vol - > logfile_ino ) {
iput ( vol - > logfile_ino ) ;
vol - > logfile_ino = NULL ;
}
if ( vol - > mftmirr_ino ) {
iput ( vol - > mftmirr_ino ) ;
vol - > mftmirr_ino = NULL ;
}
# endif /* NTFS_RW */
/* Throw away the table of attribute definitions. */
vol - > attrdef_size = 0 ;
if ( vol - > attrdef ) {
ntfs_free ( vol - > attrdef ) ;
vol - > attrdef = NULL ;
}
vol - > upcase_len = 0 ;
down ( & ntfs_lock ) ;
if ( vol - > upcase = = default_upcase ) {
ntfs_nr_upcase_users - - ;
vol - > upcase = NULL ;
}
up ( & ntfs_lock ) ;
if ( vol - > upcase ) {
ntfs_free ( vol - > upcase ) ;
vol - > upcase = NULL ;
}
if ( vol - > nls_map ) {
unload_nls ( vol - > nls_map ) ;
vol - > nls_map = NULL ;
}
/* Error exit code path. */
unl_upcase_iput_tmp_ino_err_out_now :
/*
* Decrease the number of upcase users and destroy the global default
* upcase table if necessary .
*/
down ( & ntfs_lock ) ;
if ( ! - - ntfs_nr_upcase_users & & default_upcase ) {
ntfs_free ( default_upcase ) ;
default_upcase = NULL ;
}
if ( vol - > cluster_size < = 4096 & & ! - - ntfs_nr_compression_users )
free_compression_buffers ( ) ;
up ( & ntfs_lock ) ;
iput_tmp_ino_err_out_now :
iput ( tmp_ino ) ;
if ( vol - > mft_ino & & vol - > mft_ino ! = tmp_ino )
iput ( vol - > mft_ino ) ;
vol - > mft_ino = NULL ;
/*
* This is needed to get ntfs_clear_extent_inode ( ) called for each
* inode we have ever called ntfs_iget ( ) / iput ( ) on , otherwise we A )
* leak resources and B ) a subsequent mount fails automatically due to
* ntfs_iget ( ) never calling down into our ntfs_read_locked_inode ( )
* method again . . . FIXME : Do we need to do this twice now because of
* attribute inodes ? I think not , so leave as is for now . . . ( AIA )
*/
if ( invalidate_inodes ( sb ) ) {
ntfs_error ( sb , " Busy inodes left. This is most likely a NTFS "
" driver bug. " ) ;
/* Copied from fs/super.c. I just love this message. (-; */
printk ( " NTFS: Busy inodes after umount. Self-destruct in 5 "
" seconds. Have a nice day... \n " ) ;
}
/* Errors at this stage are irrelevant. */
err_out_now :
lock_kernel ( ) ;
sb - > s_fs_info = NULL ;
kfree ( vol ) ;
ntfs_debug ( " Failed, returning -EINVAL. " ) ;
return - EINVAL ;
}
/*
* This is a slab cache to optimize allocations and deallocations of Unicode
* strings of the maximum length allowed by NTFS , which is NTFS_MAX_NAME_LEN
* ( 255 ) Unicode characters + a terminating NULL Unicode character .
*/
kmem_cache_t * ntfs_name_cache ;
2005-04-06 19:09:21 +04:00
/* Slab caches for efficient allocation/deallocation of inodes. */
2005-04-17 02:20:36 +04:00
kmem_cache_t * ntfs_inode_cache ;
kmem_cache_t * ntfs_big_inode_cache ;
/* Init once constructor for the inode slab cache. */
static void ntfs_big_inode_init_once ( void * foo , kmem_cache_t * cachep ,
unsigned long flags )
{
ntfs_inode * ni = ( ntfs_inode * ) foo ;
if ( ( flags & ( SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR ) ) = =
SLAB_CTOR_CONSTRUCTOR )
inode_init_once ( VFS_I ( ni ) ) ;
}
/*
* Slab caches to optimize allocations and deallocations of attribute search
* contexts and index contexts , respectively .
*/
kmem_cache_t * ntfs_attr_ctx_cache ;
kmem_cache_t * ntfs_index_ctx_cache ;
/* Driver wide semaphore. */
DECLARE_MUTEX ( ntfs_lock ) ;
static struct super_block * ntfs_get_sb ( struct file_system_type * fs_type ,
int flags , const char * dev_name , void * data )
{
return get_sb_bdev ( fs_type , flags , dev_name , data , ntfs_fill_super ) ;
}
static struct file_system_type ntfs_fs_type = {
. owner = THIS_MODULE ,
. name = " ntfs " ,
. get_sb = ntfs_get_sb ,
. kill_sb = kill_block_super ,
. fs_flags = FS_REQUIRES_DEV ,
} ;
/* Stable names for the slab caches. */
static const char ntfs_index_ctx_cache_name [ ] = " ntfs_index_ctx_cache " ;
static const char ntfs_attr_ctx_cache_name [ ] = " ntfs_attr_ctx_cache " ;
static const char ntfs_name_cache_name [ ] = " ntfs_name_cache " ;
static const char ntfs_inode_cache_name [ ] = " ntfs_inode_cache " ;
static const char ntfs_big_inode_cache_name [ ] = " ntfs_big_inode_cache " ;
static int __init init_ntfs_fs ( void )
{
int err = 0 ;
/* This may be ugly but it results in pretty output so who cares. (-8 */
printk ( KERN_INFO " NTFS driver " NTFS_VERSION " [Flags: R/ "
# ifdef NTFS_RW
" W "
# else
" O "
# endif
# ifdef DEBUG
" DEBUG "
# endif
# ifdef MODULE
" MODULE "
# endif
" ]. \n " ) ;
ntfs_debug ( " Debug messages are enabled. " ) ;
ntfs_index_ctx_cache = kmem_cache_create ( ntfs_index_ctx_cache_name ,
sizeof ( ntfs_index_context ) , 0 /* offset */ ,
SLAB_HWCACHE_ALIGN , NULL /* ctor */ , NULL /* dtor */ ) ;
if ( ! ntfs_index_ctx_cache ) {
printk ( KERN_CRIT " NTFS: Failed to create %s! \n " ,
ntfs_index_ctx_cache_name ) ;
goto ictx_err_out ;
}
ntfs_attr_ctx_cache = kmem_cache_create ( ntfs_attr_ctx_cache_name ,
sizeof ( ntfs_attr_search_ctx ) , 0 /* offset */ ,
SLAB_HWCACHE_ALIGN , NULL /* ctor */ , NULL /* dtor */ ) ;
if ( ! ntfs_attr_ctx_cache ) {
printk ( KERN_CRIT " NTFS: Failed to create %s! \n " ,
ntfs_attr_ctx_cache_name ) ;
goto actx_err_out ;
}
ntfs_name_cache = kmem_cache_create ( ntfs_name_cache_name ,
( NTFS_MAX_NAME_LEN + 1 ) * sizeof ( ntfschar ) , 0 ,
SLAB_HWCACHE_ALIGN , NULL , NULL ) ;
if ( ! ntfs_name_cache ) {
printk ( KERN_CRIT " NTFS: Failed to create %s! \n " ,
ntfs_name_cache_name ) ;
goto name_err_out ;
}
ntfs_inode_cache = kmem_cache_create ( ntfs_inode_cache_name ,
sizeof ( ntfs_inode ) , 0 ,
SLAB_RECLAIM_ACCOUNT , NULL , NULL ) ;
if ( ! ntfs_inode_cache ) {
printk ( KERN_CRIT " NTFS: Failed to create %s! \n " ,
ntfs_inode_cache_name ) ;
goto inode_err_out ;
}
ntfs_big_inode_cache = kmem_cache_create ( ntfs_big_inode_cache_name ,
sizeof ( big_ntfs_inode ) , 0 ,
SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT ,
ntfs_big_inode_init_once , NULL ) ;
if ( ! ntfs_big_inode_cache ) {
printk ( KERN_CRIT " NTFS: Failed to create %s! \n " ,
ntfs_big_inode_cache_name ) ;
goto big_inode_err_out ;
}
/* Register the ntfs sysctls. */
err = ntfs_sysctl ( 1 ) ;
if ( err ) {
printk ( KERN_CRIT " NTFS: Failed to register NTFS sysctls! \n " ) ;
goto sysctl_err_out ;
}
err = register_filesystem ( & ntfs_fs_type ) ;
if ( ! err ) {
ntfs_debug ( " NTFS driver registered successfully. " ) ;
return 0 ; /* Success! */
}
2005-02-03 15:02:56 +03:00
printk ( KERN_CRIT " NTFS: Failed to register NTFS filesystem driver! \n " ) ;
2005-04-17 02:20:36 +04:00
sysctl_err_out :
kmem_cache_destroy ( ntfs_big_inode_cache ) ;
big_inode_err_out :
kmem_cache_destroy ( ntfs_inode_cache ) ;
inode_err_out :
kmem_cache_destroy ( ntfs_name_cache ) ;
name_err_out :
kmem_cache_destroy ( ntfs_attr_ctx_cache ) ;
actx_err_out :
kmem_cache_destroy ( ntfs_index_ctx_cache ) ;
ictx_err_out :
if ( ! err ) {
2005-02-03 15:02:56 +03:00
printk ( KERN_CRIT " NTFS: Aborting NTFS filesystem driver "
2005-04-17 02:20:36 +04:00
" registration... \n " ) ;
err = - ENOMEM ;
}
return err ;
}
static void __exit exit_ntfs_fs ( void )
{
int err = 0 ;
ntfs_debug ( " Unregistering NTFS driver. " ) ;
unregister_filesystem ( & ntfs_fs_type ) ;
if ( kmem_cache_destroy ( ntfs_big_inode_cache ) & & ( err = 1 ) )
printk ( KERN_CRIT " NTFS: Failed to destory %s. \n " ,
ntfs_big_inode_cache_name ) ;
if ( kmem_cache_destroy ( ntfs_inode_cache ) & & ( err = 1 ) )
printk ( KERN_CRIT " NTFS: Failed to destory %s. \n " ,
ntfs_inode_cache_name ) ;
if ( kmem_cache_destroy ( ntfs_name_cache ) & & ( err = 1 ) )
printk ( KERN_CRIT " NTFS: Failed to destory %s. \n " ,
ntfs_name_cache_name ) ;
if ( kmem_cache_destroy ( ntfs_attr_ctx_cache ) & & ( err = 1 ) )
printk ( KERN_CRIT " NTFS: Failed to destory %s. \n " ,
ntfs_attr_ctx_cache_name ) ;
if ( kmem_cache_destroy ( ntfs_index_ctx_cache ) & & ( err = 1 ) )
printk ( KERN_CRIT " NTFS: Failed to destory %s. \n " ,
ntfs_index_ctx_cache_name ) ;
if ( err )
printk ( KERN_CRIT " NTFS: This causes memory to leak! There is "
" probably a BUG in the driver! Please report "
" you saw this message to "
" linux-ntfs-dev@lists.sourceforge.net \n " ) ;
/* Unregister the ntfs sysctls. */
ntfs_sysctl ( 0 ) ;
}
MODULE_AUTHOR ( " Anton Altaparmakov <aia21@cantab.net> " ) ;
2005-02-03 15:02:56 +03:00
MODULE_DESCRIPTION ( " NTFS 1.2/3.x driver - Copyright (c) 2001-2005 Anton Altaparmakov " ) ;
2005-04-17 02:20:36 +04:00
MODULE_VERSION ( NTFS_VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;
# ifdef DEBUG
module_param ( debug_msgs , bool , 0 ) ;
MODULE_PARM_DESC ( debug_msgs , " Enable debug messages. " ) ;
# endif
module_init ( init_ntfs_fs )
module_exit ( exit_ntfs_fs )