2005-04-17 02:20:36 +04:00
/*
* linux / fs / seq_file . c
*
* helper functions for making synthetic files from sequences of records .
* initial implementation - - AV , Oct 2001.
*/
# include <linux/fs.h>
# include <linux/module.h>
# include <linux/seq_file.h>
# include <linux/slab.h>
# include <asm/uaccess.h>
# include <asm/page.h>
/**
* seq_open - initialize sequential file
* @ file : file we initialize
* @ op : method table describing the sequence
*
* seq_open ( ) sets @ file , associating it with a sequence described
* by @ op . @ op - > start ( ) sets the iterator up and returns the first
* element of sequence . @ op - > stop ( ) shuts it down . @ op - > next ( )
* returns the next element of sequence . @ op - > show ( ) prints element
* into the buffer . In case of error - > start ( ) and - > next ( ) return
* ERR_PTR ( error ) . In the end of sequence they return % NULL . - > show ( )
* returns 0 in case of success and negative number in case of error .
2008-03-28 07:46:41 +03:00
* Returning SEQ_SKIP means " discard this element and move on " .
2005-04-17 02:20:36 +04:00
*/
2006-12-07 07:40:36 +03:00
int seq_open ( struct file * file , const struct seq_operations * op )
2005-04-17 02:20:36 +04:00
{
2005-11-08 01:15:34 +03:00
struct seq_file * p = file - > private_data ;
if ( ! p ) {
p = kmalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
if ( ! p )
return - ENOMEM ;
file - > private_data = p ;
}
2005-04-17 02:20:36 +04:00
memset ( p , 0 , sizeof ( * p ) ) ;
2006-03-23 14:00:37 +03:00
mutex_init ( & p - > lock ) ;
2005-04-17 02:20:36 +04:00
p - > op = op ;
/*
* Wrappers around seq_open ( e . g . swaps_open ) need to be
* aware of this . If they set f_version themselves , they
* should call seq_open first and then set f_version .
*/
file - > f_version = 0 ;
2009-02-19 01:48:16 +03:00
/*
* seq_files support lseek ( ) and pread ( ) . They do not implement
* write ( ) at all , but we clear FMODE_PWRITE here for historical
* reasons .
*
* If a client of seq_files a ) implements file . write ( ) and b ) wishes to
* support pwrite ( ) then that client will need to implement its own
* file . open ( ) which calls seq_open ( ) and then sets FMODE_PWRITE .
*/
file - > f_mode & = ~ FMODE_PWRITE ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
EXPORT_SYMBOL ( seq_open ) ;
2009-02-05 02:12:25 +03:00
static int traverse ( struct seq_file * m , loff_t offset )
{
loff_t pos = 0 , index ;
int error = 0 ;
void * p ;
m - > version = 0 ;
index = 0 ;
m - > count = m - > from = 0 ;
if ( ! offset ) {
m - > index = index ;
return 0 ;
}
if ( ! m - > buf ) {
m - > buf = kmalloc ( m - > size = PAGE_SIZE , GFP_KERNEL ) ;
if ( ! m - > buf )
return - ENOMEM ;
}
p = m - > op - > start ( m , & index ) ;
while ( p ) {
error = PTR_ERR ( p ) ;
if ( IS_ERR ( p ) )
break ;
error = m - > op - > show ( m , p ) ;
if ( error < 0 )
break ;
if ( unlikely ( error ) ) {
error = 0 ;
m - > count = 0 ;
}
if ( m - > count = = m - > size )
goto Eoverflow ;
if ( pos + m - > count > offset ) {
m - > from = offset - pos ;
m - > count - = m - > from ;
m - > index = index ;
break ;
}
pos + = m - > count ;
m - > count = 0 ;
if ( pos = = offset ) {
index + + ;
m - > index = index ;
break ;
}
p = m - > op - > next ( m , p , & index ) ;
}
m - > op - > stop ( m , p ) ;
2009-02-06 00:30:05 +03:00
m - > index = index ;
2009-02-05 02:12:25 +03:00
return error ;
Eoverflow :
m - > op - > stop ( m , p ) ;
kfree ( m - > buf ) ;
m - > buf = kmalloc ( m - > size < < = 1 , GFP_KERNEL ) ;
return ! m - > buf ? - ENOMEM : - EAGAIN ;
}
2005-04-17 02:20:36 +04:00
/**
* seq_read - - > read ( ) method for sequential files .
2005-05-01 19:59:26 +04:00
* @ file : the file to read from
* @ buf : the buffer to read to
* @ size : the maximum number of bytes to read
* @ ppos : the current position in the file
2005-04-17 02:20:36 +04:00
*
* Ready - made - > f_op - > read ( )
*/
ssize_t seq_read ( struct file * file , char __user * buf , size_t size , loff_t * ppos )
{
struct seq_file * m = ( struct seq_file * ) file - > private_data ;
size_t copied = 0 ;
loff_t pos ;
size_t n ;
void * p ;
int err = 0 ;
2006-03-23 14:00:37 +03:00
mutex_lock ( & m - > lock ) ;
2009-02-19 01:48:16 +03:00
/* Don't assume *ppos is where we left it */
if ( unlikely ( * ppos ! = m - > read_pos ) ) {
m - > read_pos = * ppos ;
while ( ( err = traverse ( m , * ppos ) ) = = - EAGAIN )
;
if ( err ) {
/* With prejudice... */
m - > read_pos = 0 ;
m - > version = 0 ;
m - > index = 0 ;
m - > count = 0 ;
goto Done ;
}
}
2005-04-17 02:20:36 +04:00
/*
* seq_file - > op - > . . m_start / m_stop / m_next may do special actions
* or optimisations based on the file - > f_version , so we want to
* pass the file - > f_version to those methods .
*
* seq_file - > version is just copy of f_version , and seq_file
* methods can treat it simply as file version .
* It is copied in first and copied out after all operations .
* It is convenient to have it as part of structure to avoid the
* need of passing another argument to all the seq_file methods .
*/
m - > version = file - > f_version ;
/* grab buffer if we didn't have one */
if ( ! m - > buf ) {
m - > buf = kmalloc ( m - > size = PAGE_SIZE , GFP_KERNEL ) ;
if ( ! m - > buf )
goto Enomem ;
}
/* if not empty - flush it first */
if ( m - > count ) {
n = min ( m - > count , size ) ;
err = copy_to_user ( buf , m - > buf + m - > from , n ) ;
if ( err )
goto Efault ;
m - > count - = n ;
m - > from + = n ;
size - = n ;
buf + = n ;
copied + = n ;
if ( ! m - > count )
m - > index + + ;
if ( ! size )
goto Done ;
}
/* we need at least one record in buffer */
2008-08-24 15:45:33 +04:00
pos = m - > index ;
p = m - > op - > start ( m , & pos ) ;
2005-04-17 02:20:36 +04:00
while ( 1 ) {
err = PTR_ERR ( p ) ;
if ( ! p | | IS_ERR ( p ) )
break ;
err = m - > op - > show ( m , p ) ;
2008-03-28 07:46:41 +03:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
break ;
2008-03-28 07:46:41 +03:00
if ( unlikely ( err ) )
m - > count = 0 ;
2008-08-24 15:45:33 +04:00
if ( unlikely ( ! m - > count ) ) {
p = m - > op - > next ( m , p , & pos ) ;
m - > index = pos ;
continue ;
}
2005-04-17 02:20:36 +04:00
if ( m - > count < m - > size )
goto Fill ;
m - > op - > stop ( m , p ) ;
kfree ( m - > buf ) ;
m - > buf = kmalloc ( m - > size < < = 1 , GFP_KERNEL ) ;
if ( ! m - > buf )
goto Enomem ;
m - > count = 0 ;
m - > version = 0 ;
2008-08-24 15:45:33 +04:00
pos = m - > index ;
p = m - > op - > start ( m , & pos ) ;
2005-04-17 02:20:36 +04:00
}
m - > op - > stop ( m , p ) ;
m - > count = 0 ;
goto Done ;
Fill :
/* they want more? let's try to get some more */
while ( m - > count < size ) {
size_t offs = m - > count ;
loff_t next = pos ;
p = m - > op - > next ( m , p , & next ) ;
if ( ! p | | IS_ERR ( p ) ) {
err = PTR_ERR ( p ) ;
break ;
}
err = m - > op - > show ( m , p ) ;
2008-03-28 07:46:41 +03:00
if ( m - > count = = m - > size | | err ) {
2005-04-17 02:20:36 +04:00
m - > count = offs ;
2008-03-28 07:46:41 +03:00
if ( likely ( err < = 0 ) )
break ;
2005-04-17 02:20:36 +04:00
}
pos = next ;
}
m - > op - > stop ( m , p ) ;
n = min ( m - > count , size ) ;
err = copy_to_user ( buf , m - > buf , n ) ;
if ( err )
goto Efault ;
copied + = n ;
m - > count - = n ;
if ( m - > count )
m - > from = n ;
else
pos + + ;
m - > index = pos ;
Done :
if ( ! copied )
copied = err ;
2009-02-19 01:48:16 +03:00
else {
2005-04-17 02:20:36 +04:00
* ppos + = copied ;
2009-02-19 01:48:16 +03:00
m - > read_pos + = copied ;
}
2005-04-17 02:20:36 +04:00
file - > f_version = m - > version ;
2006-03-23 14:00:37 +03:00
mutex_unlock ( & m - > lock ) ;
2005-04-17 02:20:36 +04:00
return copied ;
Enomem :
err = - ENOMEM ;
goto Done ;
Efault :
err = - EFAULT ;
goto Done ;
}
EXPORT_SYMBOL ( seq_read ) ;
/**
* seq_lseek - - > llseek ( ) method for sequential files .
2005-05-01 19:59:26 +04:00
* @ file : the file in question
* @ offset : new position
* @ origin : 0 for absolute , 1 for relative position
2005-04-17 02:20:36 +04:00
*
* Ready - made - > f_op - > llseek ( )
*/
loff_t seq_lseek ( struct file * file , loff_t offset , int origin )
{
struct seq_file * m = ( struct seq_file * ) file - > private_data ;
2008-04-22 17:09:22 +04:00
loff_t retval = - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-03-23 14:00:37 +03:00
mutex_lock ( & m - > lock ) ;
2005-04-17 02:20:36 +04:00
m - > version = file - > f_version ;
switch ( origin ) {
case 1 :
offset + = file - > f_pos ;
case 0 :
if ( offset < 0 )
break ;
retval = offset ;
2009-02-19 01:48:16 +03:00
if ( offset ! = m - > read_pos ) {
2005-04-17 02:20:36 +04:00
while ( ( retval = traverse ( m , offset ) ) = = - EAGAIN )
;
if ( retval ) {
/* with extreme prejudice... */
file - > f_pos = 0 ;
2009-02-19 01:48:16 +03:00
m - > read_pos = 0 ;
2005-04-17 02:20:36 +04:00
m - > version = 0 ;
m - > index = 0 ;
m - > count = 0 ;
} else {
2009-02-19 01:48:16 +03:00
m - > read_pos = offset ;
2005-04-17 02:20:36 +04:00
retval = file - > f_pos = offset ;
}
}
}
file - > f_version = m - > version ;
2007-07-16 10:40:22 +04:00
mutex_unlock ( & m - > lock ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
EXPORT_SYMBOL ( seq_lseek ) ;
/**
* seq_release - free the structures associated with sequential file .
* @ file : file in question
2006-12-08 13:36:35 +03:00
* @ inode : file - > f_path . dentry - > d_inode
2005-04-17 02:20:36 +04:00
*
* Frees the structures associated with sequential file ; can be used
* as - > f_op - > release ( ) if you don ' t have private data to destroy .
*/
int seq_release ( struct inode * inode , struct file * file )
{
struct seq_file * m = ( struct seq_file * ) file - > private_data ;
kfree ( m - > buf ) ;
kfree ( m ) ;
return 0 ;
}
EXPORT_SYMBOL ( seq_release ) ;
/**
* seq_escape - print string into buffer , escaping some characters
* @ m : target buffer
* @ s : string
* @ esc : set of characters that need escaping
*
* Puts string into buffer , replacing each occurrence of character from
* @ esc with usual octal escape . Returns 0 in case of success , - 1 - in
* case of overflow .
*/
int seq_escape ( struct seq_file * m , const char * s , const char * esc )
{
char * end = m - > buf + m - > size ;
char * p ;
char c ;
for ( p = m - > buf + m - > count ; ( c = * s ) ! = ' \0 ' & & p < end ; s + + ) {
if ( ! strchr ( esc , c ) ) {
* p + + = c ;
continue ;
}
if ( p + 3 < end ) {
* p + + = ' \\ ' ;
* p + + = ' 0 ' + ( ( c & 0300 ) > > 6 ) ;
* p + + = ' 0 ' + ( ( c & 070 ) > > 3 ) ;
* p + + = ' 0 ' + ( c & 07 ) ;
continue ;
}
m - > count = m - > size ;
return - 1 ;
}
m - > count = p - m - > buf ;
return 0 ;
}
EXPORT_SYMBOL ( seq_escape ) ;
int seq_printf ( struct seq_file * m , const char * f , . . . )
{
va_list args ;
int len ;
if ( m - > count < m - > size ) {
va_start ( args , f ) ;
len = vsnprintf ( m - > buf + m - > count , m - > size - m - > count , f , args ) ;
va_end ( args ) ;
if ( m - > count + len < m - > size ) {
m - > count + = len ;
return 0 ;
}
}
m - > count = m - > size ;
return - 1 ;
}
EXPORT_SYMBOL ( seq_printf ) ;
2008-11-22 14:28:48 +03:00
/**
2008-11-24 00:24:53 +03:00
* mangle_path - mangle and copy path to buffer beginning
* @ s : buffer start
* @ p : beginning of path in above buffer
* @ esc : set of characters that need escaping
2008-11-22 14:28:48 +03:00
*
* Copy the path from @ p to @ s , replacing each occurrence of character from
* @ esc with usual octal escape .
* Returns pointer past last written character in @ s , or NULL in case of
* failure .
*/
char * mangle_path ( char * s , char * p , char * esc )
2008-03-27 15:06:20 +03:00
{
while ( s < = p ) {
char c = * p + + ;
if ( ! c ) {
return s ;
} else if ( ! strchr ( esc , c ) ) {
* s + + = c ;
} else if ( s + 4 > p ) {
break ;
} else {
* s + + = ' \\ ' ;
* s + + = ' 0 ' + ( ( c & 0300 ) > > 6 ) ;
* s + + = ' 0 ' + ( ( c & 070 ) > > 3 ) ;
* s + + = ' 0 ' + ( c & 07 ) ;
}
}
return NULL ;
}
2008-11-28 20:03:22 +03:00
EXPORT_SYMBOL ( mangle_path ) ;
2008-03-27 15:06:20 +03:00
2008-12-02 01:35:00 +03:00
/**
* seq_path - seq_file interface to print a pathname
* @ m : the seq_file handle
* @ path : the struct path to print
* @ esc : set of characters to escape in the output
*
* return the absolute path of ' path ' , as represented by the
* dentry / mnt pair in the path parameter .
2008-03-27 15:06:20 +03:00
*/
2008-02-15 06:38:43 +03:00
int seq_path ( struct seq_file * m , struct path * path , char * esc )
2005-04-17 02:20:36 +04:00
{
if ( m - > count < m - > size ) {
char * s = m - > buf + m - > count ;
2008-02-15 06:38:44 +03:00
char * p = d_path ( path , s , m - > size - m - > count ) ;
2005-04-17 02:20:36 +04:00
if ( ! IS_ERR ( p ) ) {
2008-03-27 15:06:20 +03:00
s = mangle_path ( s , p , esc ) ;
if ( s ) {
p = m - > buf + m - > count ;
m - > count = s - m - > buf ;
return s - p ;
2005-04-17 02:20:36 +04:00
}
}
}
m - > count = m - > size ;
return - 1 ;
}
EXPORT_SYMBOL ( seq_path ) ;
2008-03-27 15:06:21 +03:00
/*
* Same as seq_path , but relative to supplied root .
*
* root may be changed , see __d_path ( ) .
*/
int seq_path_root ( struct seq_file * m , struct path * path , struct path * root ,
char * esc )
{
int err = - ENAMETOOLONG ;
if ( m - > count < m - > size ) {
char * s = m - > buf + m - > count ;
char * p ;
spin_lock ( & dcache_lock ) ;
p = __d_path ( path , root , s , m - > size - m - > count ) ;
spin_unlock ( & dcache_lock ) ;
err = PTR_ERR ( p ) ;
if ( ! IS_ERR ( p ) ) {
s = mangle_path ( s , p , esc ) ;
if ( s ) {
p = m - > buf + m - > count ;
m - > count = s - m - > buf ;
return 0 ;
}
}
}
m - > count = m - > size ;
return err ;
}
2008-03-27 15:06:20 +03:00
/*
* returns the path of the ' dentry ' from the root of its filesystem .
*/
int seq_dentry ( struct seq_file * m , struct dentry * dentry , char * esc )
{
if ( m - > count < m - > size ) {
char * s = m - > buf + m - > count ;
char * p = dentry_path ( dentry , s , m - > size - m - > count ) ;
if ( ! IS_ERR ( p ) ) {
s = mangle_path ( s , p , esc ) ;
if ( s ) {
p = m - > buf + m - > count ;
m - > count = s - m - > buf ;
return s - p ;
}
}
}
m - > count = m - > size ;
return - 1 ;
}
2008-12-30 01:35:14 +03:00
int seq_bitmap ( struct seq_file * m , const unsigned long * bits ,
unsigned int nr_bits )
2008-08-13 02:09:02 +04:00
{
2008-10-19 07:28:18 +04:00
if ( m - > count < m - > size ) {
int len = bitmap_scnprintf ( m - > buf + m - > count ,
m - > size - m - > count , bits , nr_bits ) ;
if ( m - > count + len < m - > size ) {
m - > count + = len ;
return 0 ;
}
2008-08-13 02:09:02 +04:00
}
m - > count = m - > size ;
return - 1 ;
}
2008-10-19 07:28:18 +04:00
EXPORT_SYMBOL ( seq_bitmap ) ;
2008-08-13 02:09:02 +04:00
2009-03-31 08:05:11 +04:00
int seq_bitmap_list ( struct seq_file * m , const unsigned long * bits ,
2008-10-19 07:28:19 +04:00
unsigned int nr_bits )
{
if ( m - > count < m - > size ) {
int len = bitmap_scnlistprintf ( m - > buf + m - > count ,
m - > size - m - > count , bits , nr_bits ) ;
if ( m - > count + len < m - > size ) {
m - > count + = len ;
return 0 ;
}
}
m - > count = m - > size ;
return - 1 ;
}
EXPORT_SYMBOL ( seq_bitmap_list ) ;
2005-04-17 02:20:36 +04:00
static void * single_start ( struct seq_file * p , loff_t * pos )
{
return NULL + ( * pos = = 0 ) ;
}
static void * single_next ( struct seq_file * p , void * v , loff_t * pos )
{
+ + * pos ;
return NULL ;
}
static void single_stop ( struct seq_file * p , void * v )
{
}
int single_open ( struct file * file , int ( * show ) ( struct seq_file * , void * ) ,
void * data )
{
struct seq_operations * op = kmalloc ( sizeof ( * op ) , GFP_KERNEL ) ;
int res = - ENOMEM ;
if ( op ) {
op - > start = single_start ;
op - > next = single_next ;
op - > stop = single_stop ;
op - > show = show ;
res = seq_open ( file , op ) ;
if ( ! res )
( ( struct seq_file * ) file - > private_data ) - > private = data ;
else
kfree ( op ) ;
}
return res ;
}
EXPORT_SYMBOL ( single_open ) ;
int single_release ( struct inode * inode , struct file * file )
{
2006-12-07 07:40:36 +03:00
const struct seq_operations * op = ( ( struct seq_file * ) file - > private_data ) - > op ;
2005-04-17 02:20:36 +04:00
int res = seq_release ( inode , file ) ;
kfree ( op ) ;
return res ;
}
EXPORT_SYMBOL ( single_release ) ;
int seq_release_private ( struct inode * inode , struct file * file )
{
struct seq_file * seq = file - > private_data ;
kfree ( seq - > private ) ;
seq - > private = NULL ;
return seq_release ( inode , file ) ;
}
EXPORT_SYMBOL ( seq_release_private ) ;
2007-10-10 13:28:42 +04:00
void * __seq_open_private ( struct file * f , const struct seq_operations * ops ,
int psize )
{
int rc ;
void * private ;
struct seq_file * seq ;
private = kzalloc ( psize , GFP_KERNEL ) ;
if ( private = = NULL )
goto out ;
rc = seq_open ( f , ops ) ;
if ( rc < 0 )
goto out_free ;
seq = f - > private_data ;
seq - > private = private ;
return private ;
out_free :
kfree ( private ) ;
out :
return NULL ;
}
EXPORT_SYMBOL ( __seq_open_private ) ;
int seq_open_private ( struct file * filp , const struct seq_operations * ops ,
int psize )
{
return __seq_open_private ( filp , ops , psize ) ? 0 : - ENOMEM ;
}
EXPORT_SYMBOL ( seq_open_private ) ;
2005-04-17 02:20:36 +04:00
int seq_putc ( struct seq_file * m , char c )
{
if ( m - > count < m - > size ) {
m - > buf [ m - > count + + ] = c ;
return 0 ;
}
return - 1 ;
}
EXPORT_SYMBOL ( seq_putc ) ;
int seq_puts ( struct seq_file * m , const char * s )
{
int len = strlen ( s ) ;
if ( m - > count + len < m - > size ) {
memcpy ( m - > buf + m - > count , s , len ) ;
m - > count + = len ;
return 0 ;
}
m - > count = m - > size ;
return - 1 ;
}
EXPORT_SYMBOL ( seq_puts ) ;
2007-07-11 04:22:26 +04:00
2009-06-18 03:28:05 +04:00
/**
* seq_write - write arbitrary data to buffer
* @ seq : seq_file identifying the buffer to which data should be written
* @ data : data address
* @ len : number of bytes
*
* Return 0 on success , non - zero otherwise .
*/
int seq_write ( struct seq_file * seq , const void * data , size_t len )
{
if ( seq - > count + len < seq - > size ) {
memcpy ( seq - > buf + seq - > count , data , len ) ;
seq - > count + = len ;
return 0 ;
}
seq - > count = seq - > size ;
return - 1 ;
}
EXPORT_SYMBOL ( seq_write ) ;
2007-07-11 04:22:26 +04:00
struct list_head * seq_list_start ( struct list_head * head , loff_t pos )
{
struct list_head * lh ;
list_for_each ( lh , head )
if ( pos - - = = 0 )
return lh ;
return NULL ;
}
EXPORT_SYMBOL ( seq_list_start ) ;
struct list_head * seq_list_start_head ( struct list_head * head , loff_t pos )
{
if ( ! pos )
return head ;
return seq_list_start ( head , pos - 1 ) ;
}
EXPORT_SYMBOL ( seq_list_start_head ) ;
struct list_head * seq_list_next ( void * v , struct list_head * head , loff_t * ppos )
{
struct list_head * lh ;
lh = ( ( struct list_head * ) v ) - > next ;
+ + * ppos ;
return lh = = head ? NULL : lh ;
}
EXPORT_SYMBOL ( seq_list_next ) ;