diff options
Diffstat (limited to 'fs/ntfs/super.c')
-rw-r--r-- | fs/ntfs/super.c | 197 |
1 files changed, 132 insertions, 65 deletions
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index c3a3f1a8310b..368a8ec10668 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1,7 +1,7 @@ /* * super.c - NTFS kernel super block handling. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2005 Anton Altaparmakov + * Copyright (c) 2001-2006 Anton Altaparmakov * Copyright (c) 2001,2002 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -22,6 +22,7 @@ #include <linux/stddef.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/spinlock.h> #include <linux/blkdev.h> /* For bdev_hardsect_size(). */ @@ -471,9 +472,16 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) ntfs_error(sb, "Volume is dirty and read-only%s", es); return -EROFS; } + if (vol->vol_flags & VOLUME_MODIFIED_BY_CHKDSK) { + ntfs_error(sb, "Volume has been modified by chkdsk " + "and is 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); + ntfs_error(sb, "Volume has unsupported flags set " + "(0x%x) and is read-only%s", + (unsigned)le16_to_cpu(vol->vol_flags), + es); return -EROFS; } if (ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) { @@ -641,7 +649,7 @@ static struct buffer_head *read_ntfs_boot_sector(struct super_block *sb, { 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; + sector_t nr_blocks = NTFS_SB(sb)->nr_blocks; /* Try to read primary boot sector. */ if ((bh_primary = sb_bread(sb, 0))) { @@ -688,13 +696,18 @@ hotfix_primary_boot_sector: /* * 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. + * primary boot sector. Note we only copy the actual boot + * sector structure, not the actual whole device sector as that + * may be bigger and would potentially damage the $Boot system + * file (FIXME: Would be nice to know if the backup boot sector + * on a large sector device contains the whole boot loader or + * just the first 512 bytes). */ 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); + NTFS_BLOCK_SIZE); mark_buffer_dirty(bh_primary); sync_dirty_buffer(bh_primary); if (buffer_uptodate(bh_primary)) { @@ -733,9 +746,13 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) 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."); + if (vol->sector_size < vol->sb->s_blocksize) { + ntfs_error(vol->sb, "Sector size (%i) is smaller than the " + "device block size (%lu). This is not " + "supported. Sorry.", vol->sector_size, + vol->sb->s_blocksize); + return FALSE; + } 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", @@ -748,16 +765,11 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) 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."); + ntfs_debug("vol->cluster_size_bits = %i", vol->cluster_size_bits); + if (vol->cluster_size < vol->sector_size) { + ntfs_error(vol->sb, "Cluster size (%i) is smaller than the " + "sector size (%i). This is not supported. " + "Sorry.", vol->cluster_size, vol->sector_size); return FALSE; } clusters_per_mft_record = b->clusters_per_mft_record; @@ -786,11 +798,18 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) * 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). " + ntfs_error(vol->sb, "Mft record size (%i) exceeds the " + "PAGE_CACHE_SIZE on your system (%lu). " "This is not supported. Sorry.", - vol->mft_record_size, vol->mft_record_size, - PAGE_CACHE_SIZE, PAGE_CACHE_SIZE); + vol->mft_record_size, PAGE_CACHE_SIZE); + return FALSE; + } + /* We cannot support mft record sizes below the sector size. */ + if (vol->mft_record_size < vol->sector_size) { + ntfs_error(vol->sb, "Mft record size (%i) is smaller than the " + "sector size (%i). This is not supported. " + "Sorry.", vol->mft_record_size, + vol->sector_size); return FALSE; } clusters_per_index_record = b->clusters_per_index_record; @@ -816,6 +835,14 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ntfs_debug("vol->index_record_size_bits = %i (0x%x)", vol->index_record_size_bits, vol->index_record_size_bits); + /* We cannot support index record sizes below the sector size. */ + if (vol->index_record_size < vol->sector_size) { + ntfs_error(vol->sb, "Index record size (%i) is smaller than " + "the sector size (%i). This is not " + "supported. Sorry.", vol->index_record_size, + vol->sector_size); + return FALSE; + } /* * 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 @@ -845,15 +872,18 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) } ll = sle64_to_cpu(b->mft_lcn); if (ll >= vol->nr_clusters) { - ntfs_error(vol->sb, "MFT LCN is beyond end of volume. Weird."); + ntfs_error(vol->sb, "MFT LCN (%lli, 0x%llx) is beyond end of " + "volume. Weird.", (unsigned long long)ll, + (unsigned long long)ll); 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."); + ntfs_error(vol->sb, "MFTMirr LCN (%lli, 0x%llx) is beyond end " + "of volume. Weird.", (unsigned long long)ll, + (unsigned long long)ll); return FALSE; } vol->mftmirr_lcn = ll; @@ -1822,11 +1852,24 @@ get_ctx_vol_failed: /* 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; + static const char *es1b = "Volume has been modified by chkdsk"; + static const char *es1c = "Volume has unsupported flags set"; + static const char *es2a = ". Run chkdsk and mount in Windows."; + static const char *es2b = ". Mount in Windows."; + const char *es1, *es2; + + es2 = es2a; + if (vol->vol_flags & VOLUME_IS_DIRTY) + es1 = es1a; + else if (vol->vol_flags & VOLUME_MODIFIED_BY_CHKDSK) { + es1 = es1b; + es2 = es2b; + } else { + es1 = es1c; + ntfs_warning(sb, "Unsupported volume flags 0x%x " + "encountered.", + (unsigned)le16_to_cpu(vol->vol_flags)); + } /* 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 | @@ -2685,7 +2728,7 @@ 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; + int blocksize, result; ntfs_debug("Entering."); #ifndef NTFS_RW @@ -2724,60 +2767,85 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) if (!parse_options(vol, (char*)opt)) goto err_out_now; + /* We support sector sizes up to the PAGE_CACHE_SIZE. */ + if (bdev_hardsect_size(sb->s_bdev) > PAGE_CACHE_SIZE) { + if (!silent) + ntfs_error(sb, "Device has unsupported sector size " + "(%i). The maximum supported sector " + "size on this architecture is %lu " + "bytes.", + bdev_hardsect_size(sb->s_bdev), + PAGE_CACHE_SIZE); + 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. + * Setup the device access block size to NTFS_BLOCK_SIZE or the hard + * sector size, whichever is bigger. */ - if (bdev_hardsect_size(sb->s_bdev) > NTFS_BLOCK_SIZE) { + blocksize = sb_min_blocksize(sb, NTFS_BLOCK_SIZE); + if (blocksize < NTFS_BLOCK_SIZE) { if (!silent) - ntfs_error(sb, "Device has unsupported hardsect_size."); + ntfs_error(sb, "Unable to set device block 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) { + BUG_ON(blocksize != sb->s_blocksize); + ntfs_debug("Set device block size to %i bytes (block size bits %i).", + blocksize, sb->s_blocksize_bits); + /* Determine the size of the device in units of block_size bytes. */ + if (!i_size_read(sb->s_bdev->bd_inode)) { if (!silent) - ntfs_error(sb, "Unable to set block size."); + ntfs_error(sb, "Unable to determine device size."); goto err_out_now; } - - /* Get the size of the device in units of NTFS_BLOCK_SIZE bytes. */ vol->nr_blocks = i_size_read(sb->s_bdev->bd_inode) >> - NTFS_BLOCK_SIZE_BITS; - + sb->s_blocksize_bits; /* 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 + * Extract the data from the boot sector and setup the ntfs volume * 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). + * If the boot sector indicates a sector size bigger than the current + * device block size, switch the device block size to the sector size. + * TODO: It may be possible to support this case even when the set + * below fails, we would just be breaking up the i/o for each sector + * into multiple blocks for i/o purposes but otherwise it should just + * work. However it is safer to leave disabled until someone hits this + * error message and then we can get them to try it without the setting + * so we know for sure that it works. */ - + if (vol->sector_size > blocksize) { + blocksize = sb_set_blocksize(sb, vol->sector_size); + if (blocksize != vol->sector_size) { + if (!silent) + ntfs_error(sb, "Unable to set device block " + "size to sector size (%i).", + vol->sector_size); + goto err_out_now; + } + BUG_ON(blocksize != sb->s_blocksize); + vol->nr_blocks = i_size_read(sb->s_bdev->bd_inode) >> + sb->s_blocksize_bits; + ntfs_debug("Changed device block size to %i bytes (block size " + "bits %i) to match volume sector size.", + blocksize, sb->s_blocksize_bits); + } + /* Initialize the cluster and mft allocators. */ + ntfs_setup_allocators(vol); /* 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; @@ -2787,9 +2855,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) * without overflowing the index or to 2^63 - 1, whichever is smaller. */ sb->s_maxbytes = MAX_LFS_FILESIZE; - + /* Ntfs measures time in 100ns intervals. */ 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 @@ -2987,14 +3054,14 @@ err_out_now: * 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; +struct kmem_cache *ntfs_name_cache; /* Slab caches for efficient allocation/deallocation of inodes. */ -kmem_cache_t *ntfs_inode_cache; -kmem_cache_t *ntfs_big_inode_cache; +struct kmem_cache *ntfs_inode_cache; +struct kmem_cache *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, +static void ntfs_big_inode_init_once(void *foo, struct kmem_cache *cachep, unsigned long flags) { ntfs_inode *ni = (ntfs_inode *)foo; @@ -3008,8 +3075,8 @@ static void ntfs_big_inode_init_once(void *foo, kmem_cache_t *cachep, * 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; +struct kmem_cache *ntfs_attr_ctx_cache; +struct kmem_cache *ntfs_index_ctx_cache; /* Driver wide semaphore. */ DECLARE_MUTEX(ntfs_lock); |