2007-10-16 12:27:34 +04:00
/*
* Copyright ( C ) 2007 Jeff Dike ( jdike @ { linux . intel , addtoit } . com )
* Licensed under the GPL
*/
/*
* _XOPEN_SOURCE is needed for pread , but we define _GNU_SOURCE , which defines
2005-04-17 02:20:36 +04:00
* that .
*/
# include <unistd.h>
2007-10-16 12:27:34 +04:00
# include <errno.h>
# include <string.h>
# include <arpa/inet.h>
2012-04-06 07:35:03 +04:00
# include <endian.h>
2005-04-17 02:20:36 +04:00
# include "cow.h"
# include "cow_sys.h"
# define PATH_LEN_V1 256
2006-04-11 09:53:30 +04:00
typedef __u32 time32_t ;
2005-04-17 02:20:36 +04:00
struct cow_header_v1 {
2006-04-11 09:53:30 +04:00
__s32 magic ;
__s32 version ;
2005-04-17 02:20:36 +04:00
char backing_file [ PATH_LEN_V1 ] ;
2006-04-11 09:53:30 +04:00
time32_t mtime ;
2005-04-17 02:20:36 +04:00
__u64 size ;
2006-04-11 09:53:30 +04:00
__s32 sectorsize ;
} __attribute__ ( ( packed ) ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:34 +04:00
/*
* Define PATH_LEN_V3 as the usual value of MAXPATHLEN , just hard - code it in
2006-04-11 09:53:30 +04:00
* case other systems have different values for MAXPATHLEN .
*
* The same must hold for V2 - we want file format compatibility , not anything
* else .
*/
# define PATH_LEN_V3 4096
# define PATH_LEN_V2 PATH_LEN_V3
2005-04-17 02:20:36 +04:00
struct cow_header_v2 {
__u32 magic ;
__u32 version ;
char backing_file [ PATH_LEN_V2 ] ;
2006-04-11 09:53:30 +04:00
time32_t mtime ;
2005-04-17 02:20:36 +04:00
__u64 size ;
2006-04-11 09:53:30 +04:00
__s32 sectorsize ;
} __attribute__ ( ( packed ) ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:27:34 +04:00
/*
* Changes from V2 -
2005-04-17 02:20:36 +04:00
* PATH_LEN_V3 as described above
* Explicitly specify field bit lengths for systems with different
* lengths for the usual C types . Not sure whether char or
* time_t should be changed , this can be changed later without
* breaking compatibility
* Add alignment field so that different alignments can be used for the
* bitmap and data
* Add cow_format field to allow for the possibility of different ways
* of specifying the COW blocks . For now , the only value is 0 ,
* for the traditional COW bitmap .
* Move the backing_file field to the end of the header . This allows
* for the possibility of expanding it into the padding required
* by the bitmap alignment .
* The bitmap and data portions of the file will be aligned as specified
* by the alignment field . This is to allow COW files to be
* put on devices with restrictions on access alignments , such as
* / dev / raw , with a 512 byte alignment restriction . This also
* allows the data to be more aligned more strictly than on
* sector boundaries . This is needed for ubd - mmap , which needs
* the data to be page aligned .
* Fixed ( finally ! ) the rounding bug
*/
2007-10-16 12:27:34 +04:00
/*
* Until Dec2005 , __attribute__ ( ( packed ) ) was left out from the below
2006-04-11 09:53:30 +04:00
* definition , leading on 64 - bit systems to 4 bytes of padding after mtime , to
* align size to 8 - byte alignment . This shifted all fields above ( no padding
* was present on 32 - bit , no other padding was added ) .
*
* However , this _can be detected_ : it means that cow_format ( always 0 until
* now ) is shifted onto the first 4 bytes of backing_file , where it is otherwise
* impossible to find 4 zeros . - bb */
2005-04-17 02:20:36 +04:00
struct cow_header_v3 {
__u32 magic ;
__u32 version ;
__u32 mtime ;
__u64 size ;
__u32 sectorsize ;
__u32 alignment ;
__u32 cow_format ;
char backing_file [ PATH_LEN_V3 ] ;
2006-04-11 09:53:29 +04:00
} __attribute__ ( ( packed ) ) ;
2005-04-17 02:20:36 +04:00
2006-04-11 09:53:30 +04:00
/* This is the broken layout used by some 64-bit binaries. */
struct cow_header_v3_broken {
__u32 magic ;
__u32 version ;
__s64 mtime ;
__u64 size ;
__u32 sectorsize ;
__u32 alignment ;
__u32 cow_format ;
char backing_file [ PATH_LEN_V3 ] ;
2006-05-01 23:16:01 +04:00
} ;
2006-04-11 09:53:30 +04:00
2005-04-17 02:20:36 +04:00
/* COW format definitions - for now, we have only the usual COW bitmap */
# define COW_BITMAP 0
union cow_header {
struct cow_header_v1 v1 ;
struct cow_header_v2 v2 ;
struct cow_header_v3 v3 ;
2006-04-11 09:53:30 +04:00
struct cow_header_v3_broken v3_b ;
2005-04-17 02:20:36 +04:00
} ;
# define COW_MAGIC 0x4f4f4f4d /* MOOO */
# define COW_VERSION 3
# define DIV_ROUND(x, len) (((x) + (len) - 1) / (len))
# define ROUND_UP(x, align) DIV_ROUND(x, align) * (align)
void cow_sizes ( int version , __u64 size , int sectorsize , int align ,
int bitmap_offset , unsigned long * bitmap_len_out ,
int * data_offset_out )
{
2007-10-16 12:27:34 +04:00
if ( version < 3 ) {
2005-04-17 02:20:36 +04:00
* bitmap_len_out = ( size + sectorsize - 1 ) / ( 8 * sectorsize ) ;
* data_offset_out = bitmap_offset + * bitmap_len_out ;
* data_offset_out = ( * data_offset_out + sectorsize - 1 ) /
sectorsize ;
* data_offset_out * = sectorsize ;
}
else {
* bitmap_len_out = DIV_ROUND ( size , sectorsize ) ;
* bitmap_len_out = DIV_ROUND ( * bitmap_len_out , 8 ) ;
* data_offset_out = bitmap_offset + * bitmap_len_out ;
* data_offset_out = ROUND_UP ( * data_offset_out , align ) ;
}
}
static int absolutize ( char * to , int size , char * from )
{
char save_cwd [ 256 ] , * slash ;
int remaining ;
2007-10-16 12:27:34 +04:00
if ( getcwd ( save_cwd , sizeof ( save_cwd ) ) = = NULL ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " absolutize : unable to get cwd - errno = %d \n " ,
errno ) ;
2007-10-16 12:27:34 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
slash = strrchr ( from , ' / ' ) ;
2007-10-16 12:27:34 +04:00
if ( slash ! = NULL ) {
2005-04-17 02:20:36 +04:00
* slash = ' \0 ' ;
2007-10-16 12:27:34 +04:00
if ( chdir ( from ) ) {
2005-04-17 02:20:36 +04:00
* slash = ' / ' ;
cow_printf ( " absolutize : Can't cd to '%s' - "
" errno = %d \n " , from , errno ) ;
2007-10-16 12:27:34 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
* slash = ' / ' ;
2007-10-16 12:27:34 +04:00
if ( getcwd ( to , size ) = = NULL ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " absolutize : unable to get cwd of '%s' - "
" errno = %d \n " , from , errno ) ;
2007-10-16 12:27:34 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
remaining = size - strlen ( to ) ;
2007-10-16 12:27:34 +04:00
if ( strlen ( slash ) + 1 > remaining ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " absolutize : unable to fit '%s' into %d "
" chars \n " , from , size ) ;
2007-10-16 12:27:34 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
strcat ( to , slash ) ;
}
else {
2007-10-16 12:27:34 +04:00
if ( strlen ( save_cwd ) + 1 + strlen ( from ) + 1 > size ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " absolutize : unable to fit '%s' into %d "
" chars \n " , from , size ) ;
2007-10-16 12:27:34 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
strcpy ( to , save_cwd ) ;
strcat ( to , " / " ) ;
strcat ( to , from ) ;
}
2011-07-26 04:12:50 +04:00
if ( chdir ( save_cwd ) ) {
cow_printf ( " absolutize : Can't cd to '%s' - "
" errno = %d \n " , save_cwd , errno ) ;
return - 1 ;
}
2007-10-16 12:27:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
int write_cow_header ( char * cow_file , int fd , char * backing_file ,
int sectorsize , int alignment , unsigned long long * size )
{
struct cow_header_v3 * header ;
unsigned long modtime ;
int err ;
err = cow_seek_file ( fd , 0 ) ;
2007-10-16 12:27:34 +04:00
if ( err < 0 ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " write_cow_header - lseek failed, err = %d \n " , - err ) ;
goto out ;
}
err = - ENOMEM ;
header = cow_malloc ( sizeof ( * header ) ) ;
2007-10-16 12:27:34 +04:00
if ( header = = NULL ) {
cow_printf ( " write_cow_header - failed to allocate COW V3 "
" header \n " ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2012-04-06 07:35:03 +04:00
header - > magic = htobe32 ( COW_MAGIC ) ;
header - > version = htobe32 ( COW_VERSION ) ;
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
2007-10-16 12:27:34 +04:00
if ( strlen ( backing_file ) > sizeof ( header - > backing_file ) - 1 ) {
2006-04-11 09:53:31 +04:00
/* Below, %zd is for a size_t value */
2005-04-17 02:20:36 +04:00
cow_printf ( " Backing file name \" %s \" is too long - names are "
2006-04-11 09:53:31 +04:00
" limited to %zd characters \n " , backing_file ,
2005-04-17 02:20:36 +04:00
sizeof ( header - > backing_file ) - 1 ) ;
goto out_free ;
}
2007-10-16 12:27:34 +04:00
if ( absolutize ( header - > backing_file , sizeof ( header - > backing_file ) ,
2005-04-17 02:20:36 +04:00
backing_file ) )
goto out_free ;
err = os_file_modtime ( header - > backing_file , & modtime ) ;
2007-10-16 12:27:34 +04:00
if ( err < 0 ) {
2006-02-25 00:03:53 +03:00
cow_printf ( " write_cow_header - backing file '%s' mtime "
" request failed, err = %d \n " , header - > backing_file ,
- err ) ;
2005-04-17 02:20:36 +04:00
goto out_free ;
}
err = cow_file_size ( header - > backing_file , size ) ;
2007-10-16 12:27:34 +04:00
if ( err < 0 ) {
2006-02-25 00:03:53 +03:00
cow_printf ( " write_cow_header - couldn't get size of "
" backing file '%s', err = %d \n " ,
header - > backing_file , - err ) ;
2005-04-17 02:20:36 +04:00
goto out_free ;
}
2012-04-06 07:35:03 +04:00
header - > mtime = htobe32 ( modtime ) ;
header - > size = htobe64 ( * size ) ;
header - > sectorsize = htobe32 ( sectorsize ) ;
header - > alignment = htobe32 ( alignment ) ;
2005-04-17 02:20:36 +04:00
header - > cow_format = COW_BITMAP ;
2006-02-25 00:03:53 +03:00
err = cow_write_file ( fd , header , sizeof ( * header ) ) ;
2007-10-16 12:27:34 +04:00
if ( err ! = sizeof ( * header ) ) {
2006-02-25 00:03:53 +03:00
cow_printf ( " write_cow_header - write of header to "
" new COW file '%s' failed, err = %d \n " , cow_file ,
- err ) ;
2005-04-17 02:20:36 +04:00
goto out_free ;
}
err = 0 ;
out_free :
cow_free ( header ) ;
out :
2007-10-16 12:27:34 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
int file_reader ( __u64 offset , char * buf , int len , void * arg )
{
int fd = * ( ( int * ) arg ) ;
2007-10-16 12:27:34 +04:00
return pread ( fd , buf , len , offset ) ;
2005-04-17 02:20:36 +04:00
}
/* XXX Need to sanity-check the values read from the header */
int read_cow_header ( int ( * reader ) ( __u64 , char * , int , void * ) , void * arg ,
__u32 * version_out , char * * backing_file_out ,
time_t * mtime_out , unsigned long long * size_out ,
int * sectorsize_out , __u32 * align_out ,
int * bitmap_offset_out )
{
union cow_header * header ;
char * file ;
int err , n ;
unsigned long version , magic ;
header = cow_malloc ( sizeof ( * header ) ) ;
2007-10-16 12:27:34 +04:00
if ( header = = NULL ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " read_cow_header - Failed to allocate header \n " ) ;
2007-10-16 12:27:34 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
err = - EINVAL ;
n = ( * reader ) ( 0 , ( char * ) header , sizeof ( * header ) , arg ) ;
2007-10-16 12:27:34 +04:00
if ( n < offsetof ( typeof ( header - > v1 ) , backing_file ) ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " read_cow_header - short header \n " ) ;
goto out ;
}
magic = header - > v1 . magic ;
2007-10-16 12:27:34 +04:00
if ( magic = = COW_MAGIC )
2005-04-17 02:20:36 +04:00
version = header - > v1 . version ;
2012-04-06 07:35:03 +04:00
else if ( magic = = be32toh ( COW_MAGIC ) )
version = be32toh ( header - > v1 . version ) ;
2005-04-17 02:20:36 +04:00
/* No error printed because the non-COW case comes through here */
else goto out ;
* version_out = version ;
2007-10-16 12:27:34 +04:00
if ( version = = 1 ) {
if ( n < sizeof ( header - > v1 ) ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " read_cow_header - failed to read V1 "
" header \n " ) ;
goto out ;
}
* mtime_out = header - > v1 . mtime ;
* size_out = header - > v1 . size ;
* sectorsize_out = header - > v1 . sectorsize ;
* bitmap_offset_out = sizeof ( header - > v1 ) ;
* align_out = * sectorsize_out ;
file = header - > v1 . backing_file ;
}
2007-10-16 12:27:34 +04:00
else if ( version = = 2 ) {
if ( n < sizeof ( header - > v2 ) ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " read_cow_header - failed to read V2 "
" header \n " ) ;
goto out ;
}
2012-04-06 07:35:03 +04:00
* mtime_out = be32toh ( header - > v2 . mtime ) ;
* size_out = be64toh ( header - > v2 . size ) ;
* sectorsize_out = be32toh ( header - > v2 . sectorsize ) ;
2005-04-17 02:20:36 +04:00
* bitmap_offset_out = sizeof ( header - > v2 ) ;
* align_out = * sectorsize_out ;
file = header - > v2 . backing_file ;
}
2006-04-11 09:53:30 +04:00
/* This is very subtle - see above at union cow_header definition */
2007-10-16 12:27:34 +04:00
else if ( version = = 3 & & ( * ( ( int * ) header - > v3 . backing_file ) ! = 0 ) ) {
if ( n < sizeof ( header - > v3 ) ) {
2006-02-25 00:03:53 +03:00
cow_printf ( " read_cow_header - failed to read V3 "
2005-04-17 02:20:36 +04:00
" header \n " ) ;
goto out ;
}
2012-04-06 07:35:03 +04:00
* mtime_out = be32toh ( header - > v3 . mtime ) ;
* size_out = be64toh ( header - > v3 . size ) ;
* sectorsize_out = be32toh ( header - > v3 . sectorsize ) ;
* align_out = be32toh ( header - > v3 . alignment ) ;
2006-04-11 09:53:30 +04:00
if ( * align_out = = 0 ) {
cow_printf ( " read_cow_header - invalid COW header, "
" align == 0 \n " ) ;
}
2005-04-17 02:20:36 +04:00
* bitmap_offset_out = ROUND_UP ( sizeof ( header - > v3 ) , * align_out ) ;
file = header - > v3 . backing_file ;
}
2007-10-16 12:27:34 +04:00
else if ( version = = 3 ) {
2006-04-11 09:53:30 +04:00
cow_printf ( " read_cow_header - broken V3 file with "
" 64-bit layout - recovering content. \n " ) ;
2007-10-16 12:27:34 +04:00
if ( n < sizeof ( header - > v3_b ) ) {
2006-04-11 09:53:30 +04:00
cow_printf ( " read_cow_header - failed to read V3 "
" header \n " ) ;
goto out ;
}
2007-10-16 12:27:34 +04:00
/*
* this was used until Dec2005 - 64 bits are needed to represent
2006-04-11 09:53:30 +04:00
* 2038 + . I . e . we can safely do this truncating cast .
*
2012-04-06 07:35:03 +04:00
* Additionally , we must use be32toh ( ) instead of be64toh ( ) , since
2006-04-11 09:53:30 +04:00
* the program used to use the former ( tested - I got mtime
* mismatch " 0 vs whatever " ) .
*
* Ever heard about bug - to - bug - compatibility ? ; - ) */
2012-04-06 07:35:03 +04:00
* mtime_out = ( time32_t ) be32toh ( header - > v3_b . mtime ) ;
2006-04-11 09:53:30 +04:00
2012-04-06 07:35:03 +04:00
* size_out = be64toh ( header - > v3_b . size ) ;
* sectorsize_out = be32toh ( header - > v3_b . sectorsize ) ;
* align_out = be32toh ( header - > v3_b . alignment ) ;
2006-04-11 09:53:30 +04:00
if ( * align_out = = 0 ) {
cow_printf ( " read_cow_header - invalid COW header, "
" align == 0 \n " ) ;
}
* bitmap_offset_out = ROUND_UP ( sizeof ( header - > v3_b ) , * align_out ) ;
file = header - > v3_b . backing_file ;
}
2005-04-17 02:20:36 +04:00
else {
cow_printf ( " read_cow_header - invalid COW version \n " ) ;
goto out ;
}
err = - ENOMEM ;
* backing_file_out = cow_strdup ( file ) ;
2007-10-16 12:27:34 +04:00
if ( * backing_file_out = = NULL ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " read_cow_header - failed to allocate backing "
" file \n " ) ;
goto out ;
}
err = 0 ;
out :
cow_free ( header ) ;
2007-10-16 12:27:34 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
int init_cow_file ( int fd , char * cow_file , char * backing_file , int sectorsize ,
int alignment , int * bitmap_offset_out ,
unsigned long * bitmap_len_out , int * data_offset_out )
{
unsigned long long size , offset ;
char zero = 0 ;
int err ;
err = write_cow_header ( cow_file , fd , backing_file , sectorsize ,
alignment , & size ) ;
2007-10-16 12:27:34 +04:00
if ( err )
2005-04-17 02:20:36 +04:00
goto out ;
* bitmap_offset_out = ROUND_UP ( sizeof ( struct cow_header_v3 ) , alignment ) ;
cow_sizes ( COW_VERSION , size , sectorsize , alignment , * bitmap_offset_out ,
bitmap_len_out , data_offset_out ) ;
offset = * data_offset_out + size - sizeof ( zero ) ;
err = cow_seek_file ( fd , offset ) ;
2007-10-16 12:27:34 +04:00
if ( err < 0 ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " cow bitmap lseek failed : err = %d \n " , - err ) ;
goto out ;
}
2007-10-16 12:27:34 +04:00
/*
* does not really matter how much we write it is just to set EOF
2005-04-17 02:20:36 +04:00
* this also sets the entire COW bitmap
* to zero without having to allocate it
*/
err = cow_write_file ( fd , & zero , sizeof ( zero ) ) ;
2007-10-16 12:27:34 +04:00
if ( err ! = sizeof ( zero ) ) {
2005-04-17 02:20:36 +04:00
cow_printf ( " Write of bitmap to new COW file '%s' failed, "
" err = %d \n " , cow_file , - err ) ;
2006-02-25 00:03:58 +03:00
if ( err > = 0 )
err = - EINVAL ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2007-10-16 12:27:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
out :
2007-10-16 12:27:34 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}