1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-10-26 17:25:10 +03:00

o Rename many occurrences of 'backup' to 'archive' to reduce confusion.

o Extract file creation/renaming code into a library and change backup code
  to use it too.
o Support umask.
o Bring lvm.conf man page up-to-date.
This commit is contained in:
Alasdair Kergon 2002-01-09 19:16:48 +00:00
parent 4bbf2c3418
commit 952d12a5f5
13 changed files with 532 additions and 34 deletions

View File

@ -20,6 +20,7 @@
../lib/mm/dbg_malloc.h
../lib/mm/pool.h
../lib/mm/xlate.h
../lib/misc/lvm-file.h
../lib/misc/lvm-string.h
../lib/regex/matcher.h
../lib/uuid/uuid.h

View File

@ -29,7 +29,7 @@ SOURCES=\
format1/import-extents.c \
format1/layout.c \
format1/vg_number.c \
format_text/backup.c \
format_text/archive.c \
format_text/export.c \
format_text/flags.c \
format_text/format-text.c \
@ -40,6 +40,7 @@ SOURCES=\
metadata/merge.c \
metadata/metadata.c \
metadata/pv_map.c \
misc/lvm-file.c \
mm/dbg_malloc.c \
mm/pool.c \
regex/matcher.c \

421
lib/format_text/archive.c Normal file
View File

