2019-05-28 20:10:12 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2004 , OGAWA Hirofumi
*/
2008-08-05 21:05:46 +04:00
# include <linux/blkdev.h>
2018-08-22 07:59:41 +03:00
# include <linux/sched/signal.h>
2008-11-06 23:53:46 +03:00
# include "fat.h"
2005-04-17 02:20:36 +04:00
struct fatent_operations {
void ( * ent_blocknr ) ( struct super_block * , int , int * , sector_t * ) ;
void ( * ent_set_ptr ) ( struct fat_entry * , int ) ;
int ( * ent_bread ) ( struct super_block * , struct fat_entry * ,
int , sector_t ) ;
int ( * ent_get ) ( struct fat_entry * ) ;
void ( * ent_put ) ( struct fat_entry * , int ) ;
int ( * ent_next ) ( struct fat_entry * ) ;
} ;
2007-07-16 04:40:05 +04:00
static DEFINE_SPINLOCK ( fat12_entry_lock ) ;
2005-04-17 02:20:36 +04:00
static void fat12_ent_blocknr ( struct super_block * sb , int entry ,
int * offset , sector_t * blocknr )
{
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
int bytes = entry + ( entry > > 1 ) ;
2018-08-22 07:59:44 +03:00
WARN_ON ( ! fat_valid_entry ( sbi , entry ) ) ;
2005-04-17 02:20:36 +04:00
* offset = bytes & ( sb - > s_blocksize - 1 ) ;
* blocknr = sbi - > fat_start + ( bytes > > sb - > s_blocksize_bits ) ;
}
static void fat_ent_blocknr ( struct super_block * sb , int entry ,
int * offset , sector_t * blocknr )
{
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
int bytes = ( entry < < sbi - > fatent_shift ) ;
2018-08-22 07:59:44 +03:00
WARN_ON ( ! fat_valid_entry ( sbi , entry ) ) ;
2005-04-17 02:20:36 +04:00
* offset = bytes & ( sb - > s_blocksize - 1 ) ;
* blocknr = sbi - > fat_start + ( bytes > > sb - > s_blocksize_bits ) ;
}
static void fat12_ent_set_ptr ( struct fat_entry * fatent , int offset )
{
struct buffer_head * * bhs = fatent - > bhs ;
if ( fatent - > nr_bhs = = 1 ) {
WARN_ON ( offset > = ( bhs [ 0 ] - > b_size - 1 ) ) ;
fatent - > u . ent12_p [ 0 ] = bhs [ 0 ] - > b_data + offset ;
fatent - > u . ent12_p [ 1 ] = bhs [ 0 ] - > b_data + ( offset + 1 ) ;
} else {
WARN_ON ( offset ! = ( bhs [ 0 ] - > b_size - 1 ) ) ;
fatent - > u . ent12_p [ 0 ] = bhs [ 0 ] - > b_data + offset ;
fatent - > u . ent12_p [ 1 ] = bhs [ 1 ] - > b_data ;
}
}
static void fat16_ent_set_ptr ( struct fat_entry * fatent , int offset )
{
WARN_ON ( offset & ( 2 - 1 ) ) ;
fatent - > u . ent16_p = ( __le16 * ) ( fatent - > bhs [ 0 ] - > b_data + offset ) ;
}
static void fat32_ent_set_ptr ( struct fat_entry * fatent , int offset )
{
WARN_ON ( offset & ( 4 - 1 ) ) ;
fatent - > u . ent32_p = ( __le32 * ) ( fatent - > bhs [ 0 ] - > b_data + offset ) ;
}
static int fat12_ent_bread ( struct super_block * sb , struct fat_entry * fatent ,
int offset , sector_t blocknr )
{
struct buffer_head * * bhs = fatent - > bhs ;
WARN_ON ( blocknr < MSDOS_SB ( sb ) - > fat_start ) ;
2009-06-07 21:44:36 +04:00
fatent - > fat_inode = MSDOS_SB ( sb ) - > fat_inode ;
2005-04-17 02:20:36 +04:00
bhs [ 0 ] = sb_bread ( sb , blocknr ) ;
if ( ! bhs [ 0 ] )
goto err ;
if ( ( offset + 1 ) < sb - > s_blocksize )
fatent - > nr_bhs = 1 ;
else {
/* This entry is block boundary, it needs the next block */
blocknr + + ;
bhs [ 1 ] = sb_bread ( sb , blocknr ) ;
if ( ! bhs [ 1 ] )
goto err_brelse ;
fatent - > nr_bhs = 2 ;
}
fat12_ent_set_ptr ( fatent , offset ) ;
return 0 ;
err_brelse :
brelse ( bhs [ 0 ] ) ;
err :
2011-04-12 16:08:38 +04:00
fat_msg ( sb , KERN_ERR , " FAT read failed (blocknr %llu) " , ( llu ) blocknr ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
static int fat_ent_bread ( struct super_block * sb , struct fat_entry * fatent ,
int offset , sector_t blocknr )
{
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = MSDOS_SB ( sb ) - > fatent_ops ;
2005-04-17 02:20:36 +04:00
WARN_ON ( blocknr < MSDOS_SB ( sb ) - > fat_start ) ;
2009-06-07 21:44:36 +04:00
fatent - > fat_inode = MSDOS_SB ( sb ) - > fat_inode ;
2005-04-17 02:20:36 +04:00
fatent - > bhs [ 0 ] = sb_bread ( sb , blocknr ) ;
if ( ! fatent - > bhs [ 0 ] ) {
2011-04-12 16:08:38 +04:00
fat_msg ( sb , KERN_ERR , " FAT read failed (blocknr %llu) " ,
2008-11-06 23:53:58 +03:00
( llu ) blocknr ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
fatent - > nr_bhs = 1 ;
ops - > ent_set_ptr ( fatent , offset ) ;
return 0 ;
}
static int fat12_ent_get ( struct fat_entry * fatent )
{
u8 * * ent12_p = fatent - > u . ent12_p ;
int next ;
2007-07-16 04:40:05 +04:00
spin_lock ( & fat12_entry_lock ) ;
2005-04-17 02:20:36 +04:00
if ( fatent - > entry & 1 )
next = ( * ent12_p [ 0 ] > > 4 ) | ( * ent12_p [ 1 ] < < 4 ) ;
else
next = ( * ent12_p [ 1 ] < < 8 ) | * ent12_p [ 0 ] ;
2007-07-16 04:40:05 +04:00
spin_unlock ( & fat12_entry_lock ) ;
2005-04-17 02:20:36 +04:00
next & = 0x0fff ;
if ( next > = BAD_FAT12 )
next = FAT_ENT_EOF ;
return next ;
}
static int fat16_ent_get ( struct fat_entry * fatent )
{
int next = le16_to_cpu ( * fatent - > u . ent16_p ) ;
WARN_ON ( ( unsigned long ) fatent - > u . ent16_p & ( 2 - 1 ) ) ;
if ( next > = BAD_FAT16 )
next = FAT_ENT_EOF ;
return next ;
}
static int fat32_ent_get ( struct fat_entry * fatent )
{
int next = le32_to_cpu ( * fatent - > u . ent32_p ) & 0x0fffffff ;
WARN_ON ( ( unsigned long ) fatent - > u . ent32_p & ( 4 - 1 ) ) ;
if ( next > = BAD_FAT32 )
next = FAT_ENT_EOF ;
return next ;
}
static void fat12_ent_put ( struct fat_entry * fatent , int new )
{
u8 * * ent12_p = fatent - > u . ent12_p ;
if ( new = = FAT_ENT_EOF )
new = EOF_FAT12 ;
2007-07-16 04:40:05 +04:00
spin_lock ( & fat12_entry_lock ) ;
2005-04-17 02:20:36 +04:00
if ( fatent - > entry & 1 ) {
* ent12_p [ 0 ] = ( new < < 4 ) | ( * ent12_p [ 0 ] & 0x0f ) ;
* ent12_p [ 1 ] = new > > 4 ;
} else {
* ent12_p [ 0 ] = new & 0xff ;
* ent12_p [ 1 ] = ( * ent12_p [ 1 ] & 0xf0 ) | ( new > > 8 ) ;
}
2007-07-16 04:40:05 +04:00
spin_unlock ( & fat12_entry_lock ) ;
2005-04-17 02:20:36 +04:00
2009-06-07 21:44:36 +04:00
mark_buffer_dirty_inode ( fatent - > bhs [ 0 ] , fatent - > fat_inode ) ;
2005-04-17 02:20:36 +04:00
if ( fatent - > nr_bhs = = 2 )
2009-06-07 21:44:36 +04:00
mark_buffer_dirty_inode ( fatent - > bhs [ 1 ] , fatent - > fat_inode ) ;
2005-04-17 02:20:36 +04:00
}
static void fat16_ent_put ( struct fat_entry * fatent , int new )
{
if ( new = = FAT_ENT_EOF )
new = EOF_FAT16 ;
* fatent - > u . ent16_p = cpu_to_le16 ( new ) ;
2009-06-07 21:44:36 +04:00
mark_buffer_dirty_inode ( fatent - > bhs [ 0 ] , fatent - > fat_inode ) ;
2005-04-17 02:20:36 +04:00
}
static void fat32_ent_put ( struct fat_entry * fatent , int new )
{
WARN_ON ( new & 0xf0000000 ) ;
new | = le32_to_cpu ( * fatent - > u . ent32_p ) & ~ 0x0fffffff ;
* fatent - > u . ent32_p = cpu_to_le32 ( new ) ;
2009-06-07 21:44:36 +04:00
mark_buffer_dirty_inode ( fatent - > bhs [ 0 ] , fatent - > fat_inode ) ;
2005-04-17 02:20:36 +04:00
}
static int fat12_ent_next ( struct fat_entry * fatent )
{
u8 * * ent12_p = fatent - > u . ent12_p ;
struct buffer_head * * bhs = fatent - > bhs ;
u8 * nextp = ent12_p [ 1 ] + 1 + ( fatent - > entry & 1 ) ;
fatent - > entry + + ;
if ( fatent - > nr_bhs = = 1 ) {
2012-10-05 04:14:59 +04:00
WARN_ON ( ent12_p [ 0 ] > ( u8 * ) ( bhs [ 0 ] - > b_data +
( bhs [ 0 ] - > b_size - 2 ) ) ) ;
WARN_ON ( ent12_p [ 1 ] > ( u8 * ) ( bhs [ 0 ] - > b_data +
( bhs [ 0 ] - > b_size - 1 ) ) ) ;
2005-04-17 02:20:36 +04:00
if ( nextp < ( u8 * ) ( bhs [ 0 ] - > b_data + ( bhs [ 0 ] - > b_size - 1 ) ) ) {
ent12_p [ 0 ] = nextp - 1 ;
ent12_p [ 1 ] = nextp ;
return 1 ;
}
} else {
2012-10-05 04:14:59 +04:00
WARN_ON ( ent12_p [ 0 ] ! = ( u8 * ) ( bhs [ 0 ] - > b_data +
( bhs [ 0 ] - > b_size - 1 ) ) ) ;
2005-04-17 02:20:36 +04:00
WARN_ON ( ent12_p [ 1 ] ! = ( u8 * ) bhs [ 1 ] - > b_data ) ;
ent12_p [ 0 ] = nextp - 1 ;
ent12_p [ 1 ] = nextp ;
brelse ( bhs [ 0 ] ) ;
bhs [ 0 ] = bhs [ 1 ] ;
fatent - > nr_bhs = 1 ;
return 1 ;
}
ent12_p [ 0 ] = NULL ;
ent12_p [ 1 ] = NULL ;
return 0 ;
}
static int fat16_ent_next ( struct fat_entry * fatent )
{
const struct buffer_head * bh = fatent - > bhs [ 0 ] ;
fatent - > entry + + ;
if ( fatent - > u . ent16_p < ( __le16 * ) ( bh - > b_data + ( bh - > b_size - 2 ) ) ) {
fatent - > u . ent16_p + + ;
return 1 ;
}
fatent - > u . ent16_p = NULL ;
return 0 ;
}
static int fat32_ent_next ( struct fat_entry * fatent )
{
const struct buffer_head * bh = fatent - > bhs [ 0 ] ;
fatent - > entry + + ;
if ( fatent - > u . ent32_p < ( __le32 * ) ( bh - > b_data + ( bh - > b_size - 4 ) ) ) {
fatent - > u . ent32_p + + ;
return 1 ;
}
fatent - > u . ent32_p = NULL ;
return 0 ;
}
2016-01-21 01:59:52 +03:00
static const struct fatent_operations fat12_ops = {
2005-04-17 02:20:36 +04:00
. ent_blocknr = fat12_ent_blocknr ,
. ent_set_ptr = fat12_ent_set_ptr ,
. ent_bread = fat12_ent_bread ,
. ent_get = fat12_ent_get ,
. ent_put = fat12_ent_put ,
. ent_next = fat12_ent_next ,
} ;
2016-01-21 01:59:52 +03:00
static const struct fatent_operations fat16_ops = {
2005-04-17 02:20:36 +04:00
. ent_blocknr = fat_ent_blocknr ,
. ent_set_ptr = fat16_ent_set_ptr ,
. ent_bread = fat_ent_bread ,
. ent_get = fat16_ent_get ,
. ent_put = fat16_ent_put ,
. ent_next = fat16_ent_next ,
} ;
2016-01-21 01:59:52 +03:00
static const struct fatent_operations fat32_ops = {
2005-04-17 02:20:36 +04:00
. ent_blocknr = fat_ent_blocknr ,
. ent_set_ptr = fat32_ent_set_ptr ,
. ent_bread = fat_ent_bread ,
. ent_get = fat32_ent_get ,
. ent_put = fat32_ent_put ,
. ent_next = fat32_ent_next ,
} ;
static inline void lock_fat ( struct msdos_sb_info * sbi )
{
2006-03-23 14:00:48 +03:00
mutex_lock ( & sbi - > fat_lock ) ;
2005-04-17 02:20:36 +04:00
}
static inline void unlock_fat ( struct msdos_sb_info * sbi )
{
2006-03-23 14:00:48 +03:00
mutex_unlock ( & sbi - > fat_lock ) ;
2005-04-17 02:20:36 +04:00
}
void fat_ent_access_init ( struct super_block * sb )
{
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
2006-03-23 14:00:48 +03:00
mutex_init ( & sbi - > fat_lock ) ;
2005-04-17 02:20:36 +04:00
2019-01-04 02:28:00 +03:00
if ( is_fat32 ( sbi ) ) {
2005-04-17 02:20:36 +04:00
sbi - > fatent_shift = 2 ;
sbi - > fatent_ops = & fat32_ops ;
2019-01-04 02:28:00 +03:00
} else if ( is_fat16 ( sbi ) ) {
2005-04-17 02:20:36 +04:00
sbi - > fatent_shift = 1 ;
sbi - > fatent_ops = & fat16_ops ;
2019-01-04 02:28:00 +03:00
} else if ( is_fat12 ( sbi ) ) {
2005-04-17 02:20:36 +04:00
sbi - > fatent_shift = - 1 ;
sbi - > fatent_ops = & fat12_ops ;
2019-01-04 02:28:00 +03:00
} else {
fat_fs_error ( sb , " invalid FAT variant, %u bits " , sbi - > fat_bits ) ;
2005-04-17 02:20:36 +04:00
}
}
2012-06-01 03:26:12 +04:00
static void mark_fsinfo_dirty ( struct super_block * sb )
{
2012-06-01 03:26:13 +04:00
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
2019-01-04 02:28:00 +03:00
if ( sb_rdonly ( sb ) | | ! is_fat32 ( sbi ) )
2012-06-01 03:26:13 +04:00
return ;
__mark_inode_dirty ( sbi - > fsinfo_inode , I_DIRTY_SYNC ) ;
2012-06-01 03:26:12 +04:00
}
2005-04-17 02:20:36 +04:00
static inline int fat_ent_update_ptr ( struct super_block * sb ,
struct fat_entry * fatent ,
int offset , sector_t blocknr )
{
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = sbi - > fatent_ops ;
2005-04-17 02:20:36 +04:00
struct buffer_head * * bhs = fatent - > bhs ;
/* Is this fatent's blocks including this entry? */
if ( ! fatent - > nr_bhs | | bhs [ 0 ] - > b_blocknr ! = blocknr )
return 0 ;
2019-01-04 02:28:00 +03:00
if ( is_fat12 ( sbi ) ) {
2008-11-06 23:53:49 +03:00
if ( ( offset + 1 ) < sb - > s_blocksize ) {
/* This entry is on bhs[0]. */
if ( fatent - > nr_bhs = = 2 ) {
brelse ( bhs [ 1 ] ) ;
fatent - > nr_bhs = 1 ;
}
} else {
/* This entry needs the next block. */
if ( fatent - > nr_bhs ! = 2 )
return 0 ;
if ( bhs [ 1 ] - > b_blocknr ! = ( blocknr + 1 ) )
return 0 ;
}
2005-04-17 02:20:36 +04:00
}
ops - > ent_set_ptr ( fatent , offset ) ;
return 1 ;
}
int fat_ent_read ( struct inode * inode , struct fat_entry * fatent , int entry )
{
struct super_block * sb = inode - > i_sb ;
struct msdos_sb_info * sbi = MSDOS_SB ( inode - > i_sb ) ;
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = sbi - > fatent_ops ;
2005-04-17 02:20:36 +04:00
int err , offset ;
sector_t blocknr ;
2018-08-22 07:59:44 +03:00
if ( ! fat_valid_entry ( sbi , entry ) ) {
2005-04-17 02:20:36 +04:00
fatent_brelse ( fatent ) ;
2009-06-03 21:34:22 +04:00
fat_fs_error ( sb , " invalid access to FAT (entry 0x%08x) " , entry ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
fatent_set_entry ( fatent , entry ) ;
ops - > ent_blocknr ( sb , entry , & offset , & blocknr ) ;
if ( ! fat_ent_update_ptr ( sb , fatent , offset , blocknr ) ) {
fatent_brelse ( fatent ) ;
err = ops - > ent_bread ( sb , fatent , offset , blocknr ) ;
if ( err )
return err ;
}
return ops - > ent_get ( fatent ) ;
}
/* FIXME: We can write the blocks as more big chunk. */
static int fat_mirror_bhs ( struct super_block * sb , struct buffer_head * * bhs ,
int nr_bhs )
{
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
struct buffer_head * c_bh ;
int err , n , copy ;
err = 0 ;
for ( copy = 1 ; copy < sbi - > fats ; copy + + ) {
sector_t backup_fat = sbi - > fat_length * copy ;
for ( n = 0 ; n < nr_bhs ; n + + ) {
c_bh = sb_getblk ( sb , backup_fat + bhs [ n ] - > b_blocknr ) ;
if ( ! c_bh ) {
err = - ENOMEM ;
goto error ;
}
2019-09-24 01:32:53 +03:00
/* Avoid race with userspace read via bdev */
lock_buffer ( c_bh ) ;
2005-04-17 02:20:36 +04:00
memcpy ( c_bh - > b_data , bhs [ n ] - > b_data , sb - > s_blocksize ) ;
set_buffer_uptodate ( c_bh ) ;
2019-09-24 01:32:53 +03:00
unlock_buffer ( c_bh ) ;
2009-06-07 21:44:36 +04:00
mark_buffer_dirty_inode ( c_bh , sbi - > fat_inode ) ;
2017-11-28 00:05:09 +03:00
if ( sb - > s_flags & SB_SYNCHRONOUS )
2005-04-17 02:20:36 +04:00
err = sync_dirty_buffer ( c_bh ) ;
brelse ( c_bh ) ;
if ( err )
goto error ;
}
}
error :
return err ;
}
int fat_ent_write ( struct inode * inode , struct fat_entry * fatent ,
int new , int wait )
{
struct super_block * sb = inode - > i_sb ;
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = MSDOS_SB ( sb ) - > fatent_ops ;
2005-04-17 02:20:36 +04:00
int err ;
ops - > ent_put ( fatent , new ) ;
if ( wait ) {
err = fat_sync_bhs ( fatent - > bhs , fatent - > nr_bhs ) ;
if ( err )
return err ;
}
return fat_mirror_bhs ( sb , fatent - > bhs , fatent - > nr_bhs ) ;
}
static inline int fat_ent_next ( struct msdos_sb_info * sbi ,
struct fat_entry * fatent )
{
if ( sbi - > fatent_ops - > ent_next ( fatent ) ) {
if ( fatent - > entry < sbi - > max_cluster )
return 1 ;
}
return 0 ;
}
static inline int fat_ent_read_block ( struct super_block * sb ,
struct fat_entry * fatent )
{
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = MSDOS_SB ( sb ) - > fatent_ops ;
2005-04-17 02:20:36 +04:00
sector_t blocknr ;
int offset ;
fatent_brelse ( fatent ) ;
ops - > ent_blocknr ( sb , fatent - > entry , & offset , & blocknr ) ;
return ops - > ent_bread ( sb , fatent , offset , blocknr ) ;
}
static void fat_collect_bhs ( struct buffer_head * * bhs , int * nr_bhs ,
struct fat_entry * fatent )
{
int n , i ;
for ( n = 0 ; n < fatent - > nr_bhs ; n + + ) {
for ( i = 0 ; i < * nr_bhs ; i + + ) {
if ( fatent - > bhs [ n ] = = bhs [ i ] )
break ;
}
if ( i = = * nr_bhs ) {
get_bh ( fatent - > bhs [ n ] ) ;
bhs [ i ] = fatent - > bhs [ n ] ;
( * nr_bhs ) + + ;
}
}
}
int fat_alloc_clusters ( struct inode * inode , int * cluster , int nr_cluster )
{
struct super_block * sb = inode - > i_sb ;
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = sbi - > fatent_ops ;
2005-04-17 02:20:36 +04:00
struct fat_entry fatent , prev_ent ;
struct buffer_head * bhs [ MAX_BUF_PER_PAGE ] ;
int i , count , err , nr_bhs , idx_clus ;
BUG_ON ( nr_cluster > ( MAX_BUF_PER_PAGE / 2 ) ) ; /* fixed limit */
lock_fat ( sbi ) ;
2008-04-28 13:16:27 +04:00
if ( sbi - > free_clusters ! = - 1 & & sbi - > free_clus_valid & &
sbi - > free_clusters < nr_cluster ) {
2005-04-17 02:20:36 +04:00
unlock_fat ( sbi ) ;
return - ENOSPC ;
}
err = nr_bhs = idx_clus = 0 ;
count = FAT_START_ENT ;
fatent_init ( & prev_ent ) ;
fatent_init ( & fatent ) ;
fatent_set_entry ( & fatent , sbi - > prev_free + 1 ) ;
while ( count < sbi - > max_cluster ) {
if ( fatent . entry > = sbi - > max_cluster )
fatent . entry = FAT_START_ENT ;
fatent_set_entry ( & fatent , fatent . entry ) ;
err = fat_ent_read_block ( sb , & fatent ) ;
if ( err )
goto out ;
/* Find the free entries in a block */
do {
if ( ops - > ent_get ( & fatent ) = = FAT_ENT_FREE ) {
int entry = fatent . entry ;
/* make the cluster chain */
ops - > ent_put ( & fatent , FAT_ENT_EOF ) ;
if ( prev_ent . nr_bhs )
ops - > ent_put ( & prev_ent , entry ) ;
fat_collect_bhs ( bhs , & nr_bhs , & fatent ) ;
sbi - > prev_free = entry ;
if ( sbi - > free_clusters ! = - 1 )
sbi - > free_clusters - - ;
cluster [ idx_clus ] = entry ;
idx_clus + + ;
if ( idx_clus = = nr_cluster )
goto out ;
/*
* fat_collect_bhs ( ) gets ref - count of bhs ,
* so we can still use the prev_ent .
*/
prev_ent = fatent ;
}
count + + ;
if ( count = = sbi - > max_cluster )
break ;
} while ( fat_ent_next ( sbi , & fatent ) ) ;
}
/* Couldn't allocate the free entries */
sbi - > free_clusters = 0 ;
2008-04-28 13:16:27 +04:00
sbi - > free_clus_valid = 1 ;
2005-04-17 02:20:36 +04:00
err = - ENOSPC ;
out :
unlock_fat ( sbi ) ;
2012-06-01 03:26:12 +04:00
mark_fsinfo_dirty ( sb ) ;
2005-04-17 02:20:36 +04:00
fatent_brelse ( & fatent ) ;
if ( ! err ) {
if ( inode_needs_sync ( inode ) )
err = fat_sync_bhs ( bhs , nr_bhs ) ;
if ( ! err )
err = fat_mirror_bhs ( sb , bhs , nr_bhs ) ;
}
for ( i = 0 ; i < nr_bhs ; i + + )
brelse ( bhs [ i ] ) ;
if ( err & & idx_clus )
fat_free_clusters ( inode , cluster [ 0 ] ) ;
return err ;
}
int fat_free_clusters ( struct inode * inode , int cluster )
{
struct super_block * sb = inode - > i_sb ;
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = sbi - > fatent_ops ;
2005-04-17 02:20:36 +04:00
struct fat_entry fatent ;
struct buffer_head * bhs [ MAX_BUF_PER_PAGE ] ;
int i , err , nr_bhs ;
2012-06-01 03:26:12 +04:00
int first_cl = cluster , dirty_fsinfo = 0 ;
2005-04-17 02:20:36 +04:00
nr_bhs = 0 ;
fatent_init ( & fatent ) ;
lock_fat ( sbi ) ;
do {
cluster = fat_ent_read ( inode , & fatent , cluster ) ;
if ( cluster < 0 ) {
err = cluster ;
goto error ;
} else if ( cluster = = FAT_ENT_FREE ) {
2009-06-03 21:34:22 +04:00
fat_fs_error ( sb , " %s: deleting FAT entry beyond EOF " ,
2008-04-30 11:55:09 +04:00
__func__ ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto error ;
}
2009-11-21 14:28:52 +03:00
if ( sbi - > options . discard ) {
/*
* Issue discard for the sectors we no longer
* care about , batching contiguous clusters
* into one request
*/
if ( cluster ! = fatent . entry + 1 ) {
int nr_clus = fatent . entry - first_cl + 1 ;
sb_issue_discard ( sb ,
fat_clus_to_blknr ( sbi , first_cl ) ,
2010-08-18 13:29:10 +04:00
nr_clus * sbi - > sec_per_clus ,
2010-09-16 22:51:46 +04:00
GFP_NOFS , 0 ) ;
2009-11-21 14:28:52 +03:00
first_cl = cluster ;
}
2008-08-05 21:05:46 +04:00
}
2005-04-17 02:20:36 +04:00
ops - > ent_put ( & fatent , FAT_ENT_FREE ) ;
2006-01-08 12:02:08 +03:00
if ( sbi - > free_clusters ! = - 1 ) {
2005-04-17 02:20:36 +04:00
sbi - > free_clusters + + ;
2012-06-01 03:26:12 +04:00
dirty_fsinfo = 1 ;
2006-01-08 12:02:08 +03:00
}
2005-04-17 02:20:36 +04:00
if ( nr_bhs + fatent . nr_bhs > MAX_BUF_PER_PAGE ) {
2017-11-28 00:05:09 +03:00
if ( sb - > s_flags & SB_SYNCHRONOUS ) {
2005-04-17 02:20:36 +04:00
err = fat_sync_bhs ( bhs , nr_bhs ) ;
if ( err )
goto error ;
}
err = fat_mirror_bhs ( sb , bhs , nr_bhs ) ;
if ( err )
goto error ;
for ( i = 0 ; i < nr_bhs ; i + + )
brelse ( bhs [ i ] ) ;
nr_bhs = 0 ;
}
fat_collect_bhs ( bhs , & nr_bhs , & fatent ) ;
} while ( cluster ! = FAT_ENT_EOF ) ;
2017-11-28 00:05:09 +03:00
if ( sb - > s_flags & SB_SYNCHRONOUS ) {
2005-04-17 02:20:36 +04:00
err = fat_sync_bhs ( bhs , nr_bhs ) ;
if ( err )
goto error ;
}
err = fat_mirror_bhs ( sb , bhs , nr_bhs ) ;
error :
fatent_brelse ( & fatent ) ;
for ( i = 0 ; i < nr_bhs ; i + + )
brelse ( bhs [ i ] ) ;
unlock_fat ( sbi ) ;
2012-06-01 03:26:12 +04:00
if ( dirty_fsinfo )
mark_fsinfo_dirty ( sb ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2006-01-08 12:02:10 +03:00
EXPORT_SYMBOL_GPL ( fat_free_clusters ) ;
2005-04-17 02:20:36 +04:00
2008-01-09 02:32:41 +03:00
/* 128kb is the whole sectors for FAT12 and FAT16 */
# define FAT_READA_SIZE (128 * 1024)
static void fat_ent_reada ( struct super_block * sb , struct fat_entry * fatent ,
unsigned long reada_blocks )
{
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = MSDOS_SB ( sb ) - > fatent_ops ;
2008-01-09 02:32:41 +03:00
sector_t blocknr ;
int i , offset ;
ops - > ent_blocknr ( sb , fatent - > entry , & offset , & blocknr ) ;
for ( i = 0 ; i < reada_blocks ; i + + )
sb_breadahead ( sb , blocknr + i ) ;
}
2005-04-17 02:20:36 +04:00
int fat_count_free_clusters ( struct super_block * sb )
{
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
2016-01-21 01:59:52 +03:00
const struct fatent_operations * ops = sbi - > fatent_ops ;
2005-04-17 02:20:36 +04:00
struct fat_entry fatent ;
2008-01-09 02:32:41 +03:00
unsigned long reada_blocks , reada_mask , cur_block ;
2005-04-17 02:20:36 +04:00
int err = 0 , free ;
lock_fat ( sbi ) ;
2008-04-28 13:16:27 +04:00
if ( sbi - > free_clusters ! = - 1 & & sbi - > free_clus_valid )
2005-04-17 02:20:36 +04:00
goto out ;
2008-01-09 02:32:41 +03:00
reada_blocks = FAT_READA_SIZE > > sb - > s_blocksize_bits ;
reada_mask = reada_blocks - 1 ;
cur_block = 0 ;
2005-04-17 02:20:36 +04:00
free = 0 ;
fatent_init ( & fatent ) ;
fatent_set_entry ( & fatent , FAT_START_ENT ) ;
while ( fatent . entry < sbi - > max_cluster ) {
2008-01-09 02:32:41 +03:00
/* readahead of fat blocks */
if ( ( cur_block & reada_mask ) = = 0 ) {
unsigned long rest = sbi - > fat_length - cur_block ;
fat_ent_reada ( sb , & fatent , min ( reada_blocks , rest ) ) ;
}
cur_block + + ;
2005-04-17 02:20:36 +04:00
err = fat_ent_read_block ( sb , & fatent ) ;
if ( err )
goto out ;
do {
if ( ops - > ent_get ( & fatent ) = = FAT_ENT_FREE )
free + + ;
} while ( fat_ent_next ( sbi , & fatent ) ) ;
2018-10-13 07:34:40 +03:00
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
sbi - > free_clusters = free ;
2008-04-28 13:16:27 +04:00
sbi - > free_clus_valid = 1 ;
2012-06-01 03:26:12 +04:00
mark_fsinfo_dirty ( sb ) ;
2005-04-17 02:20:36 +04:00
fatent_brelse ( & fatent ) ;
out :
unlock_fat ( sbi ) ;
return err ;
}
2018-08-22 07:59:41 +03:00
static int fat_trim_clusters ( struct super_block * sb , u32 clus , u32 nr_clus )
{
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
return sb_issue_discard ( sb , fat_clus_to_blknr ( sbi , clus ) ,
nr_clus * sbi - > sec_per_clus , GFP_NOFS , 0 ) ;
}
int fat_trim_fs ( struct inode * inode , struct fstrim_range * range )
{
struct super_block * sb = inode - > i_sb ;
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
const struct fatent_operations * ops = sbi - > fatent_ops ;
struct fat_entry fatent ;
u64 ent_start , ent_end , minlen , trimmed = 0 ;
u32 free = 0 ;
unsigned long reada_blocks , reada_mask , cur_block = 0 ;
int err = 0 ;
/*
* FAT data is organized as clusters , trim at the granulary of cluster .
*
* fstrim_range is in byte , convert vaules to cluster index .
* Treat sectors before data region as all used , not to trim them .
*/
ent_start = max_t ( u64 , range - > start > > sbi - > cluster_bits , FAT_START_ENT ) ;
ent_end = ent_start + ( range - > len > > sbi - > cluster_bits ) - 1 ;
minlen = range - > minlen > > sbi - > cluster_bits ;
if ( ent_start > = sbi - > max_cluster | | range - > len < sbi - > cluster_size )
return - EINVAL ;
if ( ent_end > = sbi - > max_cluster )
ent_end = sbi - > max_cluster - 1 ;
reada_blocks = FAT_READA_SIZE > > sb - > s_blocksize_bits ;
reada_mask = reada_blocks - 1 ;
fatent_init ( & fatent ) ;
lock_fat ( sbi ) ;
fatent_set_entry ( & fatent , ent_start ) ;
while ( fatent . entry < = ent_end ) {
/* readahead of fat blocks */
if ( ( cur_block & reada_mask ) = = 0 ) {
unsigned long rest = sbi - > fat_length - cur_block ;
fat_ent_reada ( sb , & fatent , min ( reada_blocks , rest ) ) ;
}
cur_block + + ;
err = fat_ent_read_block ( sb , & fatent ) ;
if ( err )
goto error ;
do {
if ( ops - > ent_get ( & fatent ) = = FAT_ENT_FREE ) {
free + + ;
} else if ( free ) {
if ( free > = minlen ) {
u32 clus = fatent . entry - free ;
err = fat_trim_clusters ( sb , clus , free ) ;
if ( err & & err ! = - EOPNOTSUPP )
goto error ;
if ( ! err )
trimmed + = free ;
err = 0 ;
}
free = 0 ;
}
} while ( fat_ent_next ( sbi , & fatent ) & & fatent . entry < = ent_end ) ;
if ( fatal_signal_pending ( current ) ) {
err = - ERESTARTSYS ;
goto error ;
}
if ( need_resched ( ) ) {
fatent_brelse ( & fatent ) ;
unlock_fat ( sbi ) ;
cond_resched ( ) ;
lock_fat ( sbi ) ;
}
}
/* handle scenario when tail entries are all free */
if ( free & & free > = minlen ) {
u32 clus = fatent . entry - free ;
err = fat_trim_clusters ( sb , clus , free ) ;
if ( err & & err ! = - EOPNOTSUPP )
goto error ;
if ( ! err )
trimmed + = free ;
err = 0 ;
}
error :
fatent_brelse ( & fatent ) ;
unlock_fat ( sbi ) ;
range - > len = trimmed < < sbi - > cluster_bits ;
return err ;
}