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 ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2002-01-09 22:16:48 +03:00
*/
2002-11-18 17:04:08 +03:00
# include "lib.h"
2002-01-09 22:16:48 +03:00
# include "format-text.h"
# include "config.h"
# include "import-export.h"
# include "lvm-string.h"
# include "lvm-file.h"
2002-02-11 23:50:53 +03:00
# include "toolcontext.h"
2002-01-09 22:16:48 +03:00
# include <dirent.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/file.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
2010-08-03 17:09:21 +04:00
const char * path ;
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 ;
2003-09-15 19:03:22 +04:00
strncpy ( vgname , filename , vg_len ) ;
vgname [ vg_len ] = ' \0 ' ;
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
}
2006-04-19 19:33:07 +04:00
static char * _join_file_to_dir ( struct dm_pool * mem , const char * dir , const char * name )
2002-01-09 22:16:48 +03:00
{
2005-10-17 03:03:59 +04:00
if ( ! dm_pool_begin_object ( mem , 32 ) | |
! dm_pool_grow_object ( mem , dir , strlen ( dir ) ) | |
! dm_pool_grow_object ( mem , " / " , 1 ) | |
! dm_pool_grow_object ( mem , name , strlen ( name ) ) | |
2008-01-30 16:19:47 +03:00
! dm_pool_grow_object ( mem , " \0 " , 1 ) )
return_NULL ;
2002-01-09 22:16:48 +03:00
2005-10-17 03:03:59 +04:00
return dm_pool_end_object ( mem ) ;
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 ;
2003-09-15 19:03:22 +04:00
char vgname_found [ 64 ] , * path ;
2002-01-09 22:16:48 +03:00
struct dirent * * dirent ;
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
2002-05-07 16:47:11 +04:00
/* Sort fails beyond 5-digit indexes */
2002-02-08 14:58:18 +03:00
if ( ( count = scandir ( dir , & dirent , NULL , alphasort ) ) < 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 ;
2008-01-30 16:19:47 +03:00
if ( ! ( path = _join_file_to_dir ( mem , dir , dirent [ i ] - > d_name ) ) )
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 ;
2002-02-08 14:58:18 +03:00
af - > path = path ;
/*
* 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
}
2008-11-04 01:14:30 +03:00
static void _remove_expired ( 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 ;
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 ) {
2002-05-07 16:47:11 +04:00
/* Get the mtime of the file and unlink if too old */
if ( stat ( bf - > path , & sb ) ) {
log_sys_error ( " stat " , bf - > path ) ;
continue ;
}
2002-05-03 23:28:07 +04:00
2002-05-07 16:47:11 +04:00
if ( sb . st_mtime > retain_time )
return ;
2002-05-03 23:28:07 +04:00
2002-05-07 16:47:11 +04:00
log_very_verbose ( " Expiring archive %s " , bf - > path ) ;
if ( unlink ( bf - > path ) )
log_sys_error ( " unlink " , bf - > 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 )
return ;
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 ) ;
2008-11-04 01:14:30 +03:00
_remove_expired ( 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
2002-11-18 17:04:08 +03:00
static void _display_archive ( struct cmd_context * cmd , 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 ;
2011-03-11 17:45:17 +03:00
struct text_context tc = { . path_live = af - > path ,
. path_edit = NULL ,
. desc = NULL } ;
2002-02-11 14:43:17 +03:00
time_t when ;
char * desc ;
2003-09-15 19:03:22 +04:00
log_print ( " " ) ;
log_print ( " File: \t \t %s " , af - > path ) ;
2002-02-11 14:43:17 +03:00
2011-02-21 15:07:03 +03:00
fic . type = FMT_INSTANCE_VG | 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 */
2002-11-18 17:04:08 +03:00
if ( ! ( vg = text_vg_import_file ( tf , af - > 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
2010-12-08 23:50:48 +03:00
free_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 )
2002-11-18 17:04:08 +03:00
_display_archive ( cmd , 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 )
{
struct archive_file af ;
2010-08-03 17:09:21 +04:00
af . path = file ;
2007-06-09 02:38:48 +04:00
if ( ! path_exists ( af . path ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Archive file %s not found. " , af . path ) ;
2007-06-09 02:38:48 +04:00
return 0 ;
}
_display_archive ( cmd , & af ) ;
return 1 ;
}
2003-09-15 19:03:22 +04:00
int backup_list ( struct cmd_context * cmd , const char * dir , const char * vgname )
{
struct archive_file af ;
2008-01-30 16:19:47 +03:00
if ( ! ( af . path = _join_file_to_dir ( cmd - > mem , dir , vgname ) ) )
return_0 ;
2003-09-15 19:03:22 +04:00
if ( path_exists ( af . path ) )
_display_archive ( cmd , & af ) ;
return 1 ;
}