2005-04-17 02:20:36 +04:00
/*
* linux / fs / adfs / dir . c
*
* Copyright ( C ) 1999 - 2000 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 .
*
* Common directory handling for ADFS
*/
# include "adfs.h"
/*
* For future . This should probably be per - directory .
*/
static DEFINE_RWLOCK ( adfs_dir_lock ) ;
2019-03-24 15:57:32 +03:00
void adfs_object_fixup ( struct adfs_dir * dir , struct object_info * obj )
{
2019-03-24 16:22:28 +03:00
unsigned int i ;
/*
* RISC OS allows the use of ' / ' in directory entry names , so we need
* to fix these up . ' / ' is typically used for FAT compatibility to
* represent ' . ' , so do the same conversion here . In any case , ' . '
* will never be in a RISC OS name since it is used as the pathname
* separator .
*/
for ( i = 0 ; i < obj - > name_len ; i + + )
if ( obj - > name [ i ] = = ' / ' )
obj - > name [ i ] = ' . ' ;
2019-03-24 15:57:32 +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
static int
2013-05-18 01:30:10 +04:00
adfs_readdir ( struct file * file , struct dir_context * ctx )
2005-04-17 02:20:36 +04:00
{
2013-05-18 01:30:10 +04:00
struct inode * inode = file_inode ( file ) ;
2005-04-17 02:20:36 +04:00
struct super_block * sb = inode - > i_sb ;
2015-11-21 18:15:37 +03:00
const struct adfs_dir_ops * ops = ADFS_SB ( sb ) - > s_dir ;
2005-04-17 02:20:36 +04:00
struct object_info obj ;
struct adfs_dir dir ;
int ret = 0 ;
2013-05-18 01:30:10 +04:00
if ( ctx - > pos > > 32 )
return 0 ;
2005-04-17 02:20:36 +04:00
ret = ops - > read ( sb , inode - > i_ino , inode - > i_size , & dir ) ;
if ( ret )
2013-05-18 01:30:10 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
2013-05-18 01:30:10 +04:00
if ( ctx - > pos = = 0 ) {
if ( ! dir_emit_dot ( file , ctx ) )
2005-04-17 02:20:36 +04:00
goto free_out ;
2013-05-18 01:30:10 +04:00
ctx - > pos = 1 ;
}
if ( ctx - > pos = = 1 ) {
if ( ! dir_emit ( ctx , " .. " , 2 , dir . parent_id , DT_DIR ) )
2005-04-17 02:20:36 +04:00
goto free_out ;
2013-05-18 01:30:10 +04:00
ctx - > pos = 2 ;
2005-04-17 02:20:36 +04:00
}
read_lock ( & adfs_dir_lock ) ;
2013-05-18 01:30:10 +04:00
ret = ops - > setpos ( & dir , ctx - > pos - 2 ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
goto unlock_out ;
while ( ops - > getnext ( & dir , & obj ) = = 0 ) {
2013-05-18 01:30:10 +04:00
if ( ! dir_emit ( ctx , obj . name , obj . name_len ,
obj . file_id , DT_UNKNOWN ) )
break ;
ctx - > pos + + ;
2005-04-17 02:20:36 +04:00
}
unlock_out :
read_unlock ( & adfs_dir_lock ) ;
free_out :
ops - > free ( & dir ) ;
return ret ;
}
int
2009-06-08 08:44:42 +04:00
adfs_dir_update ( struct super_block * sb , struct object_info * obj , int wait )
2005-04-17 02:20:36 +04:00
{
int ret = - EINVAL ;
# ifdef CONFIG_ADFS_FS_RW
2015-11-21 18:15:37 +03:00
const struct adfs_dir_ops * ops = ADFS_SB ( sb ) - > s_dir ;
2005-04-17 02:20:36 +04:00
struct adfs_dir dir ;
printk ( KERN_INFO " adfs_dir_update: object %06X in dir %06X \n " ,
obj - > file_id , obj - > parent_id ) ;
if ( ! ops - > update ) {
ret = - EINVAL ;
goto out ;
}
ret = ops - > read ( sb , obj - > parent_id , 0 , & dir ) ;
if ( ret )
goto out ;
write_lock ( & adfs_dir_lock ) ;
ret = ops - > update ( & dir , obj ) ;
write_unlock ( & adfs_dir_lock ) ;
2009-06-08 08:44:42 +04:00
if ( wait ) {
int err = ops - > sync ( & dir ) ;
if ( ! ret )
ret = err ;
}
2005-04-17 02:20:36 +04:00
ops - > free ( & dir ) ;
out :
# endif
return ret ;
}
2019-03-24 15:22:04 +03:00
static unsigned char adfs_tolower ( unsigned char c )
{
if ( c > = ' A ' & & c < = ' Z ' )
c + = ' a ' - ' A ' ;
return c ;
}
2019-03-24 13:54:03 +03:00
static int __adfs_compare ( const unsigned char * qstr , u32 qlen ,
const char * str , u32 len )
2005-04-17 02:20:36 +04:00
{
2019-03-24 13:54:03 +03:00
u32 i ;
2005-04-17 02:20:36 +04:00
2019-03-24 13:54:03 +03:00
if ( qlen ! = len )
return 1 ;
2005-04-17 02:20:36 +04:00
2019-03-24 15:22:04 +03:00
for ( i = 0 ; i < qlen ; i + + )
if ( adfs_tolower ( qstr [ i ] ) ! = adfs_tolower ( str [ i ] ) )
2019-03-24 13:54:03 +03:00
return 1 ;
2019-03-24 15:22:04 +03:00
2019-03-24 13:54:03 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2019-03-24 13:54:03 +03:00
static int adfs_dir_lookup_byname ( struct inode * inode , const struct qstr * qstr ,
struct object_info * obj )
2005-04-17 02:20:36 +04:00
{
struct super_block * sb = inode - > i_sb ;
2015-11-21 18:15:37 +03:00
const struct adfs_dir_ops * ops = ADFS_SB ( sb ) - > s_dir ;
2019-03-24 13:54:03 +03:00
const unsigned char * name ;
2005-04-17 02:20:36 +04:00
struct adfs_dir dir ;
2019-03-24 13:54:03 +03:00
u32 name_len ;
2005-04-17 02:20:36 +04:00
int ret ;
ret = ops - > read ( sb , inode - > i_ino , inode - > i_size , & dir ) ;
if ( ret )
goto out ;
if ( ADFS_I ( inode ) - > parent_id ! = dir . parent_id ) {
2014-08-09 01:22:26 +04:00
adfs_error ( sb , " parent directory changed under me! (%lx but got %x) \n " ,
2005-04-17 02:20:36 +04:00
ADFS_I ( inode ) - > parent_id , dir . parent_id ) ;
ret = - EIO ;
goto free_out ;
}
obj - > parent_id = inode - > i_ino ;
read_lock ( & adfs_dir_lock ) ;
ret = ops - > setpos ( & dir , 0 ) ;
if ( ret )
goto unlock_out ;
ret = - ENOENT ;
2019-03-24 13:54:03 +03:00
name = qstr - > name ;
name_len = qstr - > len ;
2005-04-17 02:20:36 +04:00
while ( ops - > getnext ( & dir , obj ) = = 0 ) {
2019-03-24 13:54:03 +03:00
if ( ! __adfs_compare ( name , name_len , obj - > name , obj - > name_len ) ) {
2005-04-17 02:20:36 +04:00
ret = 0 ;
break ;
}
}
unlock_out :
read_unlock ( & adfs_dir_lock ) ;
free_out :
ops - > free ( & dir ) ;
out :
return ret ;
}
2006-03-28 13:56:42 +04:00
const struct file_operations adfs_dir_operations = {
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
2008-08-24 15:24:41 +04:00
. llseek = generic_file_llseek ,
2013-05-18 01:30:10 +04:00
. iterate = adfs_readdir ,
2010-05-26 19:53:41 +04:00
. fsync = generic_file_fsync ,
2005-04-17 02:20:36 +04:00
} ;
static int
2013-05-22 02:22:44 +04:00
adfs_hash ( const struct dentry * parent , struct qstr * qstr )
2005-04-17 02:20:36 +04:00
{
const unsigned char * name ;
unsigned long hash ;
2019-03-24 14:02:02 +03:00
u32 len ;
2005-04-17 02:20:36 +04:00
2019-03-24 14:02:02 +03:00
if ( qstr - > len > ADFS_SB ( parent - > d_sb ) - > s_namelen )
return - ENAMETOOLONG ;
2005-04-17 02:20:36 +04:00
2019-03-24 14:02:02 +03:00
len = qstr - > len ;
2005-04-17 02:20:36 +04:00
name = qstr - > name ;
2016-06-10 17:51:30 +03:00
hash = init_name_hash ( parent ) ;
2019-03-24 14:02:02 +03:00
while ( len - - )
2019-03-24 15:22:04 +03:00
hash = partial_name_hash ( adfs_tolower ( * name + + ) , hash ) ;
2005-04-17 02:20:36 +04:00
qstr - > hash = end_name_hash ( hash ) ;
return 0 ;
}
/*
* Compare two names , taking note of the name length
* requirements of the underlying filesystem .
*/
2019-03-24 13:54:03 +03:00
static int adfs_compare ( const struct dentry * dentry , unsigned int len ,
const char * str , const struct qstr * qstr )
2005-04-17 02:20:36 +04:00
{
2019-03-24 13:54:03 +03:00
return __adfs_compare ( qstr - > name , qstr - > len , str , len ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-20 08:55:13 +03:00
const struct dentry_operations adfs_dentry_operations = {
2005-04-17 02:20:36 +04:00
. d_hash = adfs_hash ,
. d_compare = adfs_compare ,
} ;
static struct dentry *
2012-06-11 01:13:09 +04:00
adfs_lookup ( struct inode * dir , struct dentry * dentry , unsigned int flags )
2005-04-17 02:20:36 +04:00
{
struct inode * inode = NULL ;
struct object_info obj ;
int error ;
error = adfs_dir_lookup_byname ( dir , & dentry - > d_name , & obj ) ;
if ( error = = 0 ) {
/*
* This only returns NULL if get_empty_inode
* fails .
*/
inode = adfs_iget ( dir - > i_sb , & obj ) ;
2018-05-01 05:57:42 +03:00
if ( ! inode )
inode = ERR_PTR ( - EACCES ) ;
} else if ( error ! = - ENOENT ) {
inode = ERR_PTR ( error ) ;
2005-04-17 02:20:36 +04:00
}
2018-05-01 05:57:42 +03:00
return d_splice_alias ( inode , dentry ) ;
2005-04-17 02:20:36 +04:00
}
/*
* directories can handle most operations . . .
*/
2007-02-12 11:55:38 +03:00
const struct inode_operations adfs_dir_inode_operations = {
2005-04-17 02:20:36 +04:00
. lookup = adfs_lookup ,
. setattr = adfs_notify_change ,
} ;