2002-01-09 22:16:48 +03:00
/*
* Copyright ( C ) 2001 Sistina Software ( UK ) Limited .
*
* This file is released under the LGPL .
*/
# include "format-text.h"
# include "log.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/types.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
/*
* 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 .
*/
static int _split_vg ( const char * filename , char * vg , size_t vg_size ,
uint32_t * index )
{
int len , vg_len ;
char * dot , * underscore ;
len = strlen ( filename ) ;
if ( len < 7 )
return 0 ;
dot = ( char * ) ( filename + len - 3 ) ;
if ( strcmp ( " .vg " , dot ) )
return 0 ;
if ( ! ( underscore = rindex ( filename , ' _ ' ) ) )
return 0 ;
if ( sscanf ( underscore + 1 , " %u " , index ) ! = 1 )
return 0 ;
vg_len = underscore - filename ;
if ( vg_len + 1 > vg_size )
return 0 ;
strncpy ( vg , filename , vg_len ) ;
vg [ vg_len ] = ' \0 ' ;
return 1 ;
}
static void _insert_file ( struct list * head , struct archive_file * b )
{
struct list * bh ;
struct archive_file * bf ;
if ( list_empty ( head ) ) {
list_add ( head , & b - > list ) ;
return ;
}
/* index increases through list */
list_iterate ( bh , head ) {
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 ,
const char * vg , const char * dir )
2002-01-09 22:16:48 +03:00
{
2002-02-08 14:58:18 +03:00
int i , count , index ;
2002-01-09 22:16:48 +03:00
char vg_name [ 64 ] , * path ;
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-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 */
if ( ! _split_vg ( dirent [ i ] - > d_name , vg_name ,
2002-01-09 22:16:48 +03:00
sizeof ( vg_name ) , & index ) )
continue ;
2002-02-08 14:58:18 +03:00
/* is it the vg we're interested in ? */
if ( strcmp ( vg , vg_name ) )
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 ;
}
af - > index = index ;
af - > path = path ;
/*
* Insert it to the correct part of the list .
*/
_insert_file ( results , af ) ;
2002-01-09 22:16:48 +03:00
}
out :
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-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-02-08 14:58:18 +03:00
int i , fd ;
2002-01-09 22:16:48 +03:00
unsigned int index = 0 ;
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-02-08 14:58:18 +03:00
if ( ! text_vg_export ( fp , vg , desc ) ) {
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 ) )
index = 0 ;
else {
last = list_item ( archives - > p , struct archive_file ) ;
2002-01-09 22:16:48 +03:00
index = last - > index + 1 ;
}
for ( i = 0 ; i < 10 ; i + + ) {
if ( lvm_snprintf ( archive_name , sizeof ( archive_name ) ,
" %s/%s_%05d.vg " ,
2002-02-08 14:58:18 +03:00
dir , vg - > name , index ) < 0 ) {
2002-01-09 22:16:48 +03:00
log_err ( " archive file name too long. " ) ;
2002-02-08 14:58:18 +03:00
return 0 ;
2002-01-09 22:16:48 +03:00
}
2002-02-08 14:58:18 +03:00
if ( lvm_rename ( temp_file , archive_name ) )
2002-01-09 22:16:48 +03:00
break ;
index + + ;
}
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
static void _display_archive ( struct cmd_context * cmd , struct uuid_map * um ,
struct archive_file * af )
{
struct volume_group * vg ;
time_t when ;
char * desc ;
log_print ( " path: \t \t %s " , af - > path ) ;
/*
* Read the archive file to ensure that it is valid , and
* retrieve the archive time and description .
*/
if ( ! ( vg = text_vg_import ( cmd , af - > path , um , & when , & desc ) ) ) {
log_print ( " Unable to read archive file. " ) ;
return ;
}
log_print ( " description: \t %s " , desc ? desc : " <No description> " ) ;
log_print ( " time: \t \t %s " , ctime ( & when ) ) ;
pool_free ( cmd - > mem , vg ) ;
}
int archive_list ( struct cmd_context * cmd , struct uuid_map * um ,
const char * dir , const char * vg )
{
struct list * archives , * ah ;
struct archive_file * af ;
if ( ! ( archives = _scan_archive ( cmd - > mem , vg , dir ) ) ) {
log_err ( " Couldn't scan the archive directory (%s). " , dir ) ;
return 0 ;
}
if ( list_empty ( archives ) )
log_print ( " No archives found. " ) ;
list_iterate ( ah , archives ) {
af = list_item ( ah , struct archive_file ) ;
_display_archive ( cmd , um , af ) ;
log_print ( " " ) ;
}
pool_free ( cmd - > mem , archives ) ;
return 1 ;
}