2001-10-03 16:41:29 +04:00
/*
2002-07-22 12:10:54 +04:00
* Copyright ( C ) 2002 Sistina Software
2001-10-03 16:41:29 +04:00
*
2001-10-31 15:47:01 +03:00
* This file is released under the LGPL .
2001-10-03 16:41:29 +04:00
*/
2001-10-04 14:13:07 +04:00
# include "device.h"
# include "lvm-types.h"
# include "log.h"
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
2002-07-22 12:10:54 +04:00
# include <stdlib.h>
# include <fcntl.h>
2001-10-04 14:13:07 +04:00
# include <sys/ioctl.h>
2002-07-22 12:10:54 +04:00
# include <linux/fs.h> /* UGH!!! for BLKSSZGET */
/* Buffer for O_DIRECT, allocated as
twice _SC_PAGE_SIZE so we can make sure it is
properly aligned .
It is assumed that _SC_PAGE_SIZE is a power of 2
but I think that ' s fairly safe : )
*/
static char * bigbuf ;
static char * aligned_buf ;
static long page_size ;
2001-11-13 21:52:52 +03:00
2002-04-24 22:20:51 +04:00
int dev_get_size ( struct device * dev , uint64_t * size )
2001-10-03 16:41:29 +04:00
{
int fd ;
long s ;
2001-10-25 18:04:18 +04:00
const char * name = dev_name ( dev ) ;
2001-10-03 16:41:29 +04:00
2001-10-25 18:04:18 +04:00
log_very_verbose ( " Getting size of %s " , name ) ;
if ( ( fd = open ( name , O_RDONLY ) ) < 0 ) {
log_sys_error ( " open " , name ) ;
2001-10-03 16:41:29 +04:00
return 0 ;
}
/* FIXME: add 64 bit ioctl */
if ( ioctl ( fd , BLKGETSIZE , & s ) < 0 ) {
2001-10-25 18:04:18 +04:00
log_sys_error ( " ioctl BLKGETSIZE " , name ) ;
2001-10-03 16:41:29 +04:00
close ( fd ) ;
return 0 ;
}
close ( fd ) ;
* size = ( uint64_t ) s ;
return 1 ;
}
2002-04-24 22:20:51 +04:00
int dev_get_sectsize ( struct device * dev , uint32_t * size )
2001-12-11 13:18:49 +03:00
{
2002-07-22 12:10:54 +04:00
int fd ;
int s ;
const char * name = dev_name ( dev ) ;
log_very_verbose ( " Getting size of %s " , name ) ;
if ( ( fd = open ( name , O_RDONLY ) ) < 0 ) {
log_sys_error ( " open " , name ) ;
return 0 ;
}
if ( ioctl ( fd , BLKSSZGET , & s ) < 0 ) {
log_sys_error ( " ioctl BLKSSZGET " , name ) ;
close ( fd ) ;
return 0 ;
}
close ( fd ) ;
* size = ( uint32_t ) s ;
return 1 ;
2002-06-25 18:02:28 +04:00
}
2001-11-14 13:01:52 +03:00
int dev_open ( struct device * dev , int flags )
{
2002-01-23 21:55:01 +03:00
struct stat buf ;
2002-01-25 02:16:19 +03:00
const char * name = dev_name_confirmed ( dev ) ;
2002-07-22 12:10:54 +04:00
/* First time through - allocate & align the buffer */
if ( ! page_size ) {
page_size = sysconf ( _SC_PAGESIZE ) ;
bigbuf = malloc ( page_size * 2 ) ;
if ( ! bigbuf ) {
stack ;
return 0 ;
}
aligned_buf = ( char * ) ( ( ( long ) bigbuf + page_size ) & ~ ( page_size - 1 ) ) ;
}
2002-01-25 02:16:19 +03:00
if ( ! name ) {
stack ;
return 0 ;
}
2001-11-14 13:01:52 +03:00
if ( dev - > fd > = 0 ) {
2001-11-14 16:52:38 +03:00
log_error ( " Device '%s' has already been opened " , name ) ;
2001-11-14 13:01:52 +03:00
return 0 ;
}
2002-01-23 21:55:01 +03:00
if ( ( stat ( name , & buf ) < 0 ) | | ( buf . st_rdev ! = dev - > dev ) ) {
log_error ( " %s: stat failed: Has device name changed? " , name ) ;
return 0 ;
}
2002-07-22 12:10:54 +04:00
/* If write was requested then add read so we can pre-fill
buffers for aligned writes */
if ( flags & O_WRONLY )
flags = ( flags & ~ O_WRONLY ) | O_RDWR ;
if ( ( dev - > fd = open ( name , flags | O_DIRECT ) ) < 0 ) {
2001-11-14 16:52:38 +03:00
log_sys_error ( " open " , name ) ;
2001-11-14 13:01:52 +03:00
return 0 ;
}
2002-01-23 21:55:01 +03:00
if ( ( fstat ( dev - > fd , & buf ) < 0 ) | | ( buf . st_rdev ! = dev - > dev ) ) {
log_error ( " %s: fstat failed: Has device name changed? " , name ) ;
2002-01-24 16:31:18 +03:00
dev_close ( dev ) ;
2002-01-23 21:55:01 +03:00
return 0 ;
}
2002-07-22 12:10:54 +04:00
2002-04-24 22:20:51 +04:00
dev - > flags = 0 ;
2001-11-14 13:01:52 +03:00
return 1 ;
}
int dev_close ( struct device * dev )
{
if ( dev - > fd < 0 ) {
2001-11-14 16:52:38 +03:00
log_error ( " Attempt to close device '%s' "
" which is not open. " , dev_name ( dev ) ) ;
2001-11-14 13:01:52 +03:00
return 0 ;
}
2001-11-14 16:52:38 +03:00
if ( close ( dev - > fd ) )
log_sys_error ( " close " , dev_name ( dev ) ) ;
2001-11-14 13:01:52 +03:00
dev - > fd = - 1 ;
return 1 ;
}
2001-10-10 17:03:10 +04:00
/*
* FIXME : factor common code out .
*/
2001-10-08 20:08:16 +04:00
int _read ( int fd , void * buf , size_t count )
{
size_t n = 0 ;
int tot = 0 ;
while ( tot < count ) {
2001-10-23 15:16:30 +04:00
do
n = read ( fd , buf , count - tot ) ;
while ( ( n < 0 ) & & ( ( errno = = EINTR ) | | ( errno = = EAGAIN ) ) ) ;
2001-10-08 20:08:16 +04:00
if ( n < = 0 )
return tot ? tot : n ;
tot + = n ;
buf + = n ;
}
return tot ;
}
2002-04-24 22:20:51 +04:00
int64_t dev_read ( struct device * dev , uint64_t offset ,
2001-10-03 16:41:29 +04:00
int64_t len , void * buffer )
{
2001-10-25 18:04:18 +04:00
const char * name = dev_name ( dev ) ;
2001-11-14 13:01:52 +03:00
int fd = dev - > fd ;
2002-07-22 12:10:54 +04:00
int64_t newlen ;
int64_t newoffset ;
int64_t offsetdiff ;
int ret ;
2001-10-08 20:08:16 +04:00
if ( fd < 0 ) {
2001-11-14 13:01:52 +03:00
log_err ( " Attempt to read an unopened device (%s). " , name ) ;
2001-10-08 20:08:16 +04:00
return 0 ;
}
2002-07-22 12:10:54 +04:00
/* Adjust offset to page size */
newoffset = offset & ~ ( page_size - 1 ) ;
offsetdiff = offset - newoffset ;
if ( lseek ( fd , newoffset , SEEK_SET ) < 0 ) {
2001-10-25 18:04:18 +04:00
log_sys_error ( " lseek " , name ) ;
2001-10-08 20:08:16 +04:00
return 0 ;
}
2002-07-22 12:10:54 +04:00
/* Copy to aligned buffer & pad to page size */
newlen = ( len + offsetdiff ) + page_size - ( ( len + offsetdiff ) % page_size ) ;
ret = _read ( fd , aligned_buf , newlen ) ;
if ( ret < len + offsetdiff )
{
return ret ;
}
/* Copy back to users buffer */
memcpy ( buffer , aligned_buf + offsetdiff , len ) ;
return len ;
2001-10-03 16:41:29 +04:00
}
2001-10-10 17:03:10 +04:00
int _write ( int fd , const void * buf , size_t count )
{
size_t n = 0 ;
int tot = 0 ;
2001-11-28 21:03:11 +03:00
/* Skip all writes */
if ( test_mode ( ) )
return count ;
2001-10-10 17:03:10 +04:00
while ( tot < count ) {
2001-10-23 15:16:30 +04:00
do
n = write ( fd , buf , count - tot ) ;
while ( ( n < 0 ) & & ( ( errno = = EINTR ) | | ( errno = = EAGAIN ) ) ) ;
2001-10-10 17:03:10 +04:00
if ( n < = 0 )
return tot ? tot : n ;
tot + = n ;
buf + = n ;
}
return tot ;
}
2002-04-24 22:20:51 +04:00
int64_t dev_write ( struct device * dev , uint64_t offset ,
2001-10-03 16:41:29 +04:00
int64_t len , void * buffer )
{
2001-10-25 18:04:18 +04:00
const char * name = dev_name ( dev ) ;
2001-11-14 13:01:52 +03:00
int fd = dev - > fd ;
2002-07-22 12:10:54 +04:00
int64_t newlen ;
int64_t newoffset ;
int64_t offsetdiff ;
int ret ;
2001-10-10 17:03:10 +04:00
if ( fd < 0 ) {
2001-11-14 16:52:38 +03:00
log_error ( " Attempt to write to unopened device %s " , name ) ;
2001-10-10 17:03:10 +04:00
return 0 ;
}
2002-07-22 12:10:54 +04:00
/* Adjust offset to page size */
newoffset = offset & ~ ( page_size - 1 ) ;
offsetdiff = offset - newoffset ;
if ( lseek ( fd , newoffset , SEEK_SET ) < 0 ) {
2001-10-25 18:04:18 +04:00
log_sys_error ( " lseek " , name ) ;
2001-10-10 17:03:10 +04:00
return 0 ;
}
2002-04-24 22:20:51 +04:00
dev - > flags | = DEV_ACCESSED_W ;
2002-07-22 12:10:54 +04:00
/* Pad to page size */
newlen = ( len + offsetdiff ) + page_size - ( ( len + offsetdiff ) % page_size ) ;
/* We read the page in and just overwrite the bits
requested . Needs to fudge O_RDWR in dev_open for this to work . */
if ( _read ( fd , aligned_buf , newlen ) < = 0 ) {
log_sys_error ( " pre-read " , name ) ;
return 0 ;
}
if ( lseek ( fd , newoffset , SEEK_SET ) < 0 ) {
log_sys_error ( " re-lseek " , name ) ;
return 0 ;
}
memcpy ( aligned_buf + offsetdiff , buffer , len ) ;
ret = _write ( fd , aligned_buf , newlen ) ;
if ( ret = = newlen )
return len ;
else
return ret ;
2001-10-03 16:41:29 +04:00
}
2001-11-13 21:52:52 +03:00
int dev_zero ( struct device * dev , uint64_t offset , int64_t len )
{
int64_t r , s ;
const char * name = dev_name ( dev ) ;
2001-11-14 13:01:52 +03:00
int fd = dev - > fd ;
2001-11-13 21:52:52 +03:00
if ( fd < 0 ) {
2001-12-11 13:18:49 +03:00
log_error ( " Attempt to zero part of an unopened device %s " ,
2001-11-14 16:52:38 +03:00
name ) ;
2001-11-13 21:52:52 +03:00
return 0 ;
}
if ( lseek ( fd , offset , SEEK_SET ) < 0 ) {
log_sys_error ( " lseek " , name ) ;
return 0 ;
}
2002-07-22 12:10:54 +04:00
memset ( aligned_buf , 0 , page_size ) ;
2001-11-13 21:52:52 +03:00
while ( 1 ) {
2002-07-22 12:10:54 +04:00
s = len > page_size ? page_size : len ;
r = _write ( fd , aligned_buf , s ) ;
2001-11-13 21:52:52 +03:00
if ( r < = 0 )
break ;
len - = r ;
if ( ! len ) {
r = 1 ;
break ;
}
}
2002-04-24 22:20:51 +04:00
dev - > flags | = DEV_ACCESSED_W ;
2001-11-19 18:20:50 +03:00
/* FIXME: Always display error */
2001-11-14 13:01:52 +03:00
return ( len = = 0 ) ;
2001-11-13 21:52:52 +03:00
}