2006-01-31 09:09:18 +03:00
/*
CIFSDD - dd for SMB .
IO routines , generic and specific .
Copyright ( C ) James Peach 2005 - 2006
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 .
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 , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
# include "system/filesys.h"
# include "libcli/raw/libcliraw.h"
# include "libcli/libcli.h"
# include "lib/cmdline/popt_common.h"
# include "cifsdd.h"
/* ------------------------------------------------------------------------- */
/* UNIX file descriptor IO. */
/* ------------------------------------------------------------------------- */
struct fd_handle
{
struct dd_iohandle h ;
int fd ;
} ;
# define IO_HANDLE_TO_FD(h) (((struct fd_handle *)(h))->fd)
static BOOL fd_seek_func ( void * handle , uint64_t offset )
{
ssize_t ret ;
ret = lseek ( IO_HANDLE_TO_FD ( handle ) , offset , SEEK_SET ) ;
if ( ret < 0 ) {
fprintf ( stderr , " %s: seek failed: %s \n " ,
PROGNAME , strerror ( errno ) ) ;
return ( False ) ;
}
return ( True ) ;
}
2006-02-01 09:05:08 +03:00
static BOOL fd_read_func ( void * handle ,
uint8_t * buf ,
uint64_t wanted ,
uint64_t * actual )
2006-01-31 09:09:18 +03:00
{
ssize_t ret ;
ret = read ( IO_HANDLE_TO_FD ( handle ) , buf , wanted ) ;
if ( ret < 0 ) {
fprintf ( stderr , " %s: %llu byte read failed: %s \n " ,
2006-02-01 09:05:08 +03:00
PROGNAME , ( unsigned long long ) wanted ,
strerror ( errno ) ) ;
2006-01-31 09:09:18 +03:00
return ( False ) ;
}
* actual = ( uint64_t ) ret ;
return ( True ) ;
}
2006-02-01 09:05:08 +03:00
static BOOL fd_write_func ( void * handle ,
uint8_t * buf ,
uint64_t wanted ,
uint64_t * actual )
2006-01-31 09:09:18 +03:00
{
ssize_t ret ;
ret = write ( IO_HANDLE_TO_FD ( handle ) , buf , wanted ) ;
if ( ret < 0 ) {
fprintf ( stderr , " %s: %llu byte write failed: %s \n " ,
2006-02-01 09:05:08 +03:00
PROGNAME , ( unsigned long long ) wanted ,
strerror ( errno ) ) ;
2006-01-31 09:09:18 +03:00
return ( False ) ;
}
* actual = ( uint64_t ) ret ;
return ( True ) ;
}
2006-02-01 09:05:08 +03:00
static struct dd_iohandle * open_fd_handle ( const char * path ,
2006-02-06 07:06:55 +03:00
uint64_t io_size ,
2006-02-01 09:05:08 +03:00
int options )
2006-01-31 09:09:18 +03:00
{
struct fd_handle * fdh ;
int oflags = 0 ;
DEBUG ( 4 , ( " opening fd stream for %s \n " , path ) ) ;
if ( ( fdh = talloc_zero ( NULL , struct fd_handle ) ) = = NULL ) {
return ( NULL ) ;
}
fdh - > h . io_read = fd_read_func ;
fdh - > h . io_write = fd_write_func ;
fdh - > h . io_seek = fd_seek_func ;
2006-02-01 00:56:12 +03:00
if ( options & DD_DIRECT_IO ) {
# ifdef HAVE_OPEN_O_DIRECT
2006-01-31 09:09:18 +03:00
oflags | = O_DIRECT ;
2006-02-01 00:56:12 +03:00
# else
DEBUG ( 1 , ( " no support for direct IO on this platform \n " ) ) ;
# endif
}
2006-01-31 09:09:18 +03:00
if ( options & DD_SYNC_IO )
oflags | = O_SYNC ;
oflags | = ( options & DD_WRITE ) ? ( O_WRONLY | O_CREAT ) : ( O_RDONLY ) ;
fdh - > fd = open ( path , oflags , 0644 ) ;
if ( fdh - > fd < 0 ) {
fprintf ( stderr , " %s: %s: %s \n " ,
PROGNAME , path , strerror ( errno ) ) ;
talloc_free ( fdh ) ;
return ( NULL ) ;
}
if ( options & DD_OPLOCK ) {
DEBUG ( 2 , ( " FIXME: take local oplock on %s \n " , path ) ) ;
}
SMB_ASSERT ( ( void * ) fdh = = ( void * ) & fdh - > h ) ;
return ( & fdh - > h ) ;
}
/* ------------------------------------------------------------------------- */
/* CIFS client IO. */
/* ------------------------------------------------------------------------- */
2006-03-13 01:48:25 +03:00
struct cifs_handle
2006-01-31 09:09:18 +03:00
{
struct dd_iohandle h ;
struct smbcli_state * cli ;
int fnum ;
uint64_t offset ;
} ;
2006-03-13 01:48:25 +03:00
# define IO_HANDLE_TO_SMB(h) ((struct cifs_handle *)(h))
2006-01-31 09:09:18 +03:00
BOOL smb_seek_func ( void * handle , uint64_t offset )
{
IO_HANDLE_TO_SMB ( handle ) - > offset = offset ;
return ( True ) ;
}
2006-02-01 09:05:08 +03:00
BOOL smb_read_func ( void * handle ,
uint8_t * buf ,
uint64_t wanted ,
uint64_t * actual )
2006-01-31 09:09:18 +03:00
{
NTSTATUS ret ;
union smb_read r ;
2006-03-13 01:48:25 +03:00
struct cifs_handle * smbh ;
2006-01-31 09:09:18 +03:00
ZERO_STRUCT ( r ) ;
smbh = IO_HANDLE_TO_SMB ( handle ) ;
2006-03-13 01:48:25 +03:00
r . generic . level = RAW_READ_READX ;
r . readx . in . file . fnum = smbh - > fnum ;
r . readx . in . offset = smbh - > offset ;
r . readx . in . mincnt = wanted ;
r . readx . in . maxcnt = wanted ;
r . readx . out . data = buf ;
2006-01-31 09:09:18 +03:00
/* FIXME: Should I really set readx.in.remaining? That just seems
* redundant .
*/
ret = smb_raw_read ( smbh - > cli - > tree , & r ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
fprintf ( stderr , " %s: %llu byte read failed: %s \n " ,
2006-02-01 09:05:08 +03:00
PROGNAME , ( unsigned long long ) wanted ,
nt_errstr ( ret ) ) ;
2006-01-31 09:09:18 +03:00
return ( False ) ;
}
/* Trap integer wrap. */
SMB_ASSERT ( ( smbh - > offset + r . readx . out . nread ) > = smbh - > offset ) ;
* actual = r . readx . out . nread ;
smbh - > offset + = r . readx . out . nread ;
return ( True ) ;
}
2006-02-01 09:05:08 +03:00
BOOL smb_write_func ( void * handle ,
uint8_t * buf ,
uint64_t wanted ,
uint64_t * actual )
2006-01-31 09:09:18 +03:00
{
NTSTATUS ret ;
union smb_write w ;
2006-03-13 01:48:25 +03:00
struct cifs_handle * smbh ;
2006-01-31 09:09:18 +03:00
ZERO_STRUCT ( w ) ;
smbh = IO_HANDLE_TO_SMB ( handle ) ;
2006-03-13 01:48:25 +03:00
w . generic . level = RAW_WRITE_WRITEX ;
w . writex . in . file . fnum = smbh - > fnum ;
w . writex . in . offset = smbh - > offset ;
w . writex . in . count = wanted ;
w . writex . in . data = buf ;
2006-01-31 09:09:18 +03:00
ret = smb_raw_write ( smbh - > cli - > tree , & w ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
fprintf ( stderr , " %s: %llu byte write failed: %s \n " ,
2006-02-01 09:05:08 +03:00
PROGNAME , ( unsigned long long ) wanted ,
nt_errstr ( ret ) ) ;
2006-01-31 09:09:18 +03:00
return ( False ) ;
}
* actual = w . writex . out . nwritten ;
smbh - > offset + = w . writex . out . nwritten ;
return ( True ) ;
}
2006-02-01 09:05:08 +03:00
static struct smbcli_state * init_smb_session ( const char * host ,
const char * share )
2006-01-31 09:09:18 +03:00
{
NTSTATUS ret ;
struct smbcli_state * cli = NULL ;
/* When we support SMB URLs, we can get different user credentials for
* each connection , but for now , we just use the same one for both .
*/
ret = smbcli_full_connection ( NULL , & cli , host , share ,
NULL /* devtype */ , cmdline_credentials , NULL /* events */ ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
fprintf ( stderr , " %s: connecting to //%s/%s: %s \n " ,
PROGNAME , host , share , nt_errstr ( ret ) ) ;
return ( NULL ) ;
}
return ( cli ) ;
}
2006-02-01 09:05:08 +03:00
static int open_smb_file ( struct smbcli_state * cli ,
const char * path ,
int options )
2006-01-31 09:09:18 +03:00
{
NTSTATUS ret ;
union smb_open o ;
ZERO_STRUCT ( o ) ;
o . ntcreatex . level = RAW_OPEN_NTCREATEX ;
o . ntcreatex . in . fname = path ;
/* TODO: It's not clear whether to use these flags or to use the
* similarly named NTCREATEX flags in the create_options field .
*/
if ( options & DD_DIRECT_IO )
o . ntcreatex . in . flags | = FILE_FLAG_NO_BUFFERING ;
if ( options & DD_SYNC_IO )
o . ntcreatex . in . flags | = FILE_FLAG_WRITE_THROUGH ;
o . ntcreatex . in . access_mask | =
( options & DD_WRITE ) ? SEC_FILE_WRITE_DATA
: SEC_FILE_READ_DATA ;
/* Try to create the file only if we will be writing to it. */
o . ntcreatex . in . open_disposition =
( options & DD_WRITE ) ? NTCREATEX_DISP_OPEN_IF
: NTCREATEX_DISP_OPEN ;
o . ntcreatex . in . share_access =
NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE ;
if ( options & DD_OPLOCK ) {
o . ntcreatex . in . flags | = NTCREATEX_FLAGS_REQUEST_OPLOCK ;
}
ret = smb_raw_open ( cli - > tree , NULL , & o ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
fprintf ( stderr , " %s: opening %s: %s \n " ,
PROGNAME , path , nt_errstr ( ret ) ) ;
return ( - 1 ) ;
}
2006-03-13 01:48:25 +03:00
return ( o . ntcreatex . out . file . fnum ) ;
2006-01-31 09:09:18 +03:00
}
2006-03-13 01:48:25 +03:00
static struct dd_iohandle * open_cifs_handle ( const char * host ,
2006-02-01 09:05:08 +03:00
const char * share ,
const char * path ,
2006-02-06 07:06:55 +03:00
uint64_t io_size ,
2006-02-01 09:05:08 +03:00
int options )
2006-01-31 09:09:18 +03:00
{
2006-03-13 01:48:25 +03:00
struct cifs_handle * smbh ;
2006-01-31 09:09:18 +03:00
2006-02-08 08:14:48 +03:00
if ( path = = NULL | | * path = = ' \0 ' ) {
fprintf ( stderr , " %s: missing path name within share //%s/%s \n " ,
PROGNAME , host , share ) ;
}
2006-01-31 09:09:18 +03:00
DEBUG ( 4 , ( " opening SMB stream to //%s/%s for %s \n " ,
host , share , path ) ) ;
2006-03-13 01:48:25 +03:00
if ( ( smbh = talloc_zero ( NULL , struct cifs_handle ) ) = = NULL ) {
2006-01-31 09:09:18 +03:00
return ( NULL ) ;
}
smbh - > h . io_read = smb_read_func ;
smbh - > h . io_write = smb_write_func ;
smbh - > h . io_seek = smb_seek_func ;
if ( ( smbh - > cli = init_smb_session ( host , share ) ) = = NULL ) {
return ( NULL ) ;
}
DEBUG ( 4 , ( " connected to //%s/%s with xmit size of %u bytes \n " ,
host , share , smbh - > cli - > transport - > negotiate . max_xmit ) ) ;
smbh - > fnum = open_smb_file ( smbh - > cli , path , options ) ;
return ( & smbh - > h ) ;
}
/* ------------------------------------------------------------------------- */
/* Abstract IO interface. */
/* ------------------------------------------------------------------------- */
2006-02-06 07:06:55 +03:00
struct dd_iohandle * dd_open_path ( const char * path ,
uint64_t io_size ,
int options )
2006-01-31 09:09:18 +03:00
{
if ( file_exist ( path ) ) {
2006-02-06 07:06:55 +03:00
return ( open_fd_handle ( path , io_size , options ) ) ;
2006-01-31 09:09:18 +03:00
} else {
char * host ;
char * share ;
if ( smbcli_parse_unc ( path , NULL , & host , & share ) ) {
const char * remain ;
remain = strstr ( path , share ) + strlen ( share ) ;
/* Skip over leading directory separators. */
while ( * remain = = ' / ' | | * remain = = ' \\ ' ) { remain + + ; }
2006-03-13 01:48:25 +03:00
return ( open_cifs_handle ( host , share , remain ,
2006-02-06 07:06:55 +03:00
io_size , options ) ) ;
2006-01-31 09:09:18 +03:00
}
2006-02-06 07:06:55 +03:00
return ( open_fd_handle ( path , io_size , options ) ) ;
2006-01-31 09:09:18 +03:00
}
}
2006-02-06 07:06:55 +03:00
/* Fill the buffer till it has at least need_size bytes. Use read operations of
* block_size bytes . Return the number of bytes read and fill buf_size with
* the new buffer size .
2006-01-31 09:09:18 +03:00
*
2006-02-06 07:06:55 +03:00
* NOTE : The IO buffer is guaranteed to be big enough to fit
* need_size + block_size bytes into it .
2006-01-31 09:09:18 +03:00
*/
2006-02-01 09:05:08 +03:00
BOOL dd_fill_block ( struct dd_iohandle * h ,
uint8_t * buf ,
2006-02-06 07:06:55 +03:00
uint64_t * buf_size ,
uint64_t need_size ,
uint64_t block_size )
2006-01-31 09:09:18 +03:00
{
2006-02-06 07:06:55 +03:00
uint64_t read_size ;
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
SMB_ASSERT ( block_size > 0 ) ;
SMB_ASSERT ( need_size > 0 ) ;
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
while ( * buf_size < need_size ) {
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
if ( ! h - > io_read ( h , buf + ( * buf_size ) , block_size , & read_size ) ) {
2006-01-31 09:09:18 +03:00
return ( False ) ;
}
2006-02-06 07:06:55 +03:00
if ( read_size = = 0 ) {
2006-01-31 09:09:18 +03:00
h - > io_flags | = DD_END_OF_FILE ;
break ;
}
DEBUG ( 6 , ( " added %llu bytes to IO buffer (need %llu bytes) \n " ,
2006-02-06 07:06:55 +03:00
( unsigned long long ) read_size ,
( unsigned long long ) need_size ) ) ;
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
* buf_size + = read_size ;
dd_stats . in . bytes + = read_size ;
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
if ( read_size = = block_size ) {
2006-01-31 09:09:18 +03:00
dd_stats . in . fblocks + + ;
} else {
DEBUG ( 3 , ( " partial read of %llu bytes (expected %llu) \n " ,
2006-02-06 07:06:55 +03:00
( unsigned long long ) read_size ,
( unsigned long long ) block_size ) ) ;
2006-01-31 09:09:18 +03:00
dd_stats . in . pblocks + + ;
}
}
return ( True ) ;
}
2006-02-06 07:06:55 +03:00
/* Flush a buffer that contains buf_size bytes. Use writes of block_size to do it,
2006-01-31 09:09:18 +03:00
* and shift any remaining bytes back to the head of the buffer when there are
2006-02-06 07:06:55 +03:00
* no more block_size sized IOs left .
2006-01-31 09:09:18 +03:00
*/
2006-02-01 09:05:08 +03:00
BOOL dd_flush_block ( struct dd_iohandle * h ,
uint8_t * buf ,
2006-02-06 07:06:55 +03:00
uint64_t * buf_size ,
uint64_t block_size )
2006-01-31 09:09:18 +03:00
{
2006-02-06 07:06:55 +03:00
uint64_t write_size ;
uint64_t total_size = 0 ;
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
SMB_ASSERT ( block_size > 0 ) ;
2006-01-31 09:09:18 +03:00
/* We have explicitly been asked to write a partial block. */
2006-02-06 07:06:55 +03:00
if ( ( * buf_size ) < block_size ) {
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
if ( ! h - > io_write ( h , buf , * buf_size , & write_size ) ) {
2006-01-31 09:09:18 +03:00
return ( False ) ;
}
2006-02-06 07:06:55 +03:00
if ( write_size = = 0 ) {
2006-01-31 09:09:18 +03:00
fprintf ( stderr , " %s: unexpectedly wrote 0 bytes \n " ,
PROGNAME ) ;
return ( False ) ;
}
2006-02-06 07:06:55 +03:00
total_size + = write_size ;
dd_stats . out . bytes + = write_size ;
2006-01-31 09:09:18 +03:00
dd_stats . out . pblocks + + ;
}
/* Write as many full blocks as there are in the buffer. */
2006-02-06 07:06:55 +03:00
while ( ( ( * buf_size ) - total_size ) > = block_size ) {
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
if ( ! h - > io_write ( h , buf + total_size , block_size , & write_size ) ) {
2006-01-31 09:09:18 +03:00
return ( False ) ;
}
2006-02-06 07:06:55 +03:00
if ( write_size = = 0 ) {
2006-01-31 09:09:18 +03:00
fprintf ( stderr , " %s: unexpectedly wrote 0 bytes \n " ,
PROGNAME ) ;
return ( False ) ;
}
2006-02-06 07:06:55 +03:00
if ( write_size = = block_size ) {
2006-01-31 09:09:18 +03:00
dd_stats . out . fblocks + + ;
} else {
dd_stats . out . pblocks + + ;
}
2006-02-06 07:06:55 +03:00
total_size + = write_size ;
dd_stats . out . bytes + = write_size ;
2006-01-31 09:09:18 +03:00
DEBUG ( 6 , ( " flushed %llu bytes from IO buffer of %llu bytes (%llu remain) \n " ,
2006-02-06 07:06:55 +03:00
( unsigned long long ) block_size ,
( unsigned long long ) block_size ,
( unsigned long long ) ( block_size - total_size ) ) ) ;
2006-01-31 09:09:18 +03:00
}
2006-02-06 07:06:55 +03:00
SMB_ASSERT ( total_size > 0 ) ;
2006-01-31 09:09:18 +03:00
/* We have flushed as much of the IO buffer as we can while
2006-02-06 07:06:55 +03:00
* still doing block_size ' d operations . Shift any remaining data
2006-01-31 09:09:18 +03:00
* to the front of the IO buffer .
*/
2006-02-06 07:06:55 +03:00
if ( ( * buf_size ) > total_size ) {
uint64_t remain = ( * buf_size ) - total_size ;
2006-01-31 09:09:18 +03:00
DEBUG ( 3 , ( " shifting %llu remainder bytes to IO buffer head \n " ,
( unsigned long long ) remain ) ) ;
2006-02-06 07:06:55 +03:00
memmove ( buf , buf + total_size , remain ) ;
( * buf_size ) = remain ;
} else if ( ( * buf_size ) = = total_size ) {
( * buf_size ) = 0 ;
2006-01-31 09:09:18 +03:00
} else {
2006-02-06 07:06:55 +03:00
/* Else buffer contains buf_size bytes that we will append
2006-01-31 09:09:18 +03:00
* to next time round .
*/
DEBUG ( 3 , ( " %llu unflushed bytes left in IO buffer \n " ,
2006-02-06 07:06:55 +03:00
( unsigned long long ) ( * buf_size ) ) ) ;
2006-01-31 09:09:18 +03:00
}
return ( True ) ;
}
/* vim: set sw=8 sts=8 ts=8 tw=79 : */