2005-04-17 02:20:36 +04:00
/*
* linux / fs / fat / misc . c
*
* Written 1992 , 1993 by Werner Almesberger
* 22 / 11 / 2000 - Fixed fat_date_unix2dos for dates earlier than 01 / 01 / 1980
* and date_dos2unix for date = = 0 by Igor Zhbanov ( bsg @ uniyar . ac . ru )
*/
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/buffer_head.h>
2008-11-06 23:53:46 +03:00
# include "fat.h"
2005-04-17 02:20:36 +04:00
/*
* fat_fs_panic reports a severe file system problem and sets the file system
* read - only . The file system can be made writable again by remounting it .
*/
void fat_fs_panic ( struct super_block * s , const char * fmt , . . . )
{
va_list args ;
printk ( KERN_ERR " FAT: Filesystem panic (dev %s) \n " , s - > s_id ) ;
printk ( KERN_ERR " " ) ;
va_start ( args , fmt ) ;
vprintk ( fmt , args ) ;
va_end ( args ) ;
printk ( " \n " ) ;
if ( ! ( s - > s_flags & MS_RDONLY ) ) {
s - > s_flags | = MS_RDONLY ;
printk ( KERN_ERR " File system has been set read-only \n " ) ;
}
}
2006-01-08 12:02:10 +03:00
EXPORT_SYMBOL_GPL ( fat_fs_panic ) ;
2005-04-17 02:20:36 +04:00
/* Flushes the number of free clusters on FAT32 */
/* XXX: Need to write one per FSINFO block. Currently only writes 1 */
void fat_clusters_flush ( struct super_block * sb )
{
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
struct buffer_head * bh ;
struct fat_boot_fsinfo * fsinfo ;
if ( sbi - > fat_bits ! = 32 )
return ;
bh = sb_bread ( sb , sbi - > fsinfo_sector ) ;
if ( bh = = NULL ) {
printk ( KERN_ERR " FAT: bread failed in fat_clusters_flush \n " ) ;
return ;
}
fsinfo = ( struct fat_boot_fsinfo * ) bh - > b_data ;
/* Sanity check */
if ( ! IS_FSINFO ( fsinfo ) ) {
2008-02-06 12:36:36 +03:00
printk ( KERN_ERR " FAT: Invalid FSINFO signature: "
" 0x%08x, 0x%08x (sector = %lu) \n " ,
2005-04-17 02:20:36 +04:00
le32_to_cpu ( fsinfo - > signature1 ) ,
le32_to_cpu ( fsinfo - > signature2 ) ,
sbi - > fsinfo_sector ) ;
} else {
if ( sbi - > free_clusters ! = - 1 )
fsinfo - > free_clusters = cpu_to_le32 ( sbi - > free_clusters ) ;
if ( sbi - > prev_free ! = - 1 )
fsinfo - > next_cluster = cpu_to_le32 ( sbi - > prev_free ) ;
mark_buffer_dirty ( bh ) ;
}
brelse ( bh ) ;
}
/*
* fat_chain_add ( ) adds a new cluster to the chain of clusters represented
* by inode .
*/
int fat_chain_add ( struct inode * inode , int new_dclus , int nr_cluster )
{
struct super_block * sb = inode - > i_sb ;
struct msdos_sb_info * sbi = MSDOS_SB ( sb ) ;
int ret , new_fclus , last ;
/*
* We must locate the last cluster of the file to add this new
* one ( new_dclus ) to the end of the link list ( the FAT ) .
*/
last = new_fclus = 0 ;
if ( MSDOS_I ( inode ) - > i_start ) {
int fclus , dclus ;
ret = fat_get_cluster ( inode , FAT_ENT_EOF , & fclus , & dclus ) ;
if ( ret < 0 )
return ret ;
new_fclus = fclus + 1 ;
last = dclus ;
}
/* add new one to the last of the cluster chain */
if ( last ) {
struct fat_entry fatent ;
fatent_init ( & fatent ) ;
ret = fat_ent_read ( inode , & fatent , last ) ;
if ( ret > = 0 ) {
int wait = inode_needs_sync ( inode ) ;
ret = fat_ent_write ( inode , & fatent , new_dclus , wait ) ;
fatent_brelse ( & fatent ) ;
}
if ( ret < 0 )
return ret ;
// fat_cache_add(inode, new_fclus, new_dclus);
} else {
MSDOS_I ( inode ) - > i_start = new_dclus ;
MSDOS_I ( inode ) - > i_logstart = new_dclus ;
/*
* Since generic_osync_inode ( ) synchronize later if
* this is not directory , we don ' t here .
*/
if ( S_ISDIR ( inode - > i_mode ) & & IS_DIRSYNC ( inode ) ) {
ret = fat_sync_inode ( inode ) ;
if ( ret )
return ret ;
} else
mark_inode_dirty ( inode ) ;
}
if ( new_fclus ! = ( inode - > i_blocks > > ( sbi - > cluster_bits - 9 ) ) ) {
2008-11-06 23:53:58 +03:00
fat_fs_panic ( sb , " clusters badly computed (%d != %llu) " ,
new_fclus ,
( llu ) ( inode - > i_blocks > > ( sbi - > cluster_bits - 9 ) ) ) ;
2005-04-17 02:20:36 +04:00
fat_cache_inval_inode ( inode ) ;
}
inode - > i_blocks + = nr_cluster < < ( sbi - > cluster_bits - 9 ) ;
return 0 ;
}
extern struct timezone sys_tz ;
2008-11-06 23:53:47 +03:00
/*
* The epoch of FAT timestamp is 1980.
* : bits : value
* date : 0 - 4 : day ( 1 - 31 )
* date : 5 - 8 : month ( 1 - 12 )
* date : 9 - 15 : year ( 0 - 127 ) from 1980
* time : 0 - 4 : sec ( 0 - 29 ) 2 sec counts
* time : 5 - 10 : min ( 0 - 59 )
* time : 11 - 15 : hour ( 0 - 23 )
*/
# define SECS_PER_MIN 60
# define SECS_PER_HOUR (60 * 60)
# define SECS_PER_DAY (SECS_PER_HOUR * 24)
# define UNIX_SECS_1980 315532800L
# if BITS_PER_LONG == 64
# define UNIX_SECS_2108 4354819200L
# endif
/* days between 1.1.70 and 1.1.80 (2 leap days) */
# define DAYS_DELTA (365 * 10 + 2)
/* 120 (2100 - 1980) isn't leap year */
# define YEAR_2100 120
# define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != YEAR_2100)
2005-04-17 02:20:36 +04:00
/* Linear day numbers of the respective 1sts in non-leap years. */
2008-11-06 23:53:47 +03:00
static time_t days_in_year [ ] = {
/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
0 , 0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 , 0 , 0 , 0 ,
2005-04-17 02:20:36 +04:00
} ;
2008-11-06 23:53:47 +03:00
/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
void fat_time_fat2unix ( struct msdos_sb_info * sbi , struct timespec * ts ,
__le16 __time , __le16 __date , u8 time_cs )
2005-04-17 02:20:36 +04:00
{
2008-11-06 23:53:47 +03:00
u16 time = le16_to_cpu ( __time ) , date = le16_to_cpu ( __date ) ;
time_t second , day , leap_day , month , year ;
2005-04-17 02:20:36 +04:00
2008-11-06 23:53:47 +03:00
year = date > > 9 ;
month = max ( 1 , ( date > > 5 ) & 0xf ) ;
day = max ( 1 , date & 0x1f ) - 1 ;
leap_day = ( year + 3 ) / 4 ;
if ( year > YEAR_2100 ) /* 2100 isn't leap year */
leap_day - - ;
if ( IS_LEAP_YEAR ( year ) & & month > 2 )
leap_day + + ;
second = ( time & 0x1f ) < < 1 ;
second + = ( ( time > > 5 ) & 0x3f ) * SECS_PER_MIN ;
second + = ( time > > 11 ) * SECS_PER_HOUR ;
second + = ( year * 365 + leap_day
+ days_in_year [ month ] + day
+ DAYS_DELTA ) * SECS_PER_DAY ;
if ( ! sbi - > options . tz_utc )
second + = sys_tz . tz_minuteswest * SECS_PER_MIN ;
if ( time_cs ) {
ts - > tv_sec = second + ( time_cs / 100 ) ;
ts - > tv_nsec = ( time_cs % 100 ) * 10000000 ;
} else {
ts - > tv_sec = second ;
ts - > tv_nsec = 0 ;
}
2005-04-17 02:20:36 +04:00
}
2008-11-06 23:53:47 +03:00
/* Convert linear UNIX date to a FAT time/date pair. */
void fat_time_unix2fat ( struct msdos_sb_info * sbi , struct timespec * ts ,
__le16 * time , __le16 * date , u8 * time_cs )
2005-04-17 02:20:36 +04:00
{
2008-11-06 23:53:47 +03:00
time_t second = ts - > tv_sec ;
time_t day , leap_day , month , year ;
2005-04-17 02:20:36 +04:00
2008-11-06 23:53:47 +03:00
if ( ! sbi - > options . tz_utc )
second - = sys_tz . tz_minuteswest * SECS_PER_MIN ;
2005-04-17 02:20:36 +04:00
/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
2008-11-06 23:53:47 +03:00
if ( second < UNIX_SECS_1980 ) {
* time = 0 ;
* date = cpu_to_le16 ( ( 0 < < 9 ) | ( 1 < < 5 ) | 1 ) ;
if ( time_cs )
* time_cs = 0 ;
return ;
}
# if BITS_PER_LONG == 64
if ( second > = UNIX_SECS_2108 ) {
* time = cpu_to_le16 ( ( 23 < < 11 ) | ( 59 < < 5 ) | 29 ) ;
* date = cpu_to_le16 ( ( 127 < < 9 ) | ( 12 < < 5 ) | 31 ) ;
if ( time_cs )
* time_cs = 199 ;
return ;
}
# endif
day = second / SECS_PER_DAY - DAYS_DELTA ;
year = day / 365 ;
leap_day = ( year + 3 ) / 4 ;
if ( year > YEAR_2100 ) /* 2100 isn't leap year */
leap_day - - ;
if ( year * 365 + leap_day > day )
2005-04-17 02:20:36 +04:00
year - - ;
2008-11-06 23:53:47 +03:00
leap_day = ( year + 3 ) / 4 ;
if ( year > YEAR_2100 ) /* 2100 isn't leap year */
leap_day - - ;
day - = year * 365 + leap_day ;
if ( IS_LEAP_YEAR ( year ) & & day = = days_in_year [ 3 ] ) {
2005-04-17 02:20:36 +04:00
month = 2 ;
} else {
2008-11-06 23:53:47 +03:00
if ( IS_LEAP_YEAR ( year ) & & day > days_in_year [ 3 ] )
day - - ;
for ( month = 1 ; month < 12 ; month + + ) {
if ( days_in_year [ month + 1 ] > day )
2005-04-17 02:20:36 +04:00
break ;
}
}
2008-11-06 23:53:47 +03:00
day - = days_in_year [ month ] ;
2005-04-17 02:20:36 +04:00
2008-11-06 23:53:47 +03:00
* time = cpu_to_le16 ( ( ( second / SECS_PER_HOUR ) % 24 ) < < 11
| ( ( second / SECS_PER_MIN ) % 60 ) < < 5
| ( second % SECS_PER_MIN ) > > 1 ) ;
* date = cpu_to_le16 ( ( year < < 9 ) | ( month < < 5 ) | ( day + 1 ) ) ;
if ( time_cs )
* time_cs = ( ts - > tv_sec & 1 ) * 100 + ts - > tv_nsec / 10000000 ;
}
EXPORT_SYMBOL_GPL ( fat_time_unix2fat ) ;
2005-04-17 02:20:36 +04:00
int fat_sync_bhs ( struct buffer_head * * bhs , int nr_bhs )
{
2006-02-03 14:04:42 +03:00
int i , err = 0 ;
2005-04-17 02:20:36 +04:00
2006-02-03 14:04:42 +03:00
ll_rw_block ( SWRITE , nr_bhs , bhs ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < nr_bhs ; i + + ) {
wait_on_buffer ( bhs [ i ] ) ;
if ( buffer_eopnotsupp ( bhs [ i ] ) ) {
clear_buffer_eopnotsupp ( bhs [ i ] ) ;
err = - EOPNOTSUPP ;
} else if ( ! err & & ! buffer_uptodate ( bhs [ i ] ) )
err = - EIO ;
}
return err ;
}