2002-01-09 22:16:48 +03:00
/*
* Copyright ( C ) 2001 Sistina Software ( UK ) Limited .
*
* This file is released under the LGPL .
*/
2002-11-18 17:04:08 +03:00
# include "lib.h"
2002-01-09 22:16:48 +03:00
# include "format-text.h"
# include "pool.h"
# include "config.h"
# include "hash.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 {
struct list list ;
char * path ;
int index ;
} ;
/*
* Extract vg name and version number from a filename .
*/
2003-09-15 19:03:22 +04:00
static int _split_vg ( const char * filename , char * vgname , size_t vg_size ,
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 ;
if ( ! ( underscore = rindex ( filename , ' _ ' ) ) )
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 ;
if ( vg_len + 1 > vg_size )
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 ;
}
static void _insert_file ( struct list * head , struct archive_file * b )
{
struct list * bh ;
2002-12-20 02:25:55 +03:00
struct archive_file * bf = NULL ;
2002-01-09 22:16:48 +03:00
if ( list_empty ( head ) ) {
list_add ( head , & b - > list ) ;
return ;
}
/* index increases through list */
2002-04-24 22:20:51 +04:00
list_iterate ( bh , head ) {
2002-01-09 22:16:48 +03:00
bf = list_item ( bh , struct archive_file ) ;
if ( bf - > index > b - > index ) {
list_add ( & bf - > list , & b - > list ) ;
return ;
}
}
list_add_h ( & bf - > list , & b - > list ) ;
}
static char * _join ( struct pool * mem , const char * dir , const char * name )
{
if ( ! pool_begin_object ( mem , 32 ) | |
! pool_grow_object ( mem , dir , strlen ( dir ) ) | |
! pool_grow_object ( mem , " / " , 1 ) | |
! pool_grow_object ( mem , name , strlen ( name ) ) | |
! pool_grow_object ( mem , " \0 " , 1 ) ) {
stack ;
return NULL ;
}
return pool_end_object ( mem ) ;
}
2002-02-08 14:58:18 +03:00
/*
* Returns a list of archive_files .
*/
static struct list * _scan_archive ( struct pool * mem ,
2003-09-15 19:03:22 +04:00
const char * vgname , const char * dir )
2002-01-09 22:16:48 +03:00
{
2002-12-20 02:25:55 +03:00
int i , count , 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 ;
struct list * results ;
if ( ! ( results = pool_alloc ( mem , sizeof ( * results ) ) ) ) {
stack ;
return NULL ;
}
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 ) {
2002-01-09 22:16:48 +03:00
log_err ( " Couldn't scan archive directory. " ) ;
return 0 ;
}
for ( i = 0 ; i < count ; i + + ) {
2002-02-08 14:58:18 +03:00
/* ignore dot files */
if ( dirent [ i ] - > d_name [ 0 ] = = ' . ' )
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 ;
if ( ! ( path = _join ( mem , dir , dirent [ i ] - > d_name ) ) ) {
2002-01-09 22:16:48 +03:00
stack ;
goto out ;
}
2002-02-08 14:58:18 +03:00
/*
* Create a new archive_file .
*/
if ( ! ( af = pool_alloc ( mem , sizeof ( * af ) ) ) ) {
log_err ( " Couldn't create new archive file. " ) ;
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 .
*/
_insert_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
}
2002-05-07 16:47:11 +04:00
static void _remove_expired ( struct list * archives , uint32_t archives_size ,
uint32_t retain_days , uint32_t min_archive )
2002-05-03 23:28:07 +04:00
{
struct list * bh ;
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
2002-05-07 16:47:11 +04:00
/* Assume list is ordered oldest first (by index) */
list_iterate ( bh , archives ) {
bf = list_item ( bh , struct archive_file ) ;
2002-05-03 23:28:07 +04:00
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
{
2002-05-07 16:47:11 +04:00
int i , fd , renamed = 0 ;
2002-12-20 02:25:55 +03:00
unsigned int 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 ] ;
2002-02-08 14:58:18 +03:00
struct 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 .
*/
if ( ! create_temp_name ( dir , temp_file , sizeof ( temp_file ) , & fd ) ) {
2002-01-09 22:16:48 +03:00
log_err ( " Couldn't create temporary archive name. " ) ;
return 0 ;
}
if ( ! ( fp = fdopen ( fd , " w " ) ) ) {
log_err ( " Couldn't create FILE object for archive. " ) ;
close ( fd ) ;
return 0 ;
}
2002-11-18 17:04:08 +03:00
if ( ! text_vg_export_file ( vg , desc , fp ) ) {
2002-01-09 22:16:48 +03:00
stack ;
fclose ( fp ) ;
return 0 ;
}
fclose ( fp ) ;
/*
* Now we want to rename this file to < vg > _index . vg .
*/
2002-02-08 14:58:18 +03:00
if ( ! ( archives = _scan_archive ( vg - > cmd - > mem , vg - > name , dir ) ) ) {
log_err ( " Couldn't scan the archive directory (%s). " , dir ) ;
return 0 ;
2002-01-09 22:16:48 +03:00
}
2002-02-08 14:58:18 +03:00
if ( list_empty ( archives ) )
2002-12-20 02:25:55 +03:00
ix = 0 ;
2002-02-08 14:58:18 +03:00
else {
last = list_item ( archives - > p , struct archive_file ) ;
2002-12-20 02:25:55 +03:00
ix = last - > index + 1 ;
2002-01-09 22:16:48 +03:00
}
for ( i = 0 ; i < 10 ; i + + ) {
if ( lvm_snprintf ( archive_name , sizeof ( archive_name ) ,
2002-12-20 02:25:55 +03:00
" %s/%s_%05d.vg " , dir , vg - > name , ix ) < 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 ) ;
_remove_expired ( archives , list_size ( archives ) + renamed , retain_days ,
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 ;
2002-02-11 14:43:17 +03:00
time_t when ;
char * desc ;
2002-04-24 22:20:51 +04:00
void * context ;
2002-02-11 14:43:17 +03:00
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
2002-11-18 17:04:08 +03:00
if ( ! ( context = create_text_context ( cmd , af - > path , NULL ) ) | |
! ( tf = cmd - > fmt_backup - > ops - > create_instance ( cmd - > fmt_backup , NULL ,
context ) ) ) {
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 ) ) ) {
2002-02-11 14:43:17 +03:00
log_print ( " 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 ) ;
log_print ( " Description: \t %s " , desc ? desc : " <No description> " ) ;
log_print ( " Backup Time: \t %s " , ctime ( & when ) ) ;
2002-02-11 14:43:17 +03:00
pool_free ( cmd - > mem , vg ) ;
2002-04-24 22:20:51 +04:00
tf - > fmt - > ops - > destroy_instance ( tf ) ;
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
{
struct list * archives , * ah ;
struct archive_file * af ;
2003-09-15 19:03:22 +04:00
if ( ! ( archives = _scan_archive ( cmd - > mem , vgname , dir ) ) ) {
2002-02-11 14:43:17 +03:00
log_err ( " Couldn't scan the archive directory (%s). " , dir ) ;
return 0 ;
}
if ( 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
2002-04-24 22:20:51 +04:00
list_iterate ( ah , archives ) {
2002-02-11 14:43:17 +03:00
af = list_item ( ah , struct archive_file ) ;
2002-11-18 17:04:08 +03:00
_display_archive ( cmd , af ) ;
2002-02-11 14:43:17 +03:00
}
pool_free ( cmd - > mem , archives ) ;
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 ;
if ( ! ( af . path = _join ( cmd - > mem , dir , vgname ) ) ) {
stack ;
return 0 ;
}
if ( path_exists ( af . path ) )
_display_archive ( cmd , & af ) ;
return 1 ;
}