2009-02-12 13:40:10 +03:00
/* RomFS storage access routines
*
* Copyright © 2007 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* 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 of the License , or ( at your option ) any later version .
*/
# include <linux/fs.h>
# include <linux/mtd/super.h>
# include <linux/buffer_head.h>
# include "internal.h"
# if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK)
# error no ROMFS backing store interface configured
# endif
# ifdef CONFIG_ROMFS_ON_MTD
2012-01-30 17:11:44 +04:00
# define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__)
2009-02-12 13:40:10 +03:00
/*
* read data from an romfs image on an MTD device
*/
static int romfs_mtd_read ( struct super_block * sb , unsigned long pos ,
void * buf , size_t buflen )
{
size_t rlen ;
int ret ;
ret = ROMFS_MTD_READ ( sb , pos , buflen , & rlen , buf ) ;
return ( ret < 0 | | rlen ! = buflen ) ? - EIO : 0 ;
}
/*
* determine the length of a string in a romfs image on an MTD device
*/
static ssize_t romfs_mtd_strnlen ( struct super_block * sb ,
unsigned long pos , size_t maxlen )
{
ssize_t n = 0 ;
size_t segment ;
u_char buf [ 16 ] , * p ;
size_t len ;
int ret ;
/* scan the string up to 16 bytes at a time */
while ( maxlen > 0 ) {
segment = min_t ( size_t , maxlen , 16 ) ;
ret = ROMFS_MTD_READ ( sb , pos , segment , & len , buf ) ;
if ( ret < 0 )
return ret ;
p = memchr ( buf , 0 , len ) ;
if ( p )
return n + ( p - buf ) ;
maxlen - = len ;
pos + = len ;
n + = len ;
}
return n ;
}
/*
* compare a string to one in a romfs image on MTD
* - return 1 if matched , 0 if differ , - ve if error
*/
2009-04-23 19:41:13 +04:00
static int romfs_mtd_strcmp ( struct super_block * sb , unsigned long pos ,
const char * str , size_t size )
2009-02-12 13:40:10 +03:00
{
2009-04-23 19:41:13 +04:00
u_char buf [ 17 ] ;
2009-02-12 13:40:10 +03:00
size_t len , segment ;
int ret ;
2009-04-23 19:41:13 +04:00
/* scan the string up to 16 bytes at a time, and attempt to grab the
* trailing NUL whilst we ' re at it */
buf [ 0 ] = 0xff ;
2009-02-12 13:40:10 +03:00
while ( size > 0 ) {
2009-04-23 19:41:13 +04:00
segment = min_t ( size_t , size + 1 , 17 ) ;
2009-02-12 13:40:10 +03:00
ret = ROMFS_MTD_READ ( sb , pos , segment , & len , buf ) ;
if ( ret < 0 )
return ret ;
2009-04-23 19:41:13 +04:00
len - - ;
2009-02-12 13:40:10 +03:00
if ( memcmp ( buf , str , len ) ! = 0 )
return 0 ;
2009-04-23 19:41:13 +04:00
buf [ 0 ] = buf [ len ] ;
2009-02-12 13:40:10 +03:00
size - = len ;
pos + = len ;
str + = len ;
}
2009-04-23 19:41:13 +04:00
/* check the trailing NUL was */
if ( buf [ 0 ] )
return 0 ;
2009-02-12 13:40:10 +03:00
return 1 ;
}
# endif /* CONFIG_ROMFS_ON_MTD */
# ifdef CONFIG_ROMFS_ON_BLOCK
/*
* read data from an romfs image on a block device
*/
static int romfs_blk_read ( struct super_block * sb , unsigned long pos ,
void * buf , size_t buflen )
{
struct buffer_head * bh ;
unsigned long offset ;
size_t segment ;
/* copy the string up to blocksize bytes at a time */
while ( buflen > 0 ) {
offset = pos & ( ROMBSIZE - 1 ) ;
segment = min_t ( size_t , buflen , ROMBSIZE - offset ) ;
bh = sb_bread ( sb , pos > > ROMBSBITS ) ;
if ( ! bh )
return - EIO ;
memcpy ( buf , bh - > b_data + offset , segment ) ;
brelse ( bh ) ;
2009-04-23 19:41:18 +04:00
buf + = segment ;
2009-02-12 13:40:10 +03:00
buflen - = segment ;
pos + = segment ;
}
return 0 ;
}
/*
* determine the length of a string in romfs on a block device
*/
static ssize_t romfs_blk_strnlen ( struct super_block * sb ,
unsigned long pos , size_t limit )
{
struct buffer_head * bh ;
unsigned long offset ;
ssize_t n = 0 ;
size_t segment ;
u_char * buf , * p ;
/* scan the string up to blocksize bytes at a time */
while ( limit > 0 ) {
offset = pos & ( ROMBSIZE - 1 ) ;
segment = min_t ( size_t , limit , ROMBSIZE - offset ) ;
bh = sb_bread ( sb , pos > > ROMBSBITS ) ;
if ( ! bh )
return - EIO ;
buf = bh - > b_data + offset ;
p = memchr ( buf , 0 , segment ) ;
brelse ( bh ) ;
if ( p )
return n + ( p - buf ) ;
limit - = segment ;
pos + = segment ;
n + = segment ;
}
return n ;
}
/*
* compare a string to one in a romfs image on a block device
* - return 1 if matched , 0 if differ , - ve if error
*/
2009-04-23 19:41:13 +04:00
static int romfs_blk_strcmp ( struct super_block * sb , unsigned long pos ,
const char * str , size_t size )
2009-02-12 13:40:10 +03:00
{
struct buffer_head * bh ;
unsigned long offset ;
size_t segment ;
2009-04-23 19:41:13 +04:00
bool matched , terminated = false ;
2009-02-12 13:40:10 +03:00
2009-04-23 19:41:13 +04:00
/* compare string up to a block at a time */
2009-02-12 13:40:10 +03:00
while ( size > 0 ) {
offset = pos & ( ROMBSIZE - 1 ) ;
segment = min_t ( size_t , size , ROMBSIZE - offset ) ;
bh = sb_bread ( sb , pos > > ROMBSBITS ) ;
if ( ! bh )
return - EIO ;
2009-04-23 19:41:13 +04:00
matched = ( memcmp ( bh - > b_data + offset , str , segment ) = = 0 ) ;
2009-02-12 13:40:10 +03:00
size - = segment ;
pos + = segment ;
str + = segment ;
2009-04-23 19:41:13 +04:00
if ( matched & & size = = 0 & & offset + segment < ROMBSIZE ) {
if ( ! bh - > b_data [ offset + segment ] )
terminated = true ;
else
matched = false ;
}
brelse ( bh ) ;
if ( ! matched )
return 0 ;
}
if ( ! terminated ) {
/* the terminating NUL must be on the first byte of the next
* block */
BUG_ON ( ( pos & ( ROMBSIZE - 1 ) ) ! = 0 ) ;
bh = sb_bread ( sb , pos > > ROMBSBITS ) ;
if ( ! bh )
return - EIO ;
matched = ! bh - > b_data [ 0 ] ;
brelse ( bh ) ;
if ( ! matched )
return 0 ;
2009-02-12 13:40:10 +03:00
}
return 1 ;
}
# endif /* CONFIG_ROMFS_ON_BLOCK */
/*
* read data from the romfs image
*/
int romfs_dev_read ( struct super_block * sb , unsigned long pos ,
void * buf , size_t buflen )
{
size_t limit ;
limit = romfs_maxsize ( sb ) ;
if ( pos > = limit )
return - EIO ;
if ( buflen > limit - pos )
buflen = limit - pos ;
# ifdef CONFIG_ROMFS_ON_MTD
if ( sb - > s_mtd )
return romfs_mtd_read ( sb , pos , buf , buflen ) ;
# endif
# ifdef CONFIG_ROMFS_ON_BLOCK
if ( sb - > s_bdev )
return romfs_blk_read ( sb , pos , buf , buflen ) ;
# endif
return - EIO ;
}
/*
* determine the length of a string in romfs
*/
ssize_t romfs_dev_strnlen ( struct super_block * sb ,
unsigned long pos , size_t maxlen )
{
size_t limit ;
limit = romfs_maxsize ( sb ) ;
if ( pos > = limit )
return - EIO ;
if ( maxlen > limit - pos )
maxlen = limit - pos ;
# ifdef CONFIG_ROMFS_ON_MTD
if ( sb - > s_mtd )
2009-10-06 12:55:26 +04:00
return romfs_mtd_strnlen ( sb , pos , maxlen ) ;
2009-02-12 13:40:10 +03:00
# endif
# ifdef CONFIG_ROMFS_ON_BLOCK
if ( sb - > s_bdev )
2009-10-06 12:55:26 +04:00
return romfs_blk_strnlen ( sb , pos , maxlen ) ;
2009-02-12 13:40:10 +03:00
# endif
return - EIO ;
}
/*
* compare a string to one in romfs
2009-04-23 19:41:13 +04:00
* - the string to be compared to , str , may not be NUL - terminated ; instead the
* string is of the specified size
2009-02-12 13:40:10 +03:00
* - return 1 if matched , 0 if differ , - ve if error
*/
2009-04-23 19:41:13 +04:00
int romfs_dev_strcmp ( struct super_block * sb , unsigned long pos ,
const char * str , size_t size )
2009-02-12 13:40:10 +03:00
{
size_t limit ;
limit = romfs_maxsize ( sb ) ;
if ( pos > = limit )
return - EIO ;
if ( size > ROMFS_MAXFN )
return - ENAMETOOLONG ;
2009-04-23 19:41:13 +04:00
if ( size + 1 > limit - pos )
2009-02-12 13:40:10 +03:00
return - EIO ;
# ifdef CONFIG_ROMFS_ON_MTD
if ( sb - > s_mtd )
2009-04-23 19:41:13 +04:00
return romfs_mtd_strcmp ( sb , pos , str , size ) ;
2009-02-12 13:40:10 +03:00
# endif
# ifdef CONFIG_ROMFS_ON_BLOCK
if ( sb - > s_bdev )
2009-04-23 19:41:13 +04:00
return romfs_blk_strcmp ( sb , pos , str , size ) ;
2009-02-12 13:40:10 +03:00
# endif
return - EIO ;
}