2005-04-17 02:20:36 +04:00
/*
* linux / fs / minix / bitmap . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*/
/*
* Modified for 680 x0 by Hamish Macdonald
* Fixed for 680 x0 by Andreas Schwab
*/
/* bitmap.c contains the code that handles the inode and block bitmaps */
# include "minix.h"
# include <linux/smp_lock.h>
# include <linux/buffer_head.h>
# include <linux/bitops.h>
static int nibblemap [ ] = { 4 , 3 , 3 , 2 , 3 , 2 , 2 , 1 , 3 , 2 , 2 , 1 , 2 , 1 , 1 , 0 } ;
static unsigned long count_free ( struct buffer_head * map [ ] , unsigned numblocks , __u32 numbits )
{
unsigned i , j , sum = 0 ;
struct buffer_head * bh ;
for ( i = 0 ; i < numblocks - 1 ; i + + ) {
if ( ! ( bh = map [ i ] ) )
return ( 0 ) ;
2007-02-12 11:52:49 +03:00
for ( j = 0 ; j < bh - > b_size ; j + + )
2005-04-17 02:20:36 +04:00
sum + = nibblemap [ bh - > b_data [ j ] & 0xf ]
+ nibblemap [ ( bh - > b_data [ j ] > > 4 ) & 0xf ] ;
}
if ( numblocks = = 0 | | ! ( bh = map [ numblocks - 1 ] ) )
return ( 0 ) ;
2007-02-12 11:52:49 +03:00
i = ( ( numbits - ( numblocks - 1 ) * bh - > b_size * 8 ) / 16 ) * 2 ;
2005-04-17 02:20:36 +04:00
for ( j = 0 ; j < i ; j + + ) {
sum + = nibblemap [ bh - > b_data [ j ] & 0xf ]
+ nibblemap [ ( bh - > b_data [ j ] > > 4 ) & 0xf ] ;
}
i = numbits % 16 ;
if ( i ! = 0 ) {
i = * ( __u16 * ) ( & bh - > b_data [ j ] ) | ~ ( ( 1 < < i ) - 1 ) ;
sum + = nibblemap [ i & 0xf ] + nibblemap [ ( i > > 4 ) & 0xf ] ;
sum + = nibblemap [ ( i > > 8 ) & 0xf ] + nibblemap [ ( i > > 12 ) & 0xf ] ;
}
return ( sum ) ;
}
2007-02-12 11:52:49 +03:00
void minix_free_block ( struct inode * inode , unsigned long block )
2005-04-17 02:20:36 +04:00
{
2007-02-12 11:52:49 +03:00
struct super_block * sb = inode - > i_sb ;
struct minix_sb_info * sbi = minix_sb ( sb ) ;
struct buffer_head * bh ;
int k = sb - > s_blocksize_bits + 3 ;
unsigned long bit , zone ;
2005-04-17 02:20:36 +04:00
if ( block < sbi - > s_firstdatazone | | block > = sbi - > s_nzones ) {
2006-03-25 14:07:42 +03:00
printk ( " Trying to free block not in datazone \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
zone = block - sbi - > s_firstdatazone + 1 ;
2007-02-12 11:52:49 +03:00
bit = zone & ( ( 1 < < k ) - 1 ) ;
zone > > = k ;
2005-04-17 02:20:36 +04:00
if ( zone > = sbi - > s_zmap_blocks ) {
printk ( " minix_free_block: nonexistent bitmap buffer \n " ) ;
return ;
}
bh = sbi - > s_zmap [ zone ] ;
lock_kernel ( ) ;
2007-02-12 11:52:49 +03:00
if ( ! minix_test_and_clear_bit ( bit , bh - > b_data ) )
printk ( " minix_free_block (%s:%lu): bit already cleared \n " ,
2005-04-17 02:20:36 +04:00
sb - > s_id , block ) ;
unlock_kernel ( ) ;
mark_buffer_dirty ( bh ) ;
return ;
}
int minix_new_block ( struct inode * inode )
{
struct minix_sb_info * sbi = minix_sb ( inode - > i_sb ) ;
2007-02-12 11:52:49 +03:00
int bits_per_zone = 8 * inode - > i_sb - > s_blocksize ;
2005-04-17 02:20:36 +04:00
int i ;
for ( i = 0 ; i < sbi - > s_zmap_blocks ; i + + ) {
struct buffer_head * bh = sbi - > s_zmap [ i ] ;
int j ;
lock_kernel ( ) ;
2007-02-12 11:52:49 +03:00
j = minix_find_first_zero_bit ( bh - > b_data , bits_per_zone ) ;
if ( j < bits_per_zone ) {
minix_set_bit ( j , bh - > b_data ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
mark_buffer_dirty ( bh ) ;
2007-02-12 11:52:49 +03:00
j + = i * bits_per_zone + sbi - > s_firstdatazone - 1 ;
2005-04-17 02:20:36 +04:00
if ( j < sbi - > s_firstdatazone | | j > = sbi - > s_nzones )
break ;
return j ;
}
unlock_kernel ( ) ;
}
return 0 ;
}
unsigned long minix_count_free_blocks ( struct minix_sb_info * sbi )
{
return ( count_free ( sbi - > s_zmap , sbi - > s_zmap_blocks ,
sbi - > s_nzones - sbi - > s_firstdatazone + 1 )
< < sbi - > s_log_zone_size ) ;
}
struct minix_inode *
minix_V1_raw_inode ( struct super_block * sb , ino_t ino , struct buffer_head * * bh )
{
int block ;
struct minix_sb_info * sbi = minix_sb ( sb ) ;
struct minix_inode * p ;
if ( ! ino | | ino > sbi - > s_ninodes ) {
printk ( " Bad inode number on dev %s: %ld is out of range \n " ,
sb - > s_id , ( long ) ino ) ;
return NULL ;
}
ino - - ;
block = 2 + sbi - > s_imap_blocks + sbi - > s_zmap_blocks +
ino / MINIX_INODES_PER_BLOCK ;
* bh = sb_bread ( sb , block ) ;
if ( ! * bh ) {
2006-03-25 14:07:42 +03:00
printk ( " Unable to read inode block \n " ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
p = ( void * ) ( * bh ) - > b_data ;
return p + ino % MINIX_INODES_PER_BLOCK ;
}
struct minix2_inode *
minix_V2_raw_inode ( struct super_block * sb , ino_t ino , struct buffer_head * * bh )
{
int block ;
struct minix_sb_info * sbi = minix_sb ( sb ) ;
struct minix2_inode * p ;
2007-02-12 11:52:49 +03:00
int minix2_inodes_per_block = sb - > s_blocksize / sizeof ( struct minix2_inode ) ;
2005-04-17 02:20:36 +04:00
* bh = NULL ;
if ( ! ino | | ino > sbi - > s_ninodes ) {
printk ( " Bad inode number on dev %s: %ld is out of range \n " ,
sb - > s_id , ( long ) ino ) ;
return NULL ;
}
ino - - ;
block = 2 + sbi - > s_imap_blocks + sbi - > s_zmap_blocks +
2007-02-12 11:52:49 +03:00
ino / minix2_inodes_per_block ;
2005-04-17 02:20:36 +04:00
* bh = sb_bread ( sb , block ) ;
if ( ! * bh ) {
2006-03-25 14:07:42 +03:00
printk ( " Unable to read inode block \n " ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
p = ( void * ) ( * bh ) - > b_data ;
2007-02-12 11:52:49 +03:00
return p + ino % minix2_inodes_per_block ;
2005-04-17 02:20:36 +04:00
}
/* Clear the link count and mode of a deleted inode on disk. */
static void minix_clear_inode ( struct inode * inode )
{
struct buffer_head * bh = NULL ;
if ( INODE_VERSION ( inode ) = = MINIX_V1 ) {
struct minix_inode * raw_inode ;
raw_inode = minix_V1_raw_inode ( inode - > i_sb , inode - > i_ino , & bh ) ;
if ( raw_inode ) {
raw_inode - > i_nlinks = 0 ;
raw_inode - > i_mode = 0 ;
}
} else {
struct minix2_inode * raw_inode ;
raw_inode = minix_V2_raw_inode ( inode - > i_sb , inode - > i_ino , & bh ) ;
if ( raw_inode ) {
raw_inode - > i_nlinks = 0 ;
raw_inode - > i_mode = 0 ;
}
}
if ( bh ) {
mark_buffer_dirty ( bh ) ;
brelse ( bh ) ;
}
}
void minix_free_inode ( struct inode * inode )
{
2007-02-12 11:52:49 +03:00
struct super_block * sb = inode - > i_sb ;
2005-04-17 02:20:36 +04:00
struct minix_sb_info * sbi = minix_sb ( inode - > i_sb ) ;
2007-02-12 11:52:49 +03:00
struct buffer_head * bh ;
int k = sb - > s_blocksize_bits + 3 ;
unsigned long ino , bit ;
2005-04-17 02:20:36 +04:00
ino = inode - > i_ino ;
if ( ino < 1 | | ino > sbi - > s_ninodes ) {
printk ( " minix_free_inode: inode 0 or nonexistent inode \n " ) ;
goto out ;
}
2007-02-12 11:52:49 +03:00
bit = ino & ( ( 1 < < k ) - 1 ) ;
ino > > = k ;
if ( ino > = sbi - > s_imap_blocks ) {
2005-04-17 02:20:36 +04:00
printk ( " minix_free_inode: nonexistent imap in superblock \n " ) ;
goto out ;
}
minix_clear_inode ( inode ) ; /* clear on-disk copy */
2007-02-12 11:52:49 +03:00
bh = sbi - > s_imap [ ino ] ;
2005-04-17 02:20:36 +04:00
lock_kernel ( ) ;
2007-02-12 11:52:49 +03:00
if ( ! minix_test_and_clear_bit ( bit , bh - > b_data ) )
printk ( " minix_free_inode: bit %lu already cleared \n " , bit ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
mark_buffer_dirty ( bh ) ;
out :
clear_inode ( inode ) ; /* clear in-memory copy */
}
struct inode * minix_new_inode ( const struct inode * dir , int * error )
{
struct super_block * sb = dir - > i_sb ;
struct minix_sb_info * sbi = minix_sb ( sb ) ;
struct inode * inode = new_inode ( sb ) ;
struct buffer_head * bh ;
2007-02-12 11:52:49 +03:00
int bits_per_zone = 8 * sb - > s_blocksize ;
unsigned long j ;
int i ;
2005-04-17 02:20:36 +04:00
if ( ! inode ) {
* error = - ENOMEM ;
return NULL ;
}
2007-02-12 11:52:49 +03:00
j = bits_per_zone ;
2005-04-17 02:20:36 +04:00
bh = NULL ;
* error = - ENOSPC ;
lock_kernel ( ) ;
for ( i = 0 ; i < sbi - > s_imap_blocks ; i + + ) {
bh = sbi - > s_imap [ i ] ;
2007-02-12 11:52:49 +03:00
j = minix_find_first_zero_bit ( bh - > b_data , bits_per_zone ) ;
if ( j < bits_per_zone )
2005-04-17 02:20:36 +04:00
break ;
}
2007-02-12 11:52:49 +03:00
if ( ! bh | | j > = bits_per_zone ) {
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
iput ( inode ) ;
return NULL ;
}
2007-02-12 11:52:49 +03:00
if ( minix_test_and_set_bit ( j , bh - > b_data ) ) { /* shouldn't happen */
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
2007-02-12 11:52:49 +03:00
printk ( " minix_new_inode: bit already set \n " ) ;
2005-04-17 02:20:36 +04:00
iput ( inode ) ;
return NULL ;
}
unlock_kernel ( ) ;
mark_buffer_dirty ( bh ) ;
2007-02-12 11:52:49 +03:00
j + = i * bits_per_zone ;
2005-04-17 02:20:36 +04:00
if ( ! j | | j > sbi - > s_ninodes ) {
iput ( inode ) ;
return NULL ;
}
inode - > i_uid = current - > fsuid ;
inode - > i_gid = ( dir - > i_mode & S_ISGID ) ? dir - > i_gid : current - > fsgid ;
inode - > i_ino = j ;
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME_SEC ;
2006-09-27 12:50:49 +04:00
inode - > i_blocks = 0 ;
2005-04-17 02:20:36 +04:00
memset ( & minix_i ( inode ) - > u , 0 , sizeof ( minix_i ( inode ) - > u ) ) ;
insert_inode_hash ( inode ) ;
mark_inode_dirty ( inode ) ;
* error = 0 ;
return inode ;
}
unsigned long minix_count_free_inodes ( struct minix_sb_info * sbi )
{
return count_free ( sbi - > s_imap , sbi - > s_imap_blocks , sbi - > s_ninodes + 1 ) ;
}