2009-01-05 11:46:23 +03:00
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright ( c ) 2002 , 2003 , 2004 , 2005 , 2006 , 2007 , 2008
2011-05-26 13:39:56 +04:00
* Phillip Lougher < phillip @ squashfs . org . uk >
2009-01-05 11:46:23 +03:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2 ,
* or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* dir . c
*/
/*
* This file implements code to read directories from disk .
*
* See namei . c for a description of directory organisation on disk .
*/
# include <linux/fs.h>
# include <linux/vfs.h>
# include <linux/slab.h>
# include "squashfs_fs.h"
# include "squashfs_fs_sb.h"
# include "squashfs_fs_i.h"
# include "squashfs.h"
static const unsigned char squashfs_filetype_table [ ] = {
DT_UNKNOWN , DT_DIR , DT_REG , DT_LNK , DT_BLK , DT_CHR , DT_FIFO , DT_SOCK
} ;
/*
* Lookup offset ( f_pos ) in the directory index , returning the
* metadata block containing it .
*
* If we get an error reading the index then return the part of the index
* ( if any ) we have managed to read - the index isn ' t essential , just
* quicker .
*/
static int get_dir_index_using_offset ( struct super_block * sb ,
u64 * next_block , int * next_offset , u64 index_start , int index_offset ,
int i_count , u64 f_pos )
{
struct squashfs_sb_info * msblk = sb - > s_fs_info ;
int err , i , index , length = 0 ;
2013-09-03 07:52:52 +04:00
unsigned int size ;
2009-01-05 11:46:23 +03:00
struct squashfs_dir_index dir_index ;
TRACE ( " Entered get_dir_index_using_offset, i_count %d, f_pos %lld \n " ,
i_count , f_pos ) ;
/*
* Translate from external f_pos to the internal f_pos . This
* is offset by 3 because we invent " . " and " .. " entries which are
* not actually stored in the directory .
*/
2012-03-09 06:27:49 +04:00
if ( f_pos < = 3 )
2009-01-05 11:46:23 +03:00
return f_pos ;
f_pos - = 3 ;
for ( i = 0 ; i < i_count ; i + + ) {
err = squashfs_read_metadata ( sb , & dir_index , & index_start ,
& index_offset , sizeof ( dir_index ) ) ;
if ( err < 0 )
break ;
index = le32_to_cpu ( dir_index . index ) ;
if ( index > f_pos )
/*
* Found the index we ' re looking for .
*/
break ;
2013-09-03 07:52:52 +04:00
size = le32_to_cpu ( dir_index . size ) + 1 ;
/* size should never be larger than SQUASHFS_NAME_LEN */
if ( size > SQUASHFS_NAME_LEN )
break ;
2009-01-05 11:46:23 +03:00
err = squashfs_read_metadata ( sb , NULL , & index_start ,
2013-09-03 07:52:52 +04:00
& index_offset , size ) ;
2009-01-05 11:46:23 +03:00
if ( err < 0 )
break ;
length = index ;
* next_block = le32_to_cpu ( dir_index . start_block ) +
msblk - > directory_table ;
}
* next_offset = ( length + * next_offset ) % SQUASHFS_METADATA_SIZE ;
/*
* Translate back from internal f_pos to external f_pos .
*/
return length + 3 ;
}
2013-05-16 09:17:58 +04:00
static int squashfs_readdir ( struct file * file , struct dir_context * ctx )
2009-01-05 11:46:23 +03:00
{
2013-01-24 02:07:38 +04:00
struct inode * inode = file_inode ( file ) ;
2009-01-05 11:46:23 +03:00
struct squashfs_sb_info * msblk = inode - > i_sb - > s_fs_info ;
u64 block = squashfs_i ( inode ) - > start + msblk - > directory_table ;
2013-09-04 05:58:12 +04:00
int offset = squashfs_i ( inode ) - > offset , length , err ;
unsigned int inode_number , dir_count , size , type ;
2009-01-05 11:46:23 +03:00
struct squashfs_dir_header dirh ;
struct squashfs_dir_entry * dire ;
TRACE ( " Entered squashfs_readdir [%llx:%x] \n " , block , offset ) ;
dire = kmalloc ( sizeof ( * dire ) + SQUASHFS_NAME_LEN + 1 , GFP_KERNEL ) ;
if ( dire = = NULL ) {
ERROR ( " Failed to allocate squashfs_dir_entry \n " ) ;
goto finish ;
}
/*
* Return " . " and " .. " entries as the first two filenames in the
* directory . To maximise compression these two entries are not
* stored in the directory , and so we invent them here .
*
* It also means that the external f_pos is offset by 3 from the
* on - disk directory f_pos .
*/
2013-05-16 09:17:58 +04:00
while ( ctx - > pos < 3 ) {
2009-01-05 11:46:23 +03:00
char * name ;
int i_ino ;
2013-05-16 09:17:58 +04:00
if ( ctx - > pos = = 0 ) {
2009-01-05 11:46:23 +03:00
name = " . " ;
size = 1 ;
i_ino = inode - > i_ino ;
} else {
name = " .. " ;
size = 2 ;
i_ino = squashfs_i ( inode ) - > parent ;
}
2013-05-16 09:17:58 +04:00
if ( ! dir_emit ( ctx , name , size , i_ino ,
squashfs_filetype_table [ 1 ] ) )
2009-01-05 11:46:23 +03:00
goto finish ;
2013-05-16 09:17:58 +04:00
ctx - > pos + = size ;
2009-01-05 11:46:23 +03:00
}
length = get_dir_index_using_offset ( inode - > i_sb , & block , & offset ,
squashfs_i ( inode ) - > dir_idx_start ,
squashfs_i ( inode ) - > dir_idx_offset ,
squashfs_i ( inode ) - > dir_idx_cnt ,
2013-05-16 09:17:58 +04:00
ctx - > pos ) ;
2009-01-05 11:46:23 +03:00
while ( length < i_size_read ( inode ) ) {
/*
* Read directory header
*/
err = squashfs_read_metadata ( inode - > i_sb , & dirh , & block ,
& offset , sizeof ( dirh ) ) ;
if ( err < 0 )
goto failed_read ;
length + = sizeof ( dirh ) ;
dir_count = le32_to_cpu ( dirh . count ) + 1 ;
2011-03-16 01:09:55 +03:00
2012-02-02 11:34:49 +04:00
if ( dir_count > SQUASHFS_DIR_COUNT )
2011-03-16 01:09:55 +03:00
goto failed_read ;
2009-01-05 11:46:23 +03:00
while ( dir_count - - ) {
/*
* Read directory entry .
*/
err = squashfs_read_metadata ( inode - > i_sb , dire , & block ,
& offset , sizeof ( * dire ) ) ;
if ( err < 0 )
goto failed_read ;
size = le16_to_cpu ( dire - > size ) + 1 ;
2011-03-16 01:09:55 +03:00
/* size should never be larger than SQUASHFS_NAME_LEN */
if ( size > SQUASHFS_NAME_LEN )
goto failed_read ;
2009-01-05 11:46:23 +03:00
err = squashfs_read_metadata ( inode - > i_sb , dire - > name ,
& block , & offset , size ) ;
if ( err < 0 )
goto failed_read ;
length + = sizeof ( * dire ) + size ;
2013-05-16 09:17:58 +04:00
if ( ctx - > pos > = length )
2009-01-05 11:46:23 +03:00
continue ;
dire - > name [ size ] = ' \0 ' ;
inode_number = le32_to_cpu ( dirh . inode_number ) +
( ( short ) le16_to_cpu ( dire - > inode_number ) ) ;
type = le16_to_cpu ( dire - > type ) ;
2013-09-04 05:58:12 +04:00
if ( type > SQUASHFS_MAX_DIR_TYPE )
goto failed_read ;
2013-05-16 09:17:58 +04:00
if ( ! dir_emit ( ctx , dire - > name , size ,
2009-01-05 11:46:23 +03:00
inode_number ,
2013-05-16 09:17:58 +04:00
squashfs_filetype_table [ type ] ) )
2009-01-05 11:46:23 +03:00
goto finish ;
2013-05-16 09:17:58 +04:00
ctx - > pos = length ;
2009-01-05 11:46:23 +03:00
}
}
finish :
kfree ( dire ) ;
return 0 ;
failed_read :
ERROR ( " Unable to read directory block [%llx:%x] \n " , block , offset ) ;
kfree ( dire ) ;
return 0 ;
}
const struct file_operations squashfs_dir_ops = {
. read = generic_read_dir ,
2016-05-01 06:08:45 +03:00
. iterate_shared = squashfs_readdir ,
. llseek = generic_file_llseek ,
2009-01-05 11:46:23 +03:00
} ;