@ -0,0 +1,421 @@
/*
* 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"
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
/*
* 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.
*/
struct archive_c {
uint32_t retain_days;
uint32_t min_retains;
char *dir;
/*
* An ordered list of previous archives. Each list
* entered against the vg name. Most recent first.
*/
struct hash_table *vg_archives;
/*
* Scratch pool. Contents of vg_archives come from here.
*/
struct pool *mem;
};
/*
* A list of these is built up for each volume group. Ordered
* with the least recent at the head.
*/
struct archive_file {
struct list list;
char *path;
char *vg;
int index;
};
/*
* This format is write only.
*/
static void _unsupported(const char *cmd)
{
log_err("The archive format doesn't support '%s'", cmd);
}
static struct list *_get_vgs(struct format_instance *fi)
{
_unsupported("get_vgs");
return NULL;
}
static struct list *_get_pvs(struct format_instance *fi)
{
_unsupported("get_pvs");
return NULL;
}
static struct physical_volume *_pv_read(struct format_instance *fi,
const char *pv_name)
{
_unsupported("pv_read");
return NULL;
}
static int _pv_setup(struct format_instance *fi, struct physical_volume *pv,
struct volume_group *vg)
{
_unsupported("pv_setup");
return 0;
}
static int _pv_write(struct format_instance *fi, struct physical_volume *pv)
{
_unsupported("pv_write");
return 0;
}
static int _vg_setup(struct format_instance *fi, struct volume_group *vg)
{
_unsupported("vg_setup");
return 0;
}
static struct volume_group *_vg_read(struct format_instance *fi,
const char *vg_name)
{
_unsupported("vg_read");
return NULL;
}
static void _destroy(struct format_instance *fi)
{
struct archive_c *bc = (struct archive_c *) fi->private;
if (bc->vg_archives)
hash_destroy(bc->vg_archives);
pool_destroy(bc->mem);
}
/*
* 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 int _scan_vg(struct archive_c *bc, const char *file,
const char *vg_name, int index)
{
struct archive_file *b;
struct list *files;
/*
* Do we need to create a new list of archive files for
* this vg ?
*/
if (!(files = hash_lookup(bc->vg_archives, vg_name))) {
if (!(files = pool_alloc(bc->mem, sizeof(*files)))) {
stack;
return 0;
}
list_init(files);
if (!hash_insert(bc->vg_archives, vg_name, files)) {
log_err("Couldn't insert archive file "
"into hash table.");
return 0;
}
}
/*
* Create a new archive file.
*/
if (!(b = pool_alloc(bc->mem, sizeof(*b)))) {
log_err("Couldn't create new archive file.");
return 0;
}
b->index = index;
b->path = (char *)file;
b->vg = (char *)vg_name;
/*
* Insert it to the correct part of the list.
*/
_insert_file(files, b);
return 1;
}
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);
}
static int _scan_dir(struct archive_c *bc)
{
int r = 0, i, count, index;
char vg_name[64], *path;
struct dirent **dirent;
if ((count = scandir(bc->dir, &dirent, NULL, alphasort)) < 0) {
log_err("Couldn't scan archive directory.");
return 0;
}
for (i = 0; i < count; i++) {
if ((dirent[i]->d_name[0] == '.') ||
!_split_vg(dirent[i]->d_name, vg_name,
sizeof(vg_name), &index))
continue;
if (!(path = _join(bc->mem, bc->dir, dirent[i]->d_name))) {
stack;
goto out;
}
_scan_vg(bc, path, vg_name, index);
}
r = 1;
out:
for (i = 0; i < count; i++)
free(dirent[i]);
free(dirent);
return r;
}
static int _scan_archives(struct archive_c *bc)
{
pool_empty(bc->mem);
if (bc->vg_archives)
hash_destroy(bc->vg_archives);
if (!(bc->vg_archives = hash_create(128))) {
log_err("Couldn't create hash table for scanning archives.");
return 0;
}
if (!_scan_dir(bc)) {
stack;
return 0;
}
return 1;
}
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
{
int r = 0, i, fd;
unsigned int index = 0;
struct archive_c *bc = (struct archive_c *) fi->private;
struct archive_file *last;
FILE *fp = NULL;
char temp_file[PATH_MAX], archive_name[PATH_MAX];
if (!create_temp_name(bc->dir, temp_file, sizeof(temp_file), &fd)) {
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;
}
if (!text_vg_export(fp, vg)) {
stack;
fclose(fp);
return 0;
}
fclose(fp);
/*
* Now we want to rename this file to <vg>_index.vg.
*/
if (!_scan_archives(bc)) {
log_err("Couldn't scan the archive directory (%s).", bc->dir);
goto out;
}
if ((last = (struct archive_file *) hash_lookup(bc->vg_archives,
vg->name))) {
/* move to the last in the list */
last = list_item(last->list.p, struct archive_file);
index = last->index + 1;
}
for (i = 0; i < 10; i++) {
if (lvm_snprintf(archive_name, sizeof(archive_name),
"%s/%s_%05d.vg",
bc->dir, vg->name, index) < 0) {
log_err("archive file name too long.");
goto out;
}
if (lvm_rename(temp_file, archive_name)) {
r = 1;
break;
}
index++;
}
out:
return r;
}
void archive_expire(struct format_instance *fi)
{
/* FIXME: finish */
}
static struct format_handler _archive_handler = {
get_vgs: _get_vgs,
get_pvs: _get_pvs,
pv_read: _pv_read,
pv_setup: _pv_setup,
pv_write: _pv_write,
vg_setup: _vg_setup,
vg_read: _vg_read,
vg_write: _vg_write,
destroy: _destroy
};
struct format_instance *archive_format_create(struct cmd_context *cmd,
const char *dir,
uint32_t retain_days,
uint32_t min_retains)
{
struct format_instance *fi;
struct archive_c *bc = NULL;
struct pool *mem = cmd->mem;
if (!(bc = pool_zalloc(mem, sizeof(*bc)))) {
stack;
return NULL;
}
if (!(bc->mem = pool_create(1024))) {
stack;
goto bad;
}
if (!(bc->dir = pool_strdup(mem, dir))) {
stack;
goto bad;
}
bc->retain_days = retain_days;
bc->min_retains = min_retains;
if (!(fi = pool_alloc(mem, sizeof(*fi)))) {
stack;
goto bad;
}
fi->cmd = cmd;
fi->ops = &_archive_handler;
fi->private = bc;
return fi;
bad:
if (bc->mem)
pool_destroy(bc->mem);
pool_free(mem, bc);
return NULL;
}

View File

