1998-08-17 11:15:54 +04:00
/*
2002-07-15 14:35:28 +04:00
Unix SMB / Netbios implementation .
Version 1.9 .
1998-08-17 11:15:54 +04:00
read / write to a files_struct
Copyright ( C ) Andrew Tridgell 1992 - 1998
2002-07-15 14:35:28 +04:00
Copyright ( C ) Jeremy Allison 2000 - 2002. - write cache .
1998-08-17 11:15:54 +04:00
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"
1999-12-13 16:27:58 +03:00
static BOOL setup_write_cache ( files_struct * , SMB_OFF_T ) ;
1998-08-17 11:15:54 +04:00
1999-12-13 16:27:58 +03:00
/****************************************************************************
Read from write cache if we can .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-07-15 14:35:28 +04:00
static BOOL read_from_write_cache ( files_struct * fsp , char * data , SMB_OFF_T pos , size_t n )
1999-12-13 16:27:58 +03:00
{
2002-07-15 14:35:28 +04:00
write_cache * wcp = fsp - > wcp ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( ! wcp )
return False ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( n > wcp - > data_size | | pos < wcp - > offset | | pos + n > wcp - > offset + wcp - > data_size )
return False ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
memcpy ( data , wcp - > data + ( pos - wcp - > offset ) , n ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DO_PROFILE_INC ( writecache_read_hits ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
return True ;
1999-12-13 16:27:58 +03:00
}
1998-08-17 11:15:54 +04:00
/****************************************************************************
2002-07-15 14:35:28 +04:00
Read from a file .
1998-08-17 11:15:54 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-11 05:24:30 +04:00
ssize_t read_file ( files_struct * fsp , char * data , SMB_OFF_T pos , size_t n )
1998-08-17 11:15:54 +04:00
{
2001-11-25 11:26:37 +03:00
ssize_t ret = 0 , readret ;
1998-08-17 11:15:54 +04:00
2001-11-25 11:26:37 +03:00
/* you can't read from print files */
if ( fsp - > print_file )
return - 1 ;
2000-04-16 10:22:31 +04:00
2001-11-25 11:26:37 +03:00
/*
* Serve from write cache if we can .
*/
1999-12-13 16:27:58 +03:00
2003-08-15 01:16:06 +04:00
if ( read_from_write_cache ( fsp , data , pos , n ) ) {
fsp - > pos = pos + n ;
fsp - > position_information = fsp - > pos ;
2001-11-25 11:26:37 +03:00
return n ;
2003-08-15 01:16:06 +04:00
}
1998-08-17 11:15:54 +04:00
2001-11-25 11:26:37 +03:00
flush_write_cache ( fsp , READ_FLUSH ) ;
1998-08-17 11:15:54 +04:00
2004-01-06 04:22:14 +03:00
fsp - > pos = pos ;
2001-11-25 11:26:37 +03:00
if ( n > 0 ) {
# ifdef DMF_FIX
int numretries = 3 ;
tryagain :
2004-01-06 04:22:14 +03:00
readret = SMB_VFS_PREAD ( fsp , fsp - > fd , data , n , pos ) ;
2001-11-25 11:26:37 +03:00
if ( readret = = - 1 ) {
if ( ( errno = = EAGAIN ) & & numretries ) {
DEBUG ( 3 , ( " read_file EAGAIN retry in 10 seconds \n " ) ) ;
( void ) sleep ( 10 ) ;
- - numretries ;
goto tryagain ;
}
return - 1 ;
}
# else /* NO DMF fix. */
2004-01-06 04:22:14 +03:00
readret = SMB_VFS_PREAD ( fsp , fsp - > fd , data , n , pos ) ;
2001-11-25 11:26:37 +03:00
if ( readret = = - 1 )
return - 1 ;
# endif
if ( readret > 0 )
ret + = readret ;
}
2002-07-15 14:35:28 +04:00
DEBUG ( 10 , ( " read_file (%s): pos = %.0f, size = %lu, returned %lu \n " ,
fsp - > fsp_name , ( double ) pos , ( unsigned long ) n , ( long ) ret ) ) ;
2003-08-15 01:16:06 +04:00
fsp - > pos + = ret ;
fsp - > position_information = fsp - > pos ;
2001-11-25 11:26:37 +03:00
return ( ret ) ;
1998-08-17 11:15:54 +04:00
}
1999-12-13 16:27:58 +03:00
/* how many write cache buffers have been allocated */
static unsigned int allocated_write_caches ;
/****************************************************************************
2000-08-26 00:44:33 +04:00
* Really * write to a file .
1999-12-13 16:27:58 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static ssize_t real_write_file ( files_struct * fsp , char * data , SMB_OFF_T pos , size_t n )
{
2002-07-15 14:35:28 +04:00
ssize_t ret ;
2004-01-06 04:22:14 +03:00
if ( pos = = - 1 )
ret = vfs_write_data ( fsp , data , n ) ;
else {
fsp - > pos = pos ;
ret = vfs_pwrite_data ( fsp , data , n , pos ) ;
}
2002-07-15 14:35:28 +04:00
DEBUG ( 10 , ( " real_write_file (%s): pos = %.0f, size = %lu, returned %ld \n " ,
fsp - > fsp_name , ( double ) pos , ( unsigned long ) n , ( long ) ret ) ) ;
1999-12-13 16:27:58 +03:00
2003-08-15 01:16:06 +04:00
if ( ret ! = - 1 ) {
fsp - > pos + = ret ;
2004-11-21 00:24:52 +03:00
/*
* It turns out that setting the last write time from a Windows
* client stops any subsequent writes from updating the write time .
* Doing this after the write gives a race condition here where
* a stat may see the changed write time before we reset it here ,
* but it ' s cheaper than having to store the write time in shared
* memory and look it up using dev / inode across all running smbd ' s .
* The 99 % solution will hopefully be good enough in this case . JRA .
*/
if ( fsp - > pending_modtime ) {
set_filetime ( fsp - > conn , fsp - > fsp_name , fsp - > pending_modtime ) ;
}
2003-08-15 01:16:06 +04:00
/* Yes - this is correct - writes don't update this. JRA. */
/* Found by Samba4 tests. */
#if 0
fsp - > position_information = fsp - > pos ;
# endif
}
2002-07-15 14:35:28 +04:00
return ret ;
1999-12-13 16:27:58 +03:00
}
1998-08-17 11:15:54 +04:00
/****************************************************************************
write to a file
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-11 05:24:30 +04:00
1999-12-13 16:27:58 +03:00
ssize_t write_file ( files_struct * fsp , char * data , SMB_OFF_T pos , size_t n )
1998-08-17 11:15:54 +04:00
{
2002-07-15 14:35:28 +04:00
write_cache * wcp = fsp - > wcp ;
ssize_t total_written = 0 ;
int write_path = - 1 ;
2002-12-05 07:00:16 +03:00
if ( fsp - > print_file ) {
2004-10-19 21:05:01 +04:00
fstring sharename ;
2002-12-05 07:00:16 +03:00
uint32 jobid ;
2004-10-19 21:05:01 +04:00
if ( ! rap_to_pjobid ( fsp - > rap_print_jobid , sharename , & jobid ) ) {
2002-12-05 07:00:16 +03:00
DEBUG ( 3 , ( " write_file: Unable to map RAP jobid %u to jobid. \n " ,
( unsigned int ) fsp - > rap_print_jobid ) ) ;
errno = EBADF ;
return - 1 ;
}
return print_job_write ( SNUM ( fsp - > conn ) , jobid , data , n ) ;
}
2002-07-15 14:35:28 +04:00
if ( ! fsp - > can_write ) {
errno = EPERM ;
return ( 0 ) ;
}
if ( ! fsp - > modified ) {
SMB_STRUCT_STAT st ;
fsp - > modified = True ;
2003-05-14 14:59:01 +04:00
if ( SMB_VFS_FSTAT ( fsp , fsp - > fd , & st ) = = 0 ) {
2002-07-15 14:35:28 +04:00
int dosmode = dos_mode ( fsp - > conn , fsp - > fsp_name , & st ) ;
2002-12-04 06:12:09 +03:00
fsp - > size = ( SMB_BIG_UINT ) st . st_size ;
2004-04-02 22:46:19 +04:00
if ( ( lp_store_dos_attributes ( SNUM ( fsp - > conn ) ) | | MAP_ARCHIVE ( fsp - > conn ) ) & & ! IS_DOS_ARCHIVE ( dosmode ) ) {
2004-11-30 03:22:04 +03:00
file_set_dosmode ( fsp - > conn , fsp - > fsp_name , dosmode | aARCH , & st , False ) ;
2004-04-02 22:46:19 +04:00
}
2002-07-15 14:35:28 +04:00
/*
* If this is the first write and we have an exclusive oplock then setup
* the write cache .
*/
if ( EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) & & ! wcp ) {
setup_write_cache ( fsp , st . st_size ) ;
wcp = fsp - > wcp ;
}
}
}
1998-08-17 11:15:54 +04:00
2000-10-05 22:50:18 +04:00
# ifdef WITH_PROFILE
2002-07-15 14:35:28 +04:00
DO_PROFILE_INC ( writecache_total_writes ) ;
if ( ! fsp - > oplock_type ) {
DO_PROFILE_INC ( writecache_non_oplock_writes ) ;
}
2000-10-05 22:50:18 +04:00
# endif
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* If this file is level II oplocked then we need
* to grab the shared memory lock and inform all
* other files with a level II lock that they need
* to flush their read caches . We keep the lock over
* the shared memory area whilst doing this .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
release_level_2_oplocks_on_change ( fsp ) ;
1999-12-13 16:27:58 +03:00
2000-10-05 22:50:18 +04:00
# ifdef WITH_PROFILE
2002-07-15 14:35:28 +04:00
if ( profile_p & & profile_p - > writecache_total_writes % 500 = = 0 ) {
DEBUG ( 3 , ( " WRITECACHE: initwrites=%u abutted=%u total=%u \
1999-12-13 16:27:58 +03:00
nonop = % u allocated = % u active = % u direct = % u perfect = % u readhits = % u \ n " ,
2002-07-15 14:35:28 +04:00
profile_p - > writecache_init_writes ,
profile_p - > writecache_abutted_writes ,
profile_p - > writecache_total_writes ,
profile_p - > writecache_non_oplock_writes ,
profile_p - > writecache_allocated_write_caches ,
profile_p - > writecache_num_write_caches ,
profile_p - > writecache_direct_writes ,
profile_p - > writecache_num_perfect_writes ,
profile_p - > writecache_read_hits ) ) ;
DEBUG ( 3 , ( " WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d \n " ,
profile_p - > writecache_flushed_writes [ SEEK_FLUSH ] ,
profile_p - > writecache_flushed_writes [ READ_FLUSH ] ,
profile_p - > writecache_flushed_writes [ WRITE_FLUSH ] ,
profile_p - > writecache_flushed_writes [ READRAW_FLUSH ] ,
profile_p - > writecache_flushed_writes [ OPLOCK_RELEASE_FLUSH ] ,
profile_p - > writecache_flushed_writes [ CLOSE_FLUSH ] ,
profile_p - > writecache_flushed_writes [ SYNC_FLUSH ] ) ) ;
}
2000-10-05 22:50:18 +04:00
# endif
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( ! wcp ) {
DO_PROFILE_INC ( writecache_direct_writes ) ;
total_written = real_write_file ( fsp , data , pos , n ) ;
2003-08-15 01:16:06 +04:00
if ( ( total_written ! = - 1 ) & & ( pos + total_written > ( SMB_OFF_T ) fsp - > size ) )
2002-12-04 06:12:09 +03:00
fsp - > size = ( SMB_BIG_UINT ) ( pos + total_written ) ;
2002-07-15 14:35:28 +04:00
return total_written ;
}
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DEBUG ( 9 , ( " write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u \n " ,
fsp - > fsp_name , fsp - > fd , ( double ) pos , ( unsigned int ) n , ( double ) wcp - > offset , ( unsigned int ) wcp - > data_size ) ) ;
1999-12-13 16:27:58 +03:00
2003-08-15 01:16:06 +04:00
fsp - > pos = pos + n ;
2002-07-15 14:35:28 +04:00
/*
* If we have active cache and it isn ' t contiguous then we flush .
* NOTE : There is a small problem with running out of disk . . . .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( wcp - > data_size ) {
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
BOOL cache_flush_needed = False ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( ( pos > = wcp - > offset ) & & ( pos < = wcp - > offset + wcp - > data_size ) ) {
1999-12-13 16:27:58 +03:00
2002-10-03 23:05:36 +04:00
/* ASCII art.... JRA.
+ - - - - - - - - - - - - - - + - - - - -
| Cached data | Rest of allocated cache buffer . . . .
+ - - - - - - - - - - - - - - + - - - - -
+ - - - - - - - - - - - - - - - - - - - +
| Data to write |
+ - - - - - - - - - - - - - - - - - - - +
*/
2002-07-15 14:35:28 +04:00
/*
* Start of write overlaps or abutts the existing data .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
size_t data_used = MIN ( ( wcp - > alloc_size - ( pos - wcp - > offset ) ) , n ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
memcpy ( wcp - > data + ( pos - wcp - > offset ) , data , data_used ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Update the current buffer size with the new data .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( pos + data_used > wcp - > offset + wcp - > data_size )
wcp - > data_size = pos + data_used - wcp - > offset ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Update the file size if changed .
*/
2002-01-20 03:43:28 +03:00
2002-12-04 06:12:09 +03:00
if ( wcp - > offset + wcp - > data_size > wcp - > file_size ) {
wcp - > file_size = wcp - > offset + wcp - > data_size ;
fsp - > size = ( SMB_BIG_UINT ) wcp - > file_size ;
}
2002-01-20 03:43:28 +03:00
2002-07-15 14:35:28 +04:00
/*
* If we used all the data then
* return here .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( n = = data_used )
return n ;
else
cache_flush_needed = True ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Move the start of data forward by the amount used ,
* cut down the amount left by the same amount .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
data + = data_used ;
pos + = data_used ;
n - = data_used ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DO_PROFILE_INC ( writecache_abutted_writes ) ;
total_written = data_used ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
write_path = 1 ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
} else if ( ( pos < wcp - > offset ) & & ( pos + n > wcp - > offset ) & &
( pos + n < = wcp - > offset + wcp - > alloc_size ) ) {
1999-12-13 16:27:58 +03:00
2002-10-03 23:05:36 +04:00
/* ASCII art.... JRA.
+ - - - - - - - - - - - - - - - +
| Cache buffer |
+ - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - - - - - +
| Data to write |
+ - - - - - - - - - - - - - - - - - - - +
*/
2002-07-15 14:35:28 +04:00
/*
* End of write overlaps the existing data .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
size_t data_used = pos + n - wcp - > offset ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
memcpy ( wcp - > data , data + n - data_used , data_used ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Update the current buffer size with the new data .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( pos + n > wcp - > offset + wcp - > data_size )
wcp - > data_size = pos + n - wcp - > offset ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Update the file size if changed .
*/
2002-01-20 03:43:28 +03:00
2002-12-04 06:12:09 +03:00
if ( wcp - > offset + wcp - > data_size > wcp - > file_size ) {
wcp - > file_size = wcp - > offset + wcp - > data_size ;
fsp - > size = ( SMB_BIG_UINT ) wcp - > file_size ;
}
2002-01-20 03:43:28 +03:00
2002-07-15 14:35:28 +04:00
/*
* We don ' t need to move the start of data , but we
* cut down the amount left by the amount used .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
n - = data_used ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* We cannot have used all the data here .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
cache_flush_needed = True ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DO_PROFILE_INC ( writecache_abutted_writes ) ;
total_written = data_used ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
write_path = 2 ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
} else if ( ( pos > = wcp - > file_size ) & &
( wcp - > offset + wcp - > data_size = = wcp - > file_size ) & &
( pos > wcp - > offset + wcp - > data_size ) & &
( pos < wcp - > offset + wcp - > alloc_size ) ) {
1999-12-13 16:27:58 +03:00
2002-10-03 23:05:36 +04:00
/* ASCII art.... JRA.
End of file - - - - > |
+ - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
| Cached data | Cache buffer |
+ - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - - - - - +
| Data to write |
+ - - - - - - - - - - - - - - - - - - - +
*/
2002-07-15 14:35:28 +04:00
/*
* Non - contiguous write part of which fits within
* the cache buffer and is extending the file
* and the cache contents reflect the current
* data up to the current end of the file .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
size_t data_used ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( pos + n < = wcp - > offset + wcp - > alloc_size )
data_used = n ;
else
data_used = wcp - > offset + wcp - > alloc_size - pos ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Fill in the non - continuous area with zeros .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
memset ( wcp - > data + wcp - > data_size , ' \0 ' ,
pos - ( wcp - > offset + wcp - > data_size ) ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
memcpy ( wcp - > data + ( pos - wcp - > offset ) , data , data_used ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Update the current buffer size with the new data .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( pos + data_used > wcp - > offset + wcp - > data_size )
wcp - > data_size = pos + data_used - wcp - > offset ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Update the file size if changed .
*/
1999-12-13 16:27:58 +03:00
2002-12-04 06:12:09 +03:00
if ( wcp - > offset + wcp - > data_size > wcp - > file_size ) {
wcp - > file_size = wcp - > offset + wcp - > data_size ;
fsp - > size = ( SMB_BIG_UINT ) wcp - > file_size ;
}
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* If we used all the data then
* return here .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( n = = data_used )
return n ;
else
cache_flush_needed = True ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Move the start of data forward by the amount used ,
* cut down the amount left by the same amount .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
data + = data_used ;
pos + = data_used ;
n - = data_used ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DO_PROFILE_INC ( writecache_abutted_writes ) ;
total_written = data_used ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
write_path = 3 ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
} else {
1999-12-13 16:27:58 +03:00
2002-10-03 23:05:36 +04:00
/* ASCII art..... JRA.
Case 1 ) .
+ - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
| Cached data | Cache buffer |
+ - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - - - - - +
| Data to write |
+ - - - - - - - - - - - - - - - - - - - +
Case 2 ) .
+ - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
| Cached data | Cache buffer |
+ - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - - - - - +
| Data to write |
+ - - - - - - - - - - - - - - - - - - - +
Case 3 ) .
+ - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
| Cached data | Cache buffer |
+ - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Data to write |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
/*
2002-07-15 14:35:28 +04:00
* Write is bigger than buffer , or there is no overlap on the
* low or high ends .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DEBUG ( 9 , ( " write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
2000-04-10 17:05:23 +04:00
len = % u \ n " ,fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* Update the file size if needed .
*/
1999-12-13 16:27:58 +03:00
2002-12-04 06:12:09 +03:00
if ( pos + n > wcp - > file_size ) {
wcp - > file_size = pos + n ;
fsp - > size = ( SMB_BIG_UINT ) wcp - > file_size ;
}
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* If write would fit in the cache , and is larger than
* the data already in the cache , flush the cache and
* preferentially copy the data new data into it . Otherwise
* just write the data directly .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( n < = wcp - > alloc_size & & n > wcp - > data_size ) {
cache_flush_needed = True ;
} else {
ssize_t ret = real_write_file ( fsp , data , pos , n ) ;
2002-01-20 03:43:28 +03:00
2002-10-03 22:10:59 +04:00
/*
* If the write overlaps the entire cache , then
* discard the current contents of the cache .
* Fix from Rasmus Borup Hansen rbh @ math . ku . dk .
*/
if ( ( pos < = wcp - > offset ) & &
( pos + n > = wcp - > offset + wcp - > data_size ) ) {
DEBUG ( 9 , ( " write_file: discarding overwritten write \
cache : fd = % d , off = % .0f , size = % u \ n " , fsp->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
wcp - > data_size = 0 ;
}
2002-07-15 14:35:28 +04:00
DO_PROFILE_INC ( writecache_direct_writes ) ;
if ( ret = = - 1 )
return ret ;
2002-01-20 03:43:28 +03:00
2002-12-04 06:12:09 +03:00
if ( pos + ret > wcp - > file_size ) {
wcp - > file_size = pos + ret ;
fsp - > size = ( SMB_BIG_UINT ) wcp - > file_size ;
}
2002-01-20 03:43:28 +03:00
2002-07-15 14:35:28 +04:00
return ret ;
}
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
write_path = 4 ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
}
1999-12-13 16:27:58 +03:00
2002-12-04 06:12:09 +03:00
if ( wcp - > data_size > wcp - > file_size ) {
wcp - > file_size = wcp - > data_size ;
fsp - > size = ( SMB_BIG_UINT ) wcp - > file_size ;
}
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( cache_flush_needed ) {
DEBUG ( 3 , ( " WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
1999-12-13 16:27:58 +03:00
n = % u , wcp - > offset = % .0f , wcp - > data_size = % u \ n " ,
2002-07-15 14:35:28 +04:00
write_path , fsp - > fd , ( double ) wcp - > file_size , ( double ) pos , ( unsigned int ) n ,
( double ) wcp - > offset , ( unsigned int ) wcp - > data_size ) ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
flush_write_cache ( fsp , WRITE_FLUSH ) ;
}
}
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* If the write request is bigger than the cache
* size , write it all out .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( n > wcp - > alloc_size ) {
ssize_t ret = real_write_file ( fsp , data , pos , n ) ;
if ( ret = = - 1 )
return - 1 ;
2002-01-20 03:43:28 +03:00
2002-12-04 06:12:09 +03:00
if ( pos + ret > wcp - > file_size ) {
wcp - > file_size = pos + n ;
fsp - > size = ( SMB_BIG_UINT ) wcp - > file_size ;
}
2002-01-20 03:43:28 +03:00
2002-07-15 14:35:28 +04:00
DO_PROFILE_INC ( writecache_direct_writes ) ;
return total_written + n ;
}
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
/*
* If there ' s any data left , cache it .
*/
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( n ) {
2000-10-05 22:50:18 +04:00
# ifdef WITH_PROFILE
2002-07-15 14:35:28 +04:00
if ( wcp - > data_size ) {
DO_PROFILE_INC ( writecache_abutted_writes ) ;
} else {
DO_PROFILE_INC ( writecache_init_writes ) ;
}
2000-10-05 22:50:18 +04:00
# endif
2002-07-15 14:35:28 +04:00
memcpy ( wcp - > data + wcp - > data_size , data , n ) ;
if ( wcp - > data_size = = 0 ) {
wcp - > offset = pos ;
DO_PROFILE_INC ( writecache_num_write_caches ) ;
}
wcp - > data_size + = n ;
/*
* Update the file size if changed .
*/
2002-12-04 06:12:09 +03:00
if ( wcp - > offset + wcp - > data_size > wcp - > file_size ) {
wcp - > file_size = wcp - > offset + wcp - > data_size ;
fsp - > size = ( SMB_BIG_UINT ) wcp - > file_size ;
}
2002-07-15 14:35:28 +04:00
DEBUG ( 9 , ( " wcp->offset = %.0f wcp->data_size = %u cache return %u \n " ,
( double ) wcp - > offset , ( unsigned int ) wcp - > data_size , ( unsigned int ) n ) ) ;
total_written + = n ;
return total_written ; /* .... that's a write :) */
}
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
return total_written ;
1998-08-17 11:15:54 +04:00
}
1999-12-13 16:27:58 +03:00
/****************************************************************************
Delete the write cache structure .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-08-17 11:40:06 +04:00
1999-12-13 16:27:58 +03:00
void delete_write_cache ( files_struct * fsp )
{
2002-07-15 14:35:28 +04:00
write_cache * wcp ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( ! fsp )
return ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( ! ( wcp = fsp - > wcp ) )
return ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DO_PROFILE_DEC ( writecache_allocated_write_caches ) ;
allocated_write_caches - - ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
SMB_ASSERT ( wcp - > data_size = = 0 ) ;
2000-10-03 01:03:46 +04:00
2002-07-15 14:35:28 +04:00
SAFE_FREE ( wcp - > data ) ;
SAFE_FREE ( fsp - > wcp ) ;
2000-10-03 01:03:46 +04:00
2002-07-15 14:35:28 +04:00
DEBUG ( 10 , ( " delete_write_cache: File %s deleted write cache \n " , fsp - > fsp_name ) ) ;
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Setup the write cache structure .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL setup_write_cache ( files_struct * fsp , SMB_OFF_T file_size )
{
2002-07-15 14:35:28 +04:00
ssize_t alloc_size = lp_write_cache_size ( SNUM ( fsp - > conn ) ) ;
write_cache * wcp ;
if ( allocated_write_caches > = MAX_WRITE_CACHES )
return False ;
if ( alloc_size = = 0 | | fsp - > wcp )
return False ;
if ( ( wcp = ( write_cache * ) malloc ( sizeof ( write_cache ) ) ) = = NULL ) {
DEBUG ( 0 , ( " setup_write_cache: malloc fail. \n " ) ) ;
return False ;
}
wcp - > file_size = file_size ;
wcp - > offset = 0 ;
wcp - > alloc_size = alloc_size ;
wcp - > data_size = 0 ;
if ( ( wcp - > data = malloc ( wcp - > alloc_size ) ) = = NULL ) {
DEBUG ( 0 , ( " setup_write_cache: malloc fail for buffer size %u. \n " ,
( unsigned int ) wcp - > alloc_size ) ) ;
SAFE_FREE ( wcp ) ;
return False ;
}
memset ( wcp - > data , ' \0 ' , wcp - > alloc_size ) ;
fsp - > wcp = wcp ;
DO_PROFILE_INC ( writecache_allocated_write_caches ) ;
allocated_write_caches + + ;
2003-11-02 20:10:12 +03:00
DEBUG ( 10 , ( " setup_write_cache: File %s allocated write cache size %lu \n " ,
fsp - > fsp_name , ( unsigned long ) wcp - > alloc_size ) ) ;
2000-10-03 01:03:46 +04:00
2002-07-15 14:35:28 +04:00
return True ;
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Cope with a size change .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void set_filelen_write_cache ( files_struct * fsp , SMB_OFF_T file_size )
{
2002-12-04 06:12:09 +03:00
fsp - > size = ( SMB_BIG_UINT ) file_size ;
2002-07-15 14:35:28 +04:00
if ( fsp - > wcp ) {
/* The cache *must* have been flushed before we do this. */
if ( fsp - > wcp - > data_size ! = 0 ) {
pstring msg ;
slprintf ( msg , sizeof ( msg ) - 1 , " set_filelen_write_cache: size change \
2003-11-02 20:10:12 +03:00
on file % s with write cache size = % lu \ n " , fsp->fsp_name, (unsigned long)fsp->wcp->data_size );
2002-07-15 14:35:28 +04:00
smb_panic ( msg ) ;
}
fsp - > wcp - > file_size = file_size ;
}
1999-12-13 16:27:58 +03:00
}
/*******************************************************************
Flush a write cache struct to disk .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
ssize_t flush_write_cache ( files_struct * fsp , enum flush_reason_enum reason )
{
2002-07-15 14:35:28 +04:00
write_cache * wcp = fsp - > wcp ;
size_t data_size ;
ssize_t ret ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
if ( ! wcp | | ! wcp - > data_size )
return 0 ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
data_size = wcp - > data_size ;
wcp - > data_size = 0 ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DO_PROFILE_DEC_INC ( writecache_num_write_caches , writecache_flushed_writes [ reason ] ) ;
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
DEBUG ( 9 , ( " flushing write cache: fd = %d, off=%.0f, size=%u \n " ,
fsp - > fd , ( double ) wcp - > offset , ( unsigned int ) data_size ) ) ;
1999-12-13 16:27:58 +03:00
2000-10-05 22:50:18 +04:00
# ifdef WITH_PROFILE
2002-07-15 14:35:28 +04:00
if ( data_size = = wcp - > alloc_size )
DO_PROFILE_INC ( writecache_num_perfect_writes ) ;
2000-10-05 22:50:18 +04:00
# endif
1999-12-13 16:27:58 +03:00
2002-07-15 14:35:28 +04:00
ret = real_write_file ( fsp , wcp - > data , wcp - > offset , data_size ) ;
2002-01-20 03:04:15 +03:00
2002-07-15 14:35:28 +04:00
/*
* Ensure file size if kept up to date if write extends file .
*/
2002-01-20 03:04:15 +03:00
2002-07-15 14:35:28 +04:00
if ( ( ret ! = - 1 ) & & ( wcp - > offset + ret > wcp - > file_size ) )
wcp - > file_size = wcp - > offset + ret ;
2002-01-20 03:04:15 +03:00
2002-07-15 14:35:28 +04:00
return ret ;
1999-12-13 16:27:58 +03:00
}
2000-02-03 08:17:25 +03:00
1998-08-17 11:40:06 +04:00
/*******************************************************************
sync a file
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-11 05:24:30 +04:00
2000-05-10 05:31:46 +04:00
void sync_file ( connection_struct * conn , files_struct * fsp )
1998-08-17 11:40:06 +04:00
{
2002-07-15 14:35:28 +04:00
if ( lp_strict_sync ( SNUM ( conn ) ) & & fsp - > fd ! = - 1 ) {
flush_write_cache ( fsp , SYNC_FLUSH ) ;
2003-05-14 14:59:01 +04:00
SMB_VFS_FSYNC ( fsp , fsp - > fd ) ;
2002-07-15 14:35:28 +04:00
}
1998-08-17 11:40:06 +04:00
}
2002-03-20 03:46:53 +03:00
2002-07-15 14:35:28 +04:00
2002-03-20 03:46:53 +03:00
/************************************************************
Perform a stat whether a valid fd or not .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int fsp_stat ( files_struct * fsp , SMB_STRUCT_STAT * pst )
{
if ( fsp - > fd = = - 1 )
2003-05-14 14:59:01 +04:00
return SMB_VFS_STAT ( fsp - > conn , fsp - > fsp_name , pst ) ;
2002-03-20 03:46:53 +03:00
else
2003-05-14 14:59:01 +04:00
return SMB_VFS_FSTAT ( fsp , fsp - > fd , pst ) ;
2002-03-20 03:46:53 +03:00
}