@ -1624,11 +1624,31 @@ static void raw_close(BlockDriverState *bs)
}
}
/**
* Truncates the given regular file @ fd to @ offset and , when growing , fills the
* new space according to @ prealloc .
*
* Returns : 0 on success , - errno on failure .
*/
static int raw_regular_truncate ( int fd , int64_t offset , PreallocMode prealloc ,
Error * * errp )
{
int result = 0 ;
char * buf ;
int64_t current_length = 0 ;
char * buf = NULL ;
struct stat st ;
if ( fstat ( fd , & st ) < 0 ) {
result = - errno ;
error_setg_errno ( errp , - result , " Could not stat file " ) ;
return result ;
}
current_length = st . st_size ;
if ( current_length > offset & & prealloc ! = PREALLOC_MODE_OFF ) {
error_setg ( errp , " Cannot use preallocation for shrinking files " ) ;
return - ENOTSUP ;
}
switch ( prealloc ) {
# ifdef CONFIG_POSIX_FALLOCATE
@ -1638,17 +1658,17 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
* file systems that do not support fallocate ( ) , trying to check if a
* block is allocated before allocating it , so don ' t do that here .
*/
result = - posix_fallocate ( fd , 0 , offset ) ;
result = - posix_fallocate ( fd , current_length , offset - current_length ) ;
if ( result ! = 0 ) {
/* posix_fallocate() doesn't set errno. */
error_setg_errno ( errp , - result ,
" Could not preallocate data for the new file " ) ;
" Could not preallocate new data " ) ;
}
return resul t;
goto ou t;
# endif
case PREALLOC_MODE_FULL :
{
int64_t num = 0 , left = offset ;
int64_t num = 0 , left = offset - current_length ;
/*
* Knowing the final size from the beginning could allow the file
@ -1658,19 +1678,27 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
if ( ftruncate ( fd , offset ) ! = 0 ) {
result = - errno ;
error_setg_errno ( errp , - result , " Could not resize file " ) ;
return resul t;
goto ou t;
}
buf = g_malloc0 ( 65536 ) ;
result = lseek ( fd , current_length , SEEK_SET ) ;
if ( result < 0 ) {
result = - errno ;
error_setg_errno ( errp , - result ,
" Failed to seek to the old end of file " ) ;
goto out ;
}
while ( left > 0 ) {
num = MIN ( left , 65536 ) ;
result = write ( fd , buf , num ) ;
if ( result < 0 ) {
result = - errno ;
error_setg_errno ( errp , - result ,
" Could not write to the new file " ) ;
break ;
" Could not write zeros for preallocation " ) ;
goto out ;
}
left - = result ;
}
@ -1679,11 +1707,11 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
if ( result < 0 ) {
result = - errno ;
error_setg_errno ( errp , - result ,
" Could not flush new file to disk " ) ;
" Could not flush file to disk " ) ;
goto out ;
}
}
g_free ( buf ) ;
return result ;
goto out ;
}
case PREALLOC_MODE_OFF :
if ( ftruncate ( fd , offset ) ! = 0 ) {
@ -1697,6 +1725,17 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
PreallocMode_lookup [ prealloc ] ) ;
return result ;
}
out :
if ( result < 0 ) {
if ( ftruncate ( fd , current_length ) < 0 ) {
error_report ( " Failed to restore old file length: %s " ,
strerror ( errno ) ) ;
}
}
g_free ( buf ) ;
return result ;
}
static int raw_truncate ( BlockDriverState * bs , int64_t offset ,