@ -7,11 +7,17 @@
#include "format-text.h"
#include "import-export.h"
#include "lvm-file.h"
#include "log.h"
#include "pool.h"
#include "config.h"
#include "hash.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/file.h>
#include <limits.h>
/*
* NOTE: Currently there can be only one vg per file.
*/
@ -69,25 +75,51 @@ static struct volume_group *_vg_read(struct format_instance *fi,
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
{
FILE *fp;
int fd;
char *slash;
char *file = (char *) fi->private;
char temp_file[PATH_MAX], temp_dir[PATH_MAX];
/* FIXME: should be opened exclusively */
if (!(fp = fopen(file, "w"))) {
log_sys_error("fopen", file);
slash = rindex(file, '/');
if (slash == 0)
strcpy(temp_dir, ".");
else if (slash - file < PATH_MAX) {
strncpy(temp_dir, file, slash - file);
temp_dir[slash - file] = '\0';
} else {
log_error("Text format failed to determine directory.");
return 0;
}
if (!create_temp_name(temp_dir, temp_file, sizeof(temp_file), &fd)) {
log_err("Couldn't create temporary text file name.");
return 0;
}
if (!(fp = fdopen(fd, "w"))) {
log_sys_error("fdopen", temp_file);
close(fd);
return 0;
}
if (!text_vg_export(fp, vg)) {
log_error("Failed to write metadata to %s.", file);
log_error("Failed to write metadata to %s.", temp_file);
fclose(fp);
return 0;
}
if (!fclose(fp)) {
if (fclose(fp)) {
log_sys_error("fclose", file);
return 0;
}
if (rename(temp_file, file)) {
log_error("%s: rename to %s failed: %s", temp_file, file,
strerror(errno));
return 0;
}
return 1;
}

View File

@ -11,17 +11,18 @@
#include "metadata.h"
/*
* The backup format is used to maintain a set of backup files.
* 'retain_days' gives the minimum number of days that a backup must
* The archive format is used to maintain a set of metadata backup files
* in an archive directory.
* 'retain_days' is the minimum number of days that an archive file must
* be held for.
*
* 'min_backups' is the minimum number of backups required for each volume
* group.
* 'min_archives' is the minimum number of archives required to be kept
* for each volume group.
*/
struct format_instance *backup_format_create(struct cmd_context *cmd,
struct format_instance *archive_format_create(struct cmd_context *cmd,
const char *dir,
uint32_t retain_days,
uint32_t min_backups);
uint32_t min_archives);
void backup_expire(struct format_instance *fi);

View File

@ -42,7 +42,7 @@ e.g. backup {
.br
An assignment associates a type with an identifier.
.br
e.g. max_backups = 42
e.g. max_archives = 42
.br
.TP
\fBarray = '[' (type ',')* type ']' | '[' ']'\fP
@ -109,26 +109,50 @@ is invoked. By default tools append messages to the log file.
\fBtest\fP \(em If set to 1, run tools in test mode i.e. no metadata
gets updated.
.TP
\fBbackup\fP \(em Configuration for metadata backups
\fBbackup\fP \(em Configuration for metadata backups.
.IP
\fBdir\fP \(em Directory used for automatic metadata backups.
\fBarchive_dir\fP \(em Directory used for automatic metadata archives.
Backup copies of former metadata for each volume group are archived here.
Defaults to "/etc/lvm/archive".
.IP
\fBbackup_dir\fP \(em Directory used for automatic metadata backups.
A single backup copy of the current metadata for each volume group
is stored here.
Defaults to "/etc/lvm/backup".
.IP
\fBkeep\fP \(em Minimum number of backups to keep.
\fBarchive\fP \(em Whether or not tools automatically archive existing
metadata into \fBarchive_dir\fP before making changes to it.
Default is 1 (automatic archives enabled).
Set to 0 to disable.
Disabling this might make metadata recovery difficult or impossible
if something goes wrong.
.IP
\fBbackup\fP \(em Whether or not tools make an automatic backup
into \fBbackup_dir\fP after changing metadata.
Default is 1 (automatic backups enabled). Set to 0 to disable.
Disabling this might make metadata recovery difficult or impossible
if something goes wrong.
.IP
\fBretain_min\fP \(em Minimum number of archives to keep.
Defaults to 10.
.IP
\fBdays\fP \(em Minimum number of days to keep backup files.
Defaults to 14.
.IP
\fBauto\fP \(em Whether or not tools make automatic backups after changing
metadata. Default is 1 (automatic backups enabled). Set to 0 to disable.
\fBretain_days\fP \(em Minimum number of days to keep archive files.
Defaults to 30.
.TP
\fBshell\fP \(em LVM2 built-in readline shell settings
.IP
\fBhistory_size\fP \(em Maximum number of lines of shell history to retain (default 100) in $HOME/.lvm_history
.TP
\fBglobal\fP \(em Global settings
.IP
\fBumask\fP \(em File creation mask for any files and directories created.
Interpreted as octal if the first digit is zero.
Defaults to 077.
Use 022 to allow other users to read the files by default.
.SH FILES
.I /etc/lvm/lvm.conf
.br
.I $HOME/.lvm_history
.SH SEE ALSO
.BR lvm (8)
.BR umask (2)

View File

@ -11,6 +11,7 @@
#include "lvm-string.h"
#include "toollib.h"
#include <unistd.h>
#include <limits.h>
static struct {
@ -60,7 +61,7 @@ static int __archive(struct volume_group *vg)
int r;
struct format_instance *archiver;
if (!(archiver = backup_format_create(vg->cmd,
if (!(archiver = archive_format_create(vg->cmd,
_archive_params.dir,
_archive_params.keep_days,
_archive_params.keep_number))) {

View File

@ -37,5 +37,6 @@ void backup_exit(void);
void backup_enable(int flag);
int backup(struct volume_group *vg);
int backup_remove(const char *vg_name);
#endif

View File

@ -333,6 +333,7 @@ xx(vgcfgbackup,
"Backup volume group configuration(s)",
"vgcfgbackup " "\n"
"\t[-d|--debug] " "\n"
"\t[-f|--file filename] " "\n"
"\t[-h|--help] " "\n"
"\t[-v|--verbose]" "\n"
"\t[-V|--version] " "\n"
@ -343,7 +344,7 @@ xx(vgcfgrestore,
"Restore volume group configuration",
"vgcfgrestore " "\n"
"\t[-d|--debug] " "\n"
"\t[-f|--file VGConfPath] " "\n"
"\t[-f|--file filename] " "\n"
"\t[-l[l]|--list [--list]]" "\n"
"\t[-n|--name VolumeGroupName] " "\n"
"\t[-h|--help]" "\n"

View File

@ -21,6 +21,8 @@
#define DEFAULT_DEV_DIR "/dev"
#define DEFAULT_UMASK 0077
#ifdef READLINE_SUPPORT
#define DEFAULT_MAX_HISTORY 100
#endif

View File

@ -65,6 +65,8 @@ struct config_info {
int archive; /* should we archive ? */
int backup; /* should we backup ? */
mode_t umask;
};
static struct config_info _default_settings;
@ -661,6 +663,15 @@ static void __init_log(struct config_file *cf)
const char *log_file = find_config_str(cf->root, "log/file", '/', 0);
_default_settings.debug =
find_config_int(cf->root, "log/level", '/', 0);
_default_settings.verbose =
find_config_int(cf->root, "log/verbose", '/', 0);
_default_settings.test = find_config_int(cf->root, "log/test", '/', 0);
init_debug(_default_settings.debug);
init_verbose(_default_settings.verbose);
if (find_config_int(cf->root, "log/overwrite", '/', 0))
open_mode = "w";
@ -672,13 +683,6 @@ static void __init_log(struct config_file *cf)
init_log(_log);
}
_default_settings.debug =
find_config_int(cf->root, "log/level", '/', 0);
_default_settings.verbose =
find_config_int(cf->root, "log/verbose", '/', 0);
_default_settings.test = find_config_int(cf->root, "log/test", '/', 0);
}
static int _init_backup(struct config_file *cf)
@ -869,6 +873,7 @@ static int init(void)
{
struct stat info;
char config_file[PATH_MAX] = "";
mode_t old_umask;
if (!_get_env_vars())
return 0;
@ -890,11 +895,11 @@ static int init(void)
/* Use LOG_USER for syslog messages by default */
init_syslog(LOG_USER);
_init_rand();
/* send log messages to stderr for now */
init_log(stderr);
_init_rand();
if (*_sys_dir && lvm_snprintf(config_file, sizeof(config_file),
"%s/lvm.conf", _sys_dir) < 0) {
log_error("lvm_sys_dir was too long");
@ -912,6 +917,14 @@ static int init(void)
__init_log(cmd->cf);
}
_default_settings.umask = find_config_int(cmd->cf->root,
"global/umask", '/',
DEFAULT_UMASK);
if ((old_umask = umask((mode_t)_default_settings.umask)) !=
(mode_t)_default_settings.umask)
log_verbose("Set umask to %04o", _default_settings.umask);
if (lvm_snprintf(_dev_dir, sizeof(_dev_dir), "%s/",
find_config_str(cmd->cf->root, "devices/dir",
'/', DEFAULT_DEV_DIR)) < 0) {

View File

@ -17,7 +17,7 @@ int create_dir(const char *dir)
if (stat(dir, &info) < 0 ) {
log_verbose("Creating directory %s", dir);
if (!mkdir(dir, S_IRWXU))
if (!mkdir(dir, 0777));
return 1;
log_sys_error("mkdir", dir);
return 0;

View File

@ -77,7 +77,7 @@ static int vgremove_single(const char *vg_name)
}
}
backup(vg);
backup_remove(vg_name);
if (!ret)
log_print("Volume group %s successfully removed", vg_name);