Message ID | 20100812222949.GC22777@mail.oracle.com |
---|---|
State | Accepted, archived |
Headers | show |
You can add my: Acked-by: Andreas Dilger <adilger@dilger.ca> On 2010-08-12, at 16:29, Joel Becker wrote: > On Thu, Aug 12, 2010 at 03:32:27PM -0600, Andreas Dilger wrote: >>> If I change the BUG_ON()s to -EINVAL, does that work? Or do you >>> have some way you'd like to allow non-pagecache filesystems to use this >>> as well? >> >> That's probably fine for now. If anyone complains, we can always change it later, since they can't possibly depend on this function yet... > > How's this: > >> From f988b04c58039ae9a65fea537656088ea56a8072 Mon Sep 17 00:00:00 2001 >> From: Patrick J. LoPresti <lopresti@gmail.com> > Date: Thu, 22 Jul 2010 15:03:41 -0700 > Subject: [PATCH] ext3/ext4: Factor out disk addressability check > > As part of adding support for OCFS2 to mount huge volumes, we need to > check that the sector_t and page cache of the system are capable of > addressing the entire volume. > > An identical check already appears in ext3 and ext4. This patch moves > the addressability check into its own function in fs/libfs.c and > modifies ext3 and ext4 to invoke it. > > [Edited to -EINVAL instead of BUG_ON() for bad blocksize_bits -- Joel] > > Signed-off-by: Patrick LoPresti <lopresti@gmail.com> > Cc: linux-ext4@vger.kernel.org > Signed-off-by: Joel Becker <joel.becker@oracle.com> > --- > fs/ext3/super.c | 4 ++-- > fs/ext4/super.c | 8 +++----- > fs/libfs.c | 29 +++++++++++++++++++++++++++++ > include/linux/fs.h | 2 ++ > 4 files changed, 36 insertions(+), 7 deletions(-) > > diff --git a/fs/ext3/super.c b/fs/ext3/super.c > index 6c953bb..d0643db 100644 > --- a/fs/ext3/super.c > +++ b/fs/ext3/super.c > @@ -1862,8 +1862,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) > goto failed_mount; > } > > - if (le32_to_cpu(es->s_blocks_count) > > - (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) { > + if (generic_check_addressable(sb->s_blocksize_bits, > + le32_to_cpu(es->s_blocks_count))) { > ext3_msg(sb, KERN_ERR, > "error: filesystem is too large to mount safely"); > if (sizeof(sector_t) < 8) > diff --git a/fs/ext4/super.c b/fs/ext4/super.c > index e72d323..e03a7d2 100644 > --- a/fs/ext4/super.c > +++ b/fs/ext4/super.c > @@ -2706,15 +2706,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) > * Test whether we have more sectors than will fit in sector_t, > * and whether the max offset is addressable by the page cache. > */ > - if ((ext4_blocks_count(es) > > - (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) || > - (ext4_blocks_count(es) > > - (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - sb->s_blocksize_bits))) { > + ret = generic_check_addressable(sb->s_blocksize_bits, > + ext4_blocks_count(es)); > + if (ret) { > ext4_msg(sb, KERN_ERR, "filesystem" > " too large to mount safely on this system"); > if (sizeof(sector_t) < 8) > ext4_msg(sb, KERN_WARNING, "CONFIG_LBDAF not enabled"); > - ret = -EFBIG; > goto failed_mount; > } > > diff --git a/fs/libfs.c b/fs/libfs.c > index dcaf972..f099566 100644 > --- a/fs/libfs.c > +++ b/fs/libfs.c > @@ -955,6 +955,35 @@ int generic_file_fsync(struct file *file, int datasync) > } > EXPORT_SYMBOL(generic_file_fsync); > > +/** > + * generic_check_addressable - Check addressability of file system > + * @blocksize_bits: log of file system block size > + * @num_blocks: number of blocks in file system > + * > + * Determine whether a file system with @num_blocks blocks (and a > + * block size of 2**@blocksize_bits) is addressable by the sector_t > + * and page cache of the system. Return 0 if so and -EFBIG otherwise. > + */ > +int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) > +{ > + u64 last_fs_block = num_blocks - 1; > + > + if (unlikely(num_blocks == 0)) > + return 0; > + > + if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) > + return -EINVAL; > + > + if ((last_fs_block > > + (sector_t)(~0ULL) >> (blocksize_bits - 9)) || > + (last_fs_block > > + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { > + return -EFBIG; > + } > + return 0; > +} > +EXPORT_SYMBOL(generic_check_addressable); > + > /* > * No-op implementation of ->fsync for in-memory filesystems. > */ > diff --git a/include/linux/fs.h b/include/linux/fs.h > index e5106e4..7644248 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -2414,6 +2414,8 @@ extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos, > > extern int generic_file_fsync(struct file *, int); > > +extern int generic_check_addressable(unsigned, u64); > + > #ifdef CONFIG_MIGRATION > extern int buffer_migrate_page(struct address_space *, > struct page *, struct page *); > -- > 1.7.1 > > > -- > > "Friends may come and go, but enemies accumulate." > - Thomas Jones > > Joel Becker > Consulting Software Developer > Oracle > E-mail: joel.becker@oracle.com > Phone: (650) 506-8127 > -- > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html Cheers, Andreas -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Aug 12, 2010 at 05:07:36PM -0600, Andreas Dilger wrote: > You can add my: > > Acked-by: Andreas Dilger <adilger@dilger.ca> Thanks! Joel
On Thu 12-08-10 15:29:49, Joel Becker wrote: > diff --git a/fs/libfs.c b/fs/libfs.c > index dcaf972..f099566 100644 > --- a/fs/libfs.c > +++ b/fs/libfs.c > @@ -955,6 +955,35 @@ int generic_file_fsync(struct file *file, int datasync) > } > EXPORT_SYMBOL(generic_file_fsync); > > +/** > + * generic_check_addressable - Check addressability of file system > + * @blocksize_bits: log of file system block size > + * @num_blocks: number of blocks in file system > + * > + * Determine whether a file system with @num_blocks blocks (and a > + * block size of 2**@blocksize_bits) is addressable by the sector_t > + * and page cache of the system. Return 0 if so and -EFBIG otherwise. > + */ > +int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) > +{ > + u64 last_fs_block = num_blocks - 1; > + > + if (unlikely(num_blocks == 0)) > + return 0; > + > + if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) > + return -EINVAL; > + > + if ((last_fs_block > > + (sector_t)(~0ULL) >> (blocksize_bits - 9)) || > + (last_fs_block > > + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { ^^^ I don't get the pgoff_t check. Shouldn't it rather be (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits)? Because on 32-bit arch we are able to address 16TB device, which is for 1KB blocksize 1<<34 blocks. But your math gives 1<<30 blocks... Honza
On Fri, Aug 13, 2010 at 06:30:07PM +0200, Jan Kara wrote: > On Thu 12-08-10 15:29:49, Joel Becker wrote: > > +/** > > + * generic_check_addressable - Check addressability of file system > > + * @blocksize_bits: log of file system block size > > + * @num_blocks: number of blocks in file system > > + * > > + * Determine whether a file system with @num_blocks blocks (and a > > + * block size of 2**@blocksize_bits) is addressable by the sector_t > > + * and page cache of the system. Return 0 if so and -EFBIG otherwise. > > + */ > > +int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) > > +{ > > + u64 last_fs_block = num_blocks - 1; > > + > > + if (unlikely(num_blocks == 0)) > > + return 0; > > + > > + if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) > > + return -EINVAL; > > + > > + if ((last_fs_block > > > + (sector_t)(~0ULL) >> (blocksize_bits - 9)) || > > + (last_fs_block > > > + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { > ^^^ I don't get the pgoff_t check. Shouldn't it rather be > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits)? > Because on 32-bit arch we are able to address 16TB device, which is for 1KB > blocksize 1<<34 blocks. But your math gives 1<<30 blocks... This code is directly lifted from ext4. But that said, I am starting to think you're right. 1 page == 4 x 1K blocks, rather than 4 pages == 1 1K block. Joel
On Fri, Aug 13, 2010 at 01:47:01PM -0700, Joel Becker wrote: > On Fri, Aug 13, 2010 at 06:30:07PM +0200, Jan Kara wrote: > > On Thu 12-08-10 15:29:49, Joel Becker wrote: > > > +/** > > > + * generic_check_addressable - Check addressability of file system > > > + * @blocksize_bits: log of file system block size > > > + * @num_blocks: number of blocks in file system > > > + * > > > + * Determine whether a file system with @num_blocks blocks (and a > > > + * block size of 2**@blocksize_bits) is addressable by the sector_t > > > + * and page cache of the system. Return 0 if so and -EFBIG otherwise. > > > + */ > > > +int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) > > > +{ > > > + u64 last_fs_block = num_blocks - 1; > > > + > > > + if (unlikely(num_blocks == 0)) > > > + return 0; > > > + > > > + if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) > > > + return -EINVAL; > > > + > > > + if ((last_fs_block > > > > + (sector_t)(~0ULL) >> (blocksize_bits - 9)) || > > > + (last_fs_block > > > > + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { > > ^^^ I don't get the pgoff_t check. Shouldn't it rather be > > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits)? > > Because on 32-bit arch we are able to address 16TB device, which is for 1KB > > blocksize 1<<34 blocks. But your math gives 1<<30 blocks... > > This code is directly lifted from ext4. But that said, I am > starting to think you're right. 1 page == 4 x 1K blocks, rather than 4 > pages == 1 1K block. Wouldn't it rather be: ... || ((last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits)) > (pgoff_t)(!0ULL))) { Joel
Jan Kara wrote: > On Thu 12-08-10 15:29:49, Joel Becker wrote: >> diff --git a/fs/libfs.c b/fs/libfs.c >> index dcaf972..f099566 100644 >> --- a/fs/libfs.c >> +++ b/fs/libfs.c >> @@ -955,6 +955,35 @@ int generic_file_fsync(struct file *file, int datasync) >> } >> EXPORT_SYMBOL(generic_file_fsync); >> >> +/** >> + * generic_check_addressable - Check addressability of file system >> + * @blocksize_bits: log of file system block size >> + * @num_blocks: number of blocks in file system >> + * >> + * Determine whether a file system with @num_blocks blocks (and a >> + * block size of 2**@blocksize_bits) is addressable by the sector_t >> + * and page cache of the system. Return 0 if so and -EFBIG otherwise. >> + */ >> +int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) >> +{ >> + u64 last_fs_block = num_blocks - 1; >> + >> + if (unlikely(num_blocks == 0)) >> + return 0; >> + >> + if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) >> + return -EINVAL; >> + >> + if ((last_fs_block > >> + (sector_t)(~0ULL) >> (blocksize_bits - 9)) || >> + (last_fs_block > >> + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { > ^^^ I don't get the pgoff_t check. Shouldn't it rather be > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits)? Argh that was my fault... Thankfully not too many 1k-blocksize-formatted 16T devices out there, I guess. I went through the math again and also came up with: total fs pages is blocks / (blocks per page) total pages is blocks / (1 << PAGE_CACHE_SHIFT / 1 << blocksize_bits) total pages is blocks / (1 << (PAGE_CACHE_SHIFT - blocksize_bits)) total pages is blocks * (1 >> (PAGE_CACHE_SHIFT - blocksize_bits)) total pages is blocks >> (PAGE_CACHE_SHIFT - blocksize_bits) too big if total pages is > (pgoff_t)(~0ULL) too big if blocks >> (PAGE_CACHE_SHIFT - blocksize_bits) > (pgoff_t)(~0ULL) too big if blocks > (pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits) and to not overflow: too big if blocks > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits) so seems like the test is: last_fs_block > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits) Given the density of the logic in the helper it seems like maybe breaking it up and adding specific comments might be helpful to the reader: /* can IO layers fit total fs sectors in a sector_t? */ if (last_fs_block > (sector_t)(~0ULL) >> (blocksize_bits - 9)) return -EFBIG; /* can page cache fit total fs pages in a pgoff_t? */ if (last_fs_block > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits) return -EFBIG; Or something like that. Sorry for chiming in late... -Eric > Because on 32-bit arch we are able to address 16TB device, which is for 1KB > blocksize 1<<34 blocks. But your math gives 1<<30 blocks... > > Honza > -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Sun, Aug 15, 2010 at 12:19:36PM -0500, Eric Sandeen wrote: > >> + (last_fs_block > > >> + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { > > ^^^ I don't get the pgoff_t check. Shouldn't it rather be > > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits)? > > Argh that was my fault... Thankfully not too many 1k-blocksize-formatted > 16T devices out there, I guess. > > I went through the math again and also came up with: > > total fs pages is blocks / (blocks per page) > total pages is blocks / (1 << PAGE_CACHE_SHIFT / 1 << blocksize_bits) > total pages is blocks / (1 << (PAGE_CACHE_SHIFT - blocksize_bits)) > total pages is blocks * (1 >> (PAGE_CACHE_SHIFT - blocksize_bits)) > total pages is blocks >> (PAGE_CACHE_SHIFT - blocksize_bits) > > too big if total pages is > (pgoff_t)(~0ULL) > too big if blocks >> (PAGE_CACHE_SHIFT - blocksize_bits) > (pgoff_t)(~0ULL) Why not stop here, which is what I put in my other email? "blocks >> SHIFT-bits" is "how many pages do I need?". > too big if blocks > (pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits) > and to not overflow: > too big if blocks > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits) This still overflows. pgoff_t is a u64 on 64bit machines, right? So shift that left by anything and you wrap. Joel
Joel Becker wrote: > On Sun, Aug 15, 2010 at 12:19:36PM -0500, Eric Sandeen wrote: >>>> + (last_fs_block > >>>> + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { >>> ^^^ I don't get the pgoff_t check. Shouldn't it rather be >>> (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits)? >> Argh that was my fault... Thankfully not too many 1k-blocksize-formatted >> 16T devices out there, I guess. >> >> I went through the math again and also came up with: >> >> total fs pages is blocks / (blocks per page) >> total pages is blocks / (1 << PAGE_CACHE_SHIFT / 1 << blocksize_bits) >> total pages is blocks / (1 << (PAGE_CACHE_SHIFT - blocksize_bits)) >> total pages is blocks * (1 >> (PAGE_CACHE_SHIFT - blocksize_bits)) >> total pages is blocks >> (PAGE_CACHE_SHIFT - blocksize_bits) >> >> too big if total pages is > (pgoff_t)(~0ULL) >> too big if blocks >> (PAGE_CACHE_SHIFT - blocksize_bits) > (pgoff_t)(~0ULL) > > Why not stop here, which is what I put in my other email? > "blocks >> SHIFT-bits" is "how many pages do I need?". yeah, ok. Was going for pointless symmetry w/ the other test... >> too big if blocks > (pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits) >> and to not overflow: >> too big if blocks > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits) > > This still overflows. pgoff_t is a u64 on 64bit machines, > right? So shift that left by anything and you wrap. Er, yeah. I had 32 bits in my head since that's the case we're checking for... whoops. So I guess your ... || ((last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits)) > (pgoff_t)(!0ULL))) { is right :) (my feeble brain has a hard time reading that, though, TBH) -Eric > Joel > -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Sun, Aug 15, 2010 at 10:36:36PM -0500, Eric Sandeen wrote: > Er, yeah. I had 32 bits in my head since that's the case we're > checking for... whoops. > > So I guess your > > ... || > ((last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits)) > > (pgoff_t)(!0ULL))) { > > is right :) (my feeble brain has a hard time reading that, though, TBH) Well, note the bug in my quickly typed version: "(!0ULL)" vs "(~0ULL)". How about: u64 last_fs_page = last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits); ... || (last_fs_page > (pgoff_t)(~0ULL))) { Is that more readable? Joel
Joel Becker wrote: > On Sun, Aug 15, 2010 at 10:36:36PM -0500, Eric Sandeen wrote: >> Er, yeah. I had 32 bits in my head since that's the case we're >> checking for... whoops. >> >> So I guess your >> >> ... || >> ((last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits)) > >> (pgoff_t)(!0ULL))) { >> >> is right :) (my feeble brain has a hard time reading that, though, TBH) > > Well, note the bug in my quickly typed version: "(!0ULL)" vs > "(~0ULL)". *nod* saw that but figured it was just a typo & didn't mention it ;) > How about: > > u64 last_fs_page = last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits); > > ... || > (last_fs_page > (pgoff_t)(~0ULL))) { > > Is that more readable? To me, yes. Maybe do similar for last_fs_sector. If it's getting too verbose I understand, but less dense is a lot easier to read, IMHO. Just style though, really, so *shrug* Thanks, -Eric > Joel > -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri 13-08-10 15:52:46, Joel Becker wrote: > On Fri, Aug 13, 2010 at 01:47:01PM -0700, Joel Becker wrote: > > On Fri, Aug 13, 2010 at 06:30:07PM +0200, Jan Kara wrote: > > > On Thu 12-08-10 15:29:49, Joel Becker wrote: > > > > +/** > > > > + * generic_check_addressable - Check addressability of file system > > > > + * @blocksize_bits: log of file system block size > > > > + * @num_blocks: number of blocks in file system > > > > + * > > > > + * Determine whether a file system with @num_blocks blocks (and a > > > > + * block size of 2**@blocksize_bits) is addressable by the sector_t > > > > + * and page cache of the system. Return 0 if so and -EFBIG otherwise. > > > > + */ > > > > +int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) > > > > +{ > > > > + u64 last_fs_block = num_blocks - 1; > > > > + > > > > + if (unlikely(num_blocks == 0)) > > > > + return 0; > > > > + > > > > + if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) > > > > + return -EINVAL; > > > > + > > > > + if ((last_fs_block > > > > > + (sector_t)(~0ULL) >> (blocksize_bits - 9)) || > > > > + (last_fs_block > > > > > + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { > > > ^^^ I don't get the pgoff_t check. Shouldn't it rather be > > > (u64)(pgoff_t)(~0ULL) << (PAGE_CACHE_SHIFT - blocksize_bits)? > > > Because on 32-bit arch we are able to address 16TB device, which is for 1KB > > > blocksize 1<<34 blocks. But your math gives 1<<30 blocks... > > > > This code is directly lifted from ext4. But that said, I am > > starting to think you're right. 1 page == 4 x 1K blocks, rather than 4 > > pages == 1 1K block. > > Wouldn't it rather be: > > ... || > ((last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits)) > > (pgoff_t)(!0ULL))) { Yes, this would be even better than what I suggested. Honza
diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 6c953bb..d0643db 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -1862,8 +1862,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) goto failed_mount; } - if (le32_to_cpu(es->s_blocks_count) > - (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) { + if (generic_check_addressable(sb->s_blocksize_bits, + le32_to_cpu(es->s_blocks_count))) { ext3_msg(sb, KERN_ERR, "error: filesystem is too large to mount safely"); if (sizeof(sector_t) < 8) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index e72d323..e03a7d2 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2706,15 +2706,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) * Test whether we have more sectors than will fit in sector_t, * and whether the max offset is addressable by the page cache. */ - if ((ext4_blocks_count(es) > - (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) || - (ext4_blocks_count(es) > - (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - sb->s_blocksize_bits))) { + ret = generic_check_addressable(sb->s_blocksize_bits, + ext4_blocks_count(es)); + if (ret) { ext4_msg(sb, KERN_ERR, "filesystem" " too large to mount safely on this system"); if (sizeof(sector_t) < 8) ext4_msg(sb, KERN_WARNING, "CONFIG_LBDAF not enabled"); - ret = -EFBIG; goto failed_mount; } diff --git a/fs/libfs.c b/fs/libfs.c index dcaf972..f099566 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -955,6 +955,35 @@ int generic_file_fsync(struct file *file, int datasync) } EXPORT_SYMBOL(generic_file_fsync); +/** + * generic_check_addressable - Check addressability of file system + * @blocksize_bits: log of file system block size + * @num_blocks: number of blocks in file system + * + * Determine whether a file system with @num_blocks blocks (and a + * block size of 2**@blocksize_bits) is addressable by the sector_t + * and page cache of the system. Return 0 if so and -EFBIG otherwise. + */ +int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) +{ + u64 last_fs_block = num_blocks - 1; + + if (unlikely(num_blocks == 0)) + return 0; + + if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) + return -EINVAL; + + if ((last_fs_block > + (sector_t)(~0ULL) >> (blocksize_bits - 9)) || + (last_fs_block > + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { + return -EFBIG; + } + return 0; +} +EXPORT_SYMBOL(generic_check_addressable); + /* * No-op implementation of ->fsync for in-memory filesystems. */ diff --git a/include/linux/fs.h b/include/linux/fs.h index e5106e4..7644248 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2414,6 +2414,8 @@ extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos, extern int generic_file_fsync(struct file *, int); +extern int generic_check_addressable(unsigned, u64); + #ifdef CONFIG_MIGRATION extern int buffer_migrate_page(struct address_space *, struct page *, struct page *);