2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
* linux / fs / adfs / dir_fplus . c
*
* Copyright ( C ) 1997 - 1999 Russell King
*/
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 ;
2019-12-09 14:09:20 +03:00
int ret = - EIO ;
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 ;
}
2019-12-09 14:09:10 +03:00
dir - > bhs [ 0 ] = sb_bread ( sb , block ) ;
if ( ! dir - > bhs [ 0 ] )
2005-04-17 02:20:36 +04:00
goto out ;
dir - > nr_buffers + = 1 ;
2019-12-09 14:09:10 +03:00
h = ( struct adfs_bigdirheader * ) dir - > bhs [ 0 ] - > b_data ;
2005-04-17 02:20:36 +04:00
size = le32_to_cpu ( h - > bigdirsize ) ;
if ( size ! = sz ) {
2019-06-04 16:49:52 +03:00
adfs_msg ( sb , KERN_WARNING ,
" directory header size %X does not match directory size %X " ,
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 ) ) {
2019-06-04 16:49:52 +03:00
adfs_error ( sb , " dir %06x has malformed header " , 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 */
2019-12-09 14:09:10 +03:00
struct buffer_head * * bhs =
2014-08-09 01:22:24 +04:00
kcalloc ( size , sizeof ( struct buffer_head * ) ,
2011-03-23 02:35:04 +03:00
GFP_KERNEL ) ;
2019-12-09 14:09:10 +03:00
if ( ! bhs ) {
2019-06-04 16:49:52 +03:00
adfs_msg ( sb , KERN_ERR ,
" not enough memory for dir object %X (%d blocks) " ,
id , size ) ;
2015-04-16 22:48:13 +03:00
ret = - ENOMEM ;
2011-03-23 02:35:04 +03:00
goto out ;
}
2019-12-09 14:09:10 +03:00
dir - > bhs = bhs ;
2011-03-23 02:35:04 +03:00
/* copy over the pointer to the block that we've already read */
2019-12-09 14:09:10 +03:00
dir - > bhs [ 0 ] = dir - > bh [ 0 ] ;
2011-03-23 02:35:04 +03:00
}
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 ;
}
2019-12-09 14:09:10 +03:00
dir - > bhs [ blk ] = sb_bread ( sb , block ) ;
if ( ! dir - > bhs [ blk ] ) {
2014-08-09 01:22:26 +04:00
adfs_error ( sb , " dir object %x failed read for offset %d, mapped block %lX " ,
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 * )
2019-12-09 14:09:10 +03:00
( dir - > bhs [ 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 ) {
2019-06-04 16:49:52 +03:00
adfs_error ( sb , " dir %06x has malformed tail " , 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 ) ;
return 0 ;
2011-03-23 02:35:04 +03:00
2005-04-17 02:20:36 +04:00
out :
2019-12-09 14:09:20 +03:00
adfs_dir_relse ( dir ) ;
2011-03-23 02:35:04 +03:00
2005-04-17 02:20:36 +04:00
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 =
2019-12-09 14:09:10 +03:00
( struct adfs_bigdirheader * ) dir - > bhs [ 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 int
adfs_fplus_getnext ( struct adfs_dir * dir , struct object_info * obj )
{
2011-03-23 02:35:04 +03:00
struct adfs_bigdirheader * h =
2019-12-09 14:09:10 +03:00
( struct adfs_bigdirheader * ) dir - > bhs [ 0 ] - > b_data ;
2005-04-17 02:20:36 +04:00
struct adfs_bigdirentry bde ;
unsigned int offset ;
2019-12-09 14:09:30 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
if ( dir - > pos > = le32_to_cpu ( h - > bigdirentries ) )
2019-12-09 14:09:30 +03:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
offset = offsetof ( struct adfs_bigdirheader , bigdirname ) ;
offset + = ( ( le32_to_cpu ( h - > bigdirnamelen ) + 4 ) & ~ 3 ) ;
offset + = dir - > pos * sizeof ( struct adfs_bigdirentry ) ;
2019-12-09 14:09:30 +03:00
ret = adfs_dir_copyfrom ( & bde , dir , offset ,
sizeof ( struct adfs_bigdirentry ) ) ;
if ( ret )
return ret ;
2005-04-17 02:20:36 +04:00
obj - > loadaddr = le32_to_cpu ( bde . bigdirload ) ;
obj - > execaddr = le32_to_cpu ( bde . bigdirexec ) ;
obj - > size = le32_to_cpu ( bde . bigdirlen ) ;
2019-06-04 16:49:57 +03:00
obj - > indaddr = le32_to_cpu ( bde . bigdirindaddr ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2019-12-09 14:09:30 +03:00
ret = adfs_dir_copyfrom ( obj - > name , dir , offset , obj - > name_len ) ;
if ( ret )
return ret ;
2019-03-24 15:57:32 +03:00
adfs_object_fixup ( dir , obj ) ;
2011-03-23 02:35:06 +03:00
2005-04-17 02:20:36 +04:00
dir - > pos + = 1 ;
2019-12-09 14:09:30 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2015-11-21 18:15:37 +03:00
const struct adfs_dir_ops adfs_fplus_dir_ops = {
2005-04-17 02:20:36 +04:00
. read = adfs_fplus_read ,
. setpos = adfs_fplus_setpos ,
. getnext = adfs_fplus_getnext ,
} ;