2002-01-09 22:16:48 +03:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2002-01-09 22:16:48 +03:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2004-03-30 23:35:44 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2002-01-09 22:16:48 +03:00
*/
2018-05-14 12:30:20 +03:00
# include "lib/misc/lib.h"
# include "lib/format_text/format-text.h"
2002-01-09 22:16:48 +03:00
2018-05-14 12:30:20 +03:00
# include "lib/config/config.h"
2002-01-09 22:16:48 +03:00
# include "import-export.h"
2018-05-14 12:30:20 +03:00
# include "lib/misc/lvm-string.h"
# include "lib/misc/lvm-file.h"
# include "lib/commands/toolcontext.h"
2002-01-09 22:16:48 +03:00
# include <dirent.h>
# include <unistd.h>
# include <sys/stat.h>
# include <fcntl.h>
2002-02-11 14:43:17 +03:00
# include <time.h>
2002-01-09 22:16:48 +03:00
2002-05-07 16:47:11 +04:00
# define SECS_PER_DAY 86400 /* 24*60*60 */
2002-05-03 23:28:07 +04:00
2002-01-09 22:16:48 +03:00
/*
* The format instance is given a directory path upon creation .
* Each file in this directory whose name is of the form
* ' ( . * ) _ [ 0 - 9 ] * . vg ' is a config file ( see lib / config . [ hc ] ) , which
* contains a description of a single volume group .
*
* The prefix ( $ 1 from the above regex ) of the config file gives
* the volume group name .
*
* Backup files that have expired will be removed .
*/
/*
2002-02-08 14:58:18 +03:00
* A list of these is built up for our volume group . Ordered
2002-01-09 22:16:48 +03:00
* with the least recent at the head .
*/
struct archive_file {
2008-11-04 01:14:30 +03:00
struct dm_list list ;
2002-01-09 22:16:48 +03:00
2021-10-01 17:19:53 +03:00
const char * name ;
2005-03-11 01:34:17 +03:00
uint32_t index ;
2002-01-09 22:16:48 +03:00
} ;
/*
* Extract vg name and version number from a filename .
*/
2009-07-26 16:40:27 +04:00
static int _split_vg ( const char * filename , char * vgname , size_t vgsize ,
2002-12-20 02:25:55 +03:00
uint32_t * ix )
2002-01-09 22:16:48 +03:00
{
2002-12-20 02:25:55 +03:00
size_t len , vg_len ;
const char * dot , * underscore ;
2002-01-09 22:16:48 +03:00
len = strlen ( filename ) ;
if ( len < 7 )
return 0 ;
2002-12-20 02:25:55 +03:00
dot = ( filename + len - 3 ) ;
2002-01-09 22:16:48 +03:00
if ( strcmp ( " .vg " , dot ) )
return 0 ;
2007-07-02 15:17:21 +04:00
if ( ! ( underscore = strrchr ( filename , ' _ ' ) ) )
2002-01-09 22:16:48 +03:00
return 0 ;
2002-12-20 02:25:55 +03:00
if ( sscanf ( underscore + 1 , " %u " , ix ) ! = 1 )
2002-01-09 22:16:48 +03:00
return 0 ;
vg_len = underscore - filename ;
2009-07-26 16:40:27 +04:00
if ( vg_len + 1 > vgsize )
2002-01-09 22:16:48 +03:00
return 0 ;
2018-03-02 18:25:37 +03:00
( void ) dm_strncpy ( vgname , filename , vg_len + 1 ) ;
2002-01-09 22:16:48 +03:00
return 1 ;
}
2008-11-04 01:14:30 +03:00
static void _insert_archive_file ( struct dm_list * head , struct archive_file * b )
2002-01-09 22:16:48 +03:00
{
2002-12-20 02:25:55 +03:00
struct archive_file * bf = NULL ;
2002-01-09 22:16:48 +03:00
2008-11-04 01:14:30 +03:00
if ( dm_list_empty ( head ) ) {
dm_list_add ( head , & b - > list ) ;
2002-01-09 22:16:48 +03:00
return ;
}
2005-06-01 20:51:55 +04:00
/* index reduces through list */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( bf , head ) {
2005-06-01 20:51:55 +04:00
if ( b - > index > bf - > index ) {
2008-11-04 01:14:30 +03:00
dm_list_add ( & bf - > list , & b - > list ) ;
2002-01-09 22:16:48 +03:00
return ;
}
}
2008-11-04 01:14:30 +03:00
dm_list_add_h ( & bf - > list , & b - > list ) ;
2002-01-09 22:16:48 +03:00
}
2002-02-08 14:58:18 +03:00
/*
* Returns a list of archive_files .
*/
2008-11-04 01:14:30 +03:00
static struct dm_list * _scan_archive ( struct dm_pool * mem ,
2003-09-15 19:03:22 +04:00
const char * vgname , const char * dir )
2002-01-09 22:16:48 +03:00
{
2005-03-11 01:34:17 +03:00
int i , count ;
uint32_t ix ;
2021-10-01 17:19:53 +03:00
char vgname_found [ 64 ] , * name ;
2021-09-10 23:32:44 +03:00
struct dirent * * dirent = NULL ;
2002-02-08 14:58:18 +03:00
struct archive_file * af ;
2008-11-04 01:14:30 +03:00
struct dm_list * results ;
2002-02-08 14:58:18 +03:00
2008-01-30 16:19:47 +03:00
if ( ! ( results = dm_pool_alloc ( mem , sizeof ( * results ) ) ) )
return_NULL ;
2002-02-08 14:58:18 +03:00
2008-11-04 01:14:30 +03:00
dm_list_init ( results ) ;
2002-01-09 22:16:48 +03:00
2021-09-25 00:04:37 +03:00
# ifndef HAVE_VERSIONSORT
/* fallback to alphasort when versionsort is not defined */
# define versionsort alphasort
# endif /* !HAVE_VERSIONSORT */
2018-02-09 04:08:55 +03:00
/* Use versionsort to handle numbers beyond 5 digits */
if ( ( count = scandir ( dir , & dirent , NULL , versionsort ) ) < 0 ) {
2009-07-16 00:02:46 +04:00
log_error ( " Couldn't scan the archive directory (%s). " , dir ) ;
2002-01-09 22:16:48 +03:00
return 0 ;
}
for ( i = 0 ; i < count ; i + + ) {
2005-06-06 22:16:33 +04:00
if ( ! strcmp ( dirent [ i ] - > d_name , " . " ) | |
! strcmp ( dirent [ i ] - > d_name , " .. " ) )
2002-02-08 14:58:18 +03:00
continue ;
/* check the name is the correct format */
2003-09-15 19:03:22 +04:00
if ( ! _split_vg ( dirent [ i ] - > d_name , vgname_found ,
sizeof ( vgname_found ) , & ix ) )
2002-01-09 22:16:48 +03:00
continue ;
2002-02-08 14:58:18 +03:00
/* is it the vg we're interested in ? */
2003-09-15 19:03:22 +04:00
if ( strcmp ( vgname , vgname_found ) )
2002-02-08 14:58:18 +03:00
continue ;
2021-10-01 17:19:53 +03:00
if ( ! ( name = dm_pool_strdup ( mem , dirent [ i ] - > d_name ) ) )
2008-01-30 16:19:47 +03:00
goto_out ;
2002-01-09 22:16:48 +03:00
2002-02-08 14:58:18 +03:00
/*
* Create a new archive_file .
*/
2005-10-17 03:03:59 +04:00
if ( ! ( af = dm_pool_alloc ( mem , sizeof ( * af ) ) ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Couldn't create new archive file. " ) ;
2002-02-08 14:58:18 +03:00
results = NULL ;
goto out ;
}
2002-12-20 02:25:55 +03:00
af - > index = ix ;
2021-10-01 17:19:53 +03:00
af - > name = name ;
2002-02-08 14:58:18 +03:00
/*
* Insert it to the correct part of the list .
*/
2006-04-19 19:33:07 +04:00
_insert_archive_file ( results , af ) ;
2002-01-09 22:16:48 +03:00
}
2002-04-24 22:20:51 +04:00
out :
2002-01-09 22:16:48 +03:00
for ( i = 0 ; i < count ; i + + )
free ( dirent [ i ] ) ;
free ( dirent ) ;
2002-02-08 14:58:18 +03:00
return results ;
2002-01-09 22:16:48 +03:00
}
2021-10-01 17:19:53 +03:00
static void _remove_expired ( const char * dir , const char * vgname ,
struct dm_list * archives , uint32_t archives_size ,
2002-05-07 16:47:11 +04:00
uint32_t retain_days , uint32_t min_archive )
2002-05-03 23:28:07 +04:00
{
struct archive_file * bf ;
struct stat sb ;
2002-05-07 16:47:11 +04:00
time_t retain_time ;
2021-10-01 17:19:53 +03:00
uint64_t sum = 0 ;
char path [ PATH_MAX ] ;
2002-05-03 23:28:07 +04:00
/* Make sure there are enough archives to even bother looking for
* expired ones . . . */
2002-05-07 16:47:11 +04:00
if ( archives_size < = min_archive )
return ;
2002-05-03 23:28:07 +04:00
2002-05-07 16:47:11 +04:00
/* Convert retain_days into the time after which we must retain */
2002-11-18 17:04:08 +03:00
retain_time = time ( NULL ) - ( time_t ) retain_days * SECS_PER_DAY ;
2002-05-03 23:28:07 +04:00
2006-03-07 18:43:05 +03:00
/* Assume list is ordered newest first (by index) */
2008-11-04 01:14:30 +03:00
dm_list_iterate_back_items ( bf , archives ) {
2021-10-01 17:19:53 +03:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/%s " , dir , bf - > name ) < 0 )
continue ;
2002-05-07 16:47:11 +04:00
/* Get the mtime of the file and unlink if too old */
2021-10-01 17:19:53 +03:00
if ( stat ( path , & sb ) ) {
log_sys_debug ( " stat " , path ) ;
2002-05-07 16:47:11 +04:00
continue ;
}
2002-05-03 23:28:07 +04:00
2021-10-01 17:19:53 +03:00
sum + = sb . st_size ;
2002-05-07 16:47:11 +04:00
if ( sb . st_mtime > retain_time )
2021-10-01 17:19:53 +03:00
continue ;
2002-05-03 23:28:07 +04:00
2021-10-01 17:19:53 +03:00
log_very_verbose ( " Expiring archive %s " , path ) ;
if ( unlink ( path ) )
log_sys_debug ( " unlink " , path ) ;
2002-05-03 23:28:07 +04:00
2002-05-07 16:47:11 +04:00
/* Don't delete any more if we've reached the minimum */
if ( - - archives_size < = min_archive )
2021-10-01 17:19:53 +03:00
break ;
2002-05-03 23:28:07 +04:00
}
2021-10-01 17:19:53 +03:00
sum / = 1024 * 1024 ;
if ( sum > 128 | | archives_size > 8192 )
log_print_unless_silent ( " Consider prunning %s VG archive with more then %u MiB in %u files (check archiving is needed in lvm.conf). " ,
vgname , ( unsigned ) sum , archives_size ) ;
2002-05-03 23:28:07 +04:00
}
2002-02-08 14:58:18 +03:00
int archive_vg ( struct volume_group * vg ,
const char * dir , const char * desc ,
uint32_t retain_days , uint32_t min_archive )
2002-01-09 22:16:48 +03:00
{
2010-09-09 17:13:12 +04:00
int i , fd , rnum , renamed = 0 ;
2005-03-11 01:34:17 +03:00
uint32_t ix = 0 ;
2002-01-09 22:16:48 +03:00
struct archive_file * last ;
FILE * fp = NULL ;
char temp_file [ PATH_MAX ] , archive_name [ PATH_MAX ] ;
2008-11-04 01:14:30 +03:00
struct dm_list * archives ;
2002-01-09 22:16:48 +03:00
2002-02-08 14:58:18 +03:00
/*
* Write the vg out to a temporary file .
*/
2008-12-07 07:27:56 +03:00
if ( ! create_temp_name ( dir , temp_file , sizeof ( temp_file ) , & fd ,
& vg - > cmd - > rand_seed ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Couldn't create temporary archive name. " ) ;
2002-01-09 22:16:48 +03:00
return 0 ;
}
if ( ! ( fp = fdopen ( fd , " w " ) ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Couldn't create FILE object for archive. " ) ;
2007-01-25 17:37:48 +03:00
if ( close ( fd ) )
log_sys_error ( " close " , temp_file ) ;
2002-01-09 22:16:48 +03:00
return 0 ;
}
2002-11-18 17:04:08 +03:00
if ( ! text_vg_export_file ( vg , desc , fp ) ) {
2007-01-25 17:37:48 +03:00
if ( fclose ( fp ) )
log_sys_error ( " fclose " , temp_file ) ;
2008-01-30 16:19:47 +03:00
return_0 ;
2002-01-09 22:16:48 +03:00
}
2007-07-24 21:48:08 +04:00
if ( lvm_fclose ( fp , temp_file ) )
return_0 ; /* Leave file behind as evidence of failure */
2002-01-09 22:16:48 +03:00
/*
* Now we want to rename this file to < vg > _index . vg .
*/
2008-01-30 16:19:47 +03:00
if ( ! ( archives = _scan_archive ( vg - > cmd - > mem , vg - > name , dir ) ) )
return_0 ;
2002-01-09 22:16:48 +03:00
2008-11-04 01:14:30 +03:00
if ( dm_list_empty ( archives ) )
2002-12-20 02:25:55 +03:00
ix = 0 ;
2002-02-08 14:58:18 +03:00
else {
2008-11-04 01:14:30 +03:00
last = dm_list_item ( dm_list_first ( archives ) , struct archive_file ) ;
2002-12-20 02:25:55 +03:00
ix = last - > index + 1 ;
2002-01-09 22:16:48 +03:00
}
2010-09-09 17:13:12 +04:00
rnum = rand_r ( & vg - > cmd - > rand_seed ) ;
2002-01-09 22:16:48 +03:00
for ( i = 0 ; i < 10 ; i + + ) {
2006-08-21 16:54:53 +04:00
if ( dm_snprintf ( archive_name , sizeof ( archive_name ) ,
2010-09-09 17:13:12 +04:00
" %s/%s_%05u-%d.vg " ,
dir , vg - > name , ix , rnum ) < 0 ) {
2002-05-07 16:47:11 +04:00
log_error ( " Archive file name too long. " ) ;
2002-02-08 14:58:18 +03:00
return 0 ;
2002-01-09 22:16:48 +03:00
}
2002-05-07 16:47:11 +04:00
if ( ( renamed = lvm_rename ( temp_file , archive_name ) ) )
2002-01-09 22:16:48 +03:00
break ;
2002-12-20 02:25:55 +03:00
ix + + ;
2002-01-09 22:16:48 +03:00
}
2002-05-07 16:47:11 +04:00
if ( ! renamed )
log_error ( " Archive rename failed for %s " , temp_file ) ;
2021-10-01 17:19:53 +03:00
_remove_expired ( dir , vg - > name , archives , dm_list_size ( archives ) + renamed , retain_days ,
2002-05-07 16:47:11 +04:00
min_archive ) ;
2002-05-03 23:28:07 +04:00
2002-02-08 14:58:18 +03:00
return 1 ;
2002-01-09 22:16:48 +03:00
}
2002-02-11 14:43:17 +03:00
2021-10-01 17:19:53 +03:00
static void _display_archive ( struct cmd_context * cmd , const char * dir , struct archive_file * af )
2002-02-11 14:43:17 +03:00
{
2002-04-24 22:20:51 +04:00
struct volume_group * vg = NULL ;
struct format_instance * tf ;
2011-02-21 15:07:03 +03:00
struct format_instance_ctx fic ;
2021-10-01 17:19:53 +03:00
struct text_context tc = { NULL } ;
char path [ PATH_MAX ] ;
2002-02-11 14:43:17 +03:00
time_t when ;
char * desc ;
2021-10-01 17:19:53 +03:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/%s " , dir , af - > name ) < 0 ) {
log_debug ( " Created path %s/%s is too long. " , dir , af - > name ) ;
return ;
}
2003-09-15 19:03:22 +04:00
log_print ( " " ) ;
2021-10-01 17:19:53 +03:00
log_print ( " File: \t \t %s/%s " , path , af - > name ) ;
tc . path_live = path ;
2002-02-11 14:43:17 +03:00
2012-02-13 03:01:19 +04:00
fic . type = FMT_INSTANCE_PRIVATE_MDAS ;
2011-03-11 17:45:17 +03:00
fic . context . private = & tc ;
if ( ! ( tf = cmd - > fmt_backup - > ops - > create_instance ( cmd - > fmt_backup , & fic ) ) ) {
2002-04-24 22:20:51 +04:00
log_error ( " Couldn't create text instance object. " ) ;
return ;
}
2002-02-11 14:43:17 +03:00
/*
* Read the archive file to ensure that it is valid , and
* retrieve the archive time and description .
*/
2002-04-24 22:20:51 +04:00
/* FIXME Use variation on _vg_read */
2021-10-01 17:19:53 +03:00
if ( ! ( vg = text_read_metadata_file ( tf , path , & when , & desc ) ) ) {
2009-12-03 22:18:33 +03:00
log_error ( " Unable to read archive file. " ) ;
2002-04-24 22:20:51 +04:00
tf - > fmt - > ops - > destroy_instance ( tf ) ;
2002-02-11 14:43:17 +03:00
return ;
}
2003-09-15 19:03:22 +04:00
log_print ( " VG name: \t %s " , vg - > name ) ;
2006-05-10 01:23:51 +04:00
log_print ( " Description: \t %s " , desc ? : " <No description> " ) ;
2003-09-15 19:03:22 +04:00
log_print ( " Backup Time: \t %s " , ctime ( & when ) ) ;
2002-02-11 14:43:17 +03:00
2011-08-11 00:25:29 +04:00
release_vg ( vg ) ;
2002-02-11 14:43:17 +03:00
}
2003-09-15 19:03:22 +04:00
int archive_list ( struct cmd_context * cmd , const char * dir , const char * vgname )
2002-02-11 14:43:17 +03:00
{
2008-11-04 01:14:30 +03:00
struct dm_list * archives ;
2002-02-11 14:43:17 +03:00
struct archive_file * af ;
2008-01-30 16:19:47 +03:00
if ( ! ( archives = _scan_archive ( cmd - > mem , vgname , dir ) ) )
return_0 ;
2002-02-11 14:43:17 +03:00
2008-11-04 01:14:30 +03:00
if ( dm_list_empty ( archives ) )
2003-09-15 19:03:22 +04:00
log_print ( " No archives found in %s. " , dir ) ;
2002-02-11 14:43:17 +03:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_back_items ( af , archives )
2021-10-01 17:19:53 +03:00
_display_archive ( cmd , dir , af ) ;
2002-02-11 14:43:17 +03:00
2005-10-17 03:03:59 +04:00
dm_pool_free ( cmd - > mem , archives ) ;
2002-02-11 14:43:17 +03:00
return 1 ;
}
2003-09-15 19:03:22 +04:00
2007-06-09 02:38:48 +04:00
int archive_list_file ( struct cmd_context * cmd , const char * file )
{
2021-10-01 17:19:53 +03:00
struct archive_file af = { 0 } ;
char path [ PATH_MAX ] ;
size_t len ;
2007-06-09 02:38:48 +04:00
2021-10-01 17:19:53 +03:00
if ( ! path_exists ( file ) ) {
log_error ( " Archive file %s not found. " , file ) ;
return 0 ;
}
2007-06-09 02:38:48 +04:00
2021-10-01 17:19:53 +03:00
if ( ! ( af . name = strrchr ( file , ' / ' ) ) ) {
log_error ( " No '/' in file path %s found. " , file ) ;
2007-06-09 02:38:48 +04:00
return 0 ;
}
2021-10-01 17:19:53 +03:00
len = ( size_t ) ( af . name - file ) ;
if ( len > = sizeof ( path ) ) {
log_error ( INTERNAL_ERROR " Passed file path name %s is too long. " , file ) ;
return 0 ;
}
memcpy ( path , file , len ) ;
path [ len ] = 0 ;
af . name + + ; /* jump over '/' */
_display_archive ( cmd , path , & af ) ;
2007-06-09 02:38:48 +04:00
return 1 ;
}
2003-09-15 19:03:22 +04:00
int backup_list ( struct cmd_context * cmd , const char * dir , const char * vgname )
{
2021-10-01 17:19:53 +03:00
struct archive_file af = { . name = vgname } ;
char path [ PATH_MAX ] ;
2003-09-15 19:03:22 +04:00
2021-10-01 17:19:53 +03:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/%s " , dir , vgname ) < 0 )
2008-01-30 16:19:47 +03:00
return_0 ;
2003-09-15 19:03:22 +04:00
2021-10-01 17:19:53 +03:00
if ( path_exists ( path ) )
_display_archive ( cmd , dir , & af ) ;
2003-09-15 19:03:22 +04:00
return 1 ;
}