2005-04-17 02:20:36 +04:00
/*
* linux / fs / adfs / dir_fplus . c
*
* Copyright ( C ) 1997 - 1999 Russell King
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/buffer_head.h>
2011-03-23 02:35:04 +03:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include "adfs.h"
# include "dir_fplus.h"
static int
adfs_fplus_read ( struct super_block * sb , unsigned int id , unsigned int sz , struct adfs_dir * dir )
{
struct adfs_bigdirheader * h ;
struct adfs_bigdirtail * t ;
unsigned long block ;
unsigned int blk , size ;
int i , ret = - EIO ;
dir - > nr_buffers = 0 ;
2011-03-23 02:35:04 +03:00
/* start off using fixed bh set - only alloc for big dirs */
dir - > bh_fplus = & dir - > bh [ 0 ] ;
2005-04-17 02:20:36 +04:00
block = __adfs_block_map ( sb , id , 0 ) ;
if ( ! block ) {
adfs_error ( sb , " dir object %X has a hole at offset 0 " , id ) ;
goto out ;
}
2011-03-23 02:35:04 +03:00
dir - > bh_fplus [ 0 ] = sb_bread ( sb , block ) ;
if ( ! dir - > bh_fplus [ 0 ] )
2005-04-17 02:20:36 +04:00
goto out ;
dir - > nr_buffers + = 1 ;
2011-03-23 02:35:04 +03:00
h = ( struct adfs_bigdirheader * ) dir - > bh_fplus [ 0 ] - > b_data ;
2005-04-17 02:20:36 +04:00
size = le32_to_cpu ( h - > bigdirsize ) ;
if ( size ! = sz ) {
2011-03-23 02:35:04 +03:00
printk ( KERN_WARNING " adfs: adfs_fplus_read: "
" directory header size %X \n "
" does not match directory size %X \n " ,
size , sz ) ;
2005-04-17 02:20:36 +04:00
}
if ( h - > bigdirversion [ 0 ] ! = 0 | | h - > bigdirversion [ 1 ] ! = 0 | |
h - > bigdirversion [ 2 ] ! = 0 | | size & 2047 | |
2011-03-23 02:35:04 +03:00
h - > bigdirstartname ! = cpu_to_le32 ( BIGDIRSTARTNAME ) ) {
printk ( KERN_WARNING " adfs: dir object %X has "
" malformed dir header \n " , id ) ;
2005-04-17 02:20:36 +04:00
goto out ;
2011-03-23 02:35:04 +03:00
}
2005-04-17 02:20:36 +04:00
size > > = sb - > s_blocksize_bits ;
2014-08-09 01:22:22 +04:00
if ( size > ARRAY_SIZE ( dir - > bh ) ) {
2011-03-23 02:35:04 +03:00
/* this directory is too big for fixed bh set, must allocate */
struct buffer_head * * bh_fplus =
2014-08-09 01:22:24 +04:00
kcalloc ( size , sizeof ( struct buffer_head * ) ,
2011-03-23 02:35:04 +03:00
GFP_KERNEL ) ;
if ( ! bh_fplus ) {
adfs_error ( sb , " not enough memory for "
" dir object %X (%d blocks) " , id , size ) ;
goto out ;
}
dir - > bh_fplus = bh_fplus ;
/* copy over the pointer to the block that we've already read */
dir - > bh_fplus [ 0 ] = dir - > bh [ 0 ] ;
}
2005-04-17 02:20:36 +04:00
for ( blk = 1 ; blk < size ; blk + + ) {
block = __adfs_block_map ( sb , id , blk ) ;
if ( ! block ) {
adfs_error ( sb , " dir object %X has a hole at offset %d " , id , blk ) ;
goto out ;
}
2011-03-23 02:35:04 +03:00
dir - > bh_fplus [ blk ] = sb_bread ( sb , block ) ;
if ( ! dir - > bh_fplus [ blk ] ) {
adfs_error ( sb , " dir object %X failed read for "
" offset %d, mapped block %X " ,
id , blk , block ) ;
2005-04-17 02:20:36 +04:00
goto out ;
2011-03-23 02:35:04 +03:00
}
dir - > nr_buffers + = 1 ;
2005-04-17 02:20:36 +04:00
}
2011-03-23 02:35:04 +03:00
t = ( struct adfs_bigdirtail * )
( dir - > bh_fplus [ size - 1 ] - > b_data + ( sb - > s_blocksize - 8 ) ) ;
2005-04-17 02:20:36 +04:00
if ( t - > bigdirendname ! = cpu_to_le32 ( BIGDIRENDNAME ) | |
t - > bigdirendmasseq ! = h - > startmasseq | |
2011-03-23 02:35:04 +03:00
t - > reserved [ 0 ] ! = 0 | | t - > reserved [ 1 ] ! = 0 ) {
printk ( KERN_WARNING " adfs: dir object %X has "
" malformed dir end \n " , id ) ;
2005-04-17 02:20:36 +04:00
goto out ;
2011-03-23 02:35:04 +03:00
}
2005-04-17 02:20:36 +04:00
dir - > parent_id = le32_to_cpu ( h - > bigdirparent ) ;
dir - > sb = sb ;
return 0 ;
2011-03-23 02:35:04 +03:00
2005-04-17 02:20:36 +04:00
out :
2011-03-23 02:35:04 +03:00
if ( dir - > bh_fplus ) {
for ( i = 0 ; i < dir - > nr_buffers ; i + + )
brelse ( dir - > bh_fplus [ i ] ) ;
if ( & dir - > bh [ 0 ] ! = dir - > bh_fplus )
kfree ( dir - > bh_fplus ) ;
dir - > bh_fplus = NULL ;
}
dir - > nr_buffers = 0 ;
2005-04-17 02:20:36 +04:00
dir - > sb = NULL ;
return ret ;
}
static int
adfs_fplus_setpos ( struct adfs_dir * dir , unsigned int fpos )
{
2011-03-23 02:35:04 +03:00
struct adfs_bigdirheader * h =
( struct adfs_bigdirheader * ) dir - > bh_fplus [ 0 ] - > b_data ;
2005-04-17 02:20:36 +04:00
int ret = - ENOENT ;
if ( fpos < = le32_to_cpu ( h - > bigdirentries ) ) {
dir - > pos = fpos ;
ret = 0 ;
}
return ret ;
}
static void
dir_memcpy ( struct adfs_dir * dir , unsigned int offset , void * to , int len )
{
struct super_block * sb = dir - > sb ;
unsigned int buffer , partial , remainder ;
buffer = offset > > sb - > s_blocksize_bits ;
offset & = sb - > s_blocksize - 1 ;
partial = sb - > s_blocksize - offset ;
if ( partial > = len )
2011-03-23 02:35:04 +03:00
memcpy ( to , dir - > bh_fplus [ buffer ] - > b_data + offset , len ) ;
2005-04-17 02:20:36 +04:00
else {
char * c = ( char * ) to ;
remainder = len - partial ;
2011-03-23 02:35:04 +03:00
memcpy ( c ,
dir - > bh_fplus [ buffer ] - > b_data + offset ,
partial ) ;
memcpy ( c + partial ,
dir - > bh_fplus [ buffer + 1 ] - > b_data ,
remainder ) ;
2005-04-17 02:20:36 +04:00
}
}
static int
adfs_fplus_getnext ( struct adfs_dir * dir , struct object_info * obj )
{
2011-03-23 02:35:04 +03:00
struct adfs_bigdirheader * h =
( struct adfs_bigdirheader * ) dir - > bh_fplus [ 0 ] - > b_data ;
2005-04-17 02:20:36 +04:00
struct adfs_bigdirentry bde ;
unsigned int offset ;
int i , ret = - ENOENT ;
if ( dir - > pos > = le32_to_cpu ( h - > bigdirentries ) )
goto out ;
offset = offsetof ( struct adfs_bigdirheader , bigdirname ) ;
offset + = ( ( le32_to_cpu ( h - > bigdirnamelen ) + 4 ) & ~ 3 ) ;
offset + = dir - > pos * sizeof ( struct adfs_bigdirentry ) ;
dir_memcpy ( dir , offset , & bde , sizeof ( struct adfs_bigdirentry ) ) ;
obj - > loadaddr = le32_to_cpu ( bde . bigdirload ) ;
obj - > execaddr = le32_to_cpu ( bde . bigdirexec ) ;
obj - > size = le32_to_cpu ( bde . bigdirlen ) ;
obj - > file_id = le32_to_cpu ( bde . bigdirindaddr ) ;
obj - > attr = le32_to_cpu ( bde . bigdirattr ) ;
obj - > name_len = le32_to_cpu ( bde . bigdirobnamelen ) ;
offset = offsetof ( struct adfs_bigdirheader , bigdirname ) ;
offset + = ( ( le32_to_cpu ( h - > bigdirnamelen ) + 4 ) & ~ 3 ) ;
offset + = le32_to_cpu ( h - > bigdirentries ) * sizeof ( struct adfs_bigdirentry ) ;
offset + = le32_to_cpu ( bde . bigdirobnameptr ) ;
dir_memcpy ( dir , offset , obj - > name , obj - > name_len ) ;
for ( i = 0 ; i < obj - > name_len ; i + + )
if ( obj - > name [ i ] = = ' / ' )
obj - > name [ i ] = ' . ' ;
2011-03-23 02:35:06 +03:00
obj - > filetype = - 1 ;
/*
* object is a file and is filetyped and timestamped ?
* RISC OS 12 - bit filetype is stored in load_address [ 19 : 8 ]
*/
if ( ( 0 = = ( obj - > attr & ADFS_NDA_DIRECTORY ) ) & &
( 0xfff00000 = = ( 0xfff00000 & obj - > loadaddr ) ) ) {
obj - > filetype = ( __u16 ) ( ( 0x000fff00 & obj - > loadaddr ) > > 8 ) ;
/* optionally append the ,xyz hex filetype suffix */
if ( ADFS_SB ( dir - > sb ) - > s_ftsuffix )
obj - > name_len + =
append_filetype_suffix (
& obj - > name [ obj - > name_len ] ,
obj - > filetype ) ;
}
2005-04-17 02:20:36 +04:00
dir - > pos + = 1 ;
ret = 0 ;
out :
return ret ;
}
2009-06-08 08:44:42 +04:00
static int
adfs_fplus_sync ( struct adfs_dir * dir )
{
int err = 0 ;
int i ;
for ( i = dir - > nr_buffers - 1 ; i > = 0 ; i - - ) {
2011-03-23 02:35:04 +03:00
struct buffer_head * bh = dir - > bh_fplus [ i ] ;
2009-06-08 08:44:42 +04:00
sync_dirty_buffer ( bh ) ;
if ( buffer_req ( bh ) & & ! buffer_uptodate ( bh ) )
err = - EIO ;
}
return err ;
}
2005-04-17 02:20:36 +04:00
static void
adfs_fplus_free ( struct adfs_dir * dir )
{
int i ;
2011-03-23 02:35:04 +03:00
if ( dir - > bh_fplus ) {
for ( i = 0 ; i < dir - > nr_buffers ; i + + )
brelse ( dir - > bh_fplus [ i ] ) ;
if ( & dir - > bh [ 0 ] ! = dir - > bh_fplus )
kfree ( dir - > bh_fplus ) ;
dir - > bh_fplus = NULL ;
}
dir - > nr_buffers = 0 ;
2005-04-17 02:20:36 +04:00
dir - > sb = NULL ;
}
struct adfs_dir_ops adfs_fplus_dir_ops = {
. read = adfs_fplus_read ,
. setpos = adfs_fplus_setpos ,
. getnext = adfs_fplus_getnext ,
2009-06-08 08:44:42 +04:00
. sync = adfs_fplus_sync ,
2005-04-17 02:20:36 +04:00
. free = adfs_fplus_free
} ;