2005-04-17 02:20:36 +04:00
/* user_fixup.c: Fix up user copy faults.
*
* Copyright ( C ) 2004 David S . Miller < davem @ redhat . com >
*/
# include <linux/compiler.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
2009-01-09 03:58:20 +03:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
/* Calculating the exact fault address when using
* block loads and stores can be very complicated .
2005-09-29 08:06:47 +04:00
*
2005-04-17 02:20:36 +04:00
* Instead of trying to be clever and handling all
* of the cases , just fix things up simply here .
*/
2005-09-29 08:06:47 +04:00
static unsigned long compute_size ( unsigned long start , unsigned long size , unsigned long * offset )
2005-04-17 02:20:36 +04:00
{
2005-09-29 08:06:47 +04:00
unsigned long fault_addr = current_thread_info ( ) - > fault_address ;
unsigned long end = start + size ;
2005-04-17 02:20:36 +04:00
2005-09-29 08:06:47 +04:00
if ( fault_addr < start | | fault_addr > = end ) {
* offset = 0 ;
} else {
2008-12-01 13:48:26 +03:00
* offset = fault_addr - start ;
2005-09-29 08:06:47 +04:00
size = end - fault_addr ;
2005-04-17 02:20:36 +04:00
}
2005-09-29 08:06:47 +04:00
return size ;
}
2005-04-17 02:20:36 +04:00
2005-09-29 08:06:47 +04:00
unsigned long copy_from_user_fixup ( void * to , const void __user * from , unsigned long size )
{
unsigned long offset ;
size = compute_size ( ( unsigned long ) from , size , & offset ) ;
if ( likely ( size ) )
memset ( to + offset , 0 , size ) ;
2005-04-17 02:20:36 +04:00
return size ;
}
2009-01-09 03:58:20 +03:00
EXPORT_SYMBOL ( copy_from_user_fixup ) ;
2005-04-17 02:20:36 +04:00
unsigned long copy_to_user_fixup ( void __user * to , const void * from , unsigned long size )
{
2005-09-29 08:06:47 +04:00
unsigned long offset ;
2005-04-17 02:20:36 +04:00
2005-09-29 08:06:47 +04:00
return compute_size ( ( unsigned long ) to , size , & offset ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-09 03:58:20 +03:00
EXPORT_SYMBOL ( copy_to_user_fixup ) ;
2005-04-17 02:20:36 +04:00
unsigned long copy_in_user_fixup ( void __user * to , void __user * from , unsigned long size )
{
2005-09-29 08:06:47 +04:00
unsigned long fault_addr = current_thread_info ( ) - > fault_address ;
unsigned long start = ( unsigned long ) to ;
unsigned long end = start + size ;
2005-04-17 02:20:36 +04:00
2005-09-29 08:06:47 +04:00
if ( fault_addr > = start & & fault_addr < end )
return end - fault_addr ;
2005-04-17 02:20:36 +04:00
2005-09-29 08:06:47 +04:00
start = ( unsigned long ) from ;
end = start + size ;
if ( fault_addr > = start & & fault_addr < end )
return end - fault_addr ;
2005-04-17 02:20:36 +04:00
return size ;
}
2009-01-09 03:58:20 +03:00
EXPORT_SYMBOL ( copy_in_user_fixup ) ;