2 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
5 * This file is part of LVM2.
7 * This copyrighted material is made available to anyone wishing to use,
8 * modify, copy, or redistribute it subject to the terms and conditions
9 * of the GNU Lesser General Public License v.2.1.
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, write to the Free Software Foundation,
13 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 #include "format-text.h"
20 #include "import-export.h"
21 #include "lvm-string.h"
23 #include "toolcontext.h"
32 #define SECS_PER_DAY 86400 /* 24*60*60 */
35 * The format instance is given a directory path upon creation.
36 * Each file in this directory whose name is of the form
37 * '(.*)_[0-9]*.vg' is a config file (see lib/config.[hc]), which
38 * contains a description of a single volume group.
40 * The prefix ($1 from the above regex) of the config file gives
41 * the volume group name.
43 * Backup files that have expired will be removed.
47 * A list of these is built up for our volume group. Ordered
48 * with the least recent at the head.
58 * Extract vg name and version number from a filename.
60 static int _split_vg(const char *filename, char *vgname, size_t vgsize,
64 const char *dot, *underscore;
66 len = strlen(filename);
70 dot = (filename + len - 3);
71 if (strcmp(".vg", dot))
74 if (!(underscore = strrchr(filename, '_')))
77 if (sscanf(underscore + 1, "%u", ix) != 1)
80 vg_len = underscore - filename;
81 if (vg_len + 1 > vgsize)
84 strncpy(vgname, filename, vg_len);
85 vgname[vg_len] = '\0';
90 static void _insert_archive_file(struct dm_list *head, struct archive_file *b)
92 struct archive_file *bf = NULL;
94 if (dm_list_empty(head)) {
95 dm_list_add(head, &b->list);
99 /* index reduces through list */
100 dm_list_iterate_items(bf, head) {
101 if (b->index > bf->index) {
102 dm_list_add(&bf->list, &b->list);
107 dm_list_add_h(&bf->list, &b->list);
110 static char *_join_file_to_dir(struct dm_pool *mem, const char *dir, const char *name)
112 if (!dm_pool_begin_object(mem, 32) ||
113 !dm_pool_grow_object(mem, dir, strlen(dir)) ||
114 !dm_pool_grow_object(mem, "/", 1) ||
115 !dm_pool_grow_object(mem, name, strlen(name)) ||
116 !dm_pool_grow_object(mem, "\0", 1))
119 return dm_pool_end_object(mem);
123 * Returns a list of archive_files.
125 static struct dm_list *_scan_archive(struct dm_pool *mem,
126 const char *vgname, const char *dir)
130 char vgname_found[64], *path;
131 struct dirent **dirent;
132 struct archive_file *af;
133 struct dm_list *results;
135 if (!(results = dm_pool_alloc(mem, sizeof(*results))))
138 dm_list_init(results);
140 /* Sort fails beyond 5-digit indexes */
141 if ((count = scandir(dir, &dirent, NULL, alphasort)) < 0) {
142 log_error("Couldn't scan the archive directory (%s).", dir);
146 for (i = 0; i < count; i++) {
147 if (!strcmp(dirent[i]->d_name, ".") ||
148 !strcmp(dirent[i]->d_name, ".."))
151 /* check the name is the correct format */
152 if (!_split_vg(dirent[i]->d_name, vgname_found,
153 sizeof(vgname_found), &ix))
156 /* is it the vg we're interested in ? */
157 if (strcmp(vgname, vgname_found))
160 if (!(path = _join_file_to_dir(mem, dir, dirent[i]->d_name)))
164 * Create a new archive_file.
166 if (!(af = dm_pool_alloc(mem, sizeof(*af)))) {
167 log_error("Couldn't create new archive file.");
176 * Insert it to the correct part of the list.
178 _insert_archive_file(results, af);
182 for (i = 0; i < count; i++)
189 static void _remove_expired(struct dm_list *archives, uint32_t archives_size,
190 uint32_t retain_days, uint32_t min_archive)
192 struct archive_file *bf;
196 /* Make sure there are enough archives to even bother looking for
198 if (archives_size <= min_archive)
201 /* Convert retain_days into the time after which we must retain */
202 retain_time = time(NULL) - (time_t) retain_days *SECS_PER_DAY;
204 /* Assume list is ordered newest first (by index) */
205 dm_list_iterate_back_items(bf, archives) {
206 /* Get the mtime of the file and unlink if too old */
207 if (stat(bf->path, &sb)) {
208 log_sys_error("stat", bf->path);
212 if (sb.st_mtime > retain_time)
215 log_very_verbose("Expiring archive %s", bf->path);
216 if (unlink(bf->path))
217 log_sys_error("unlink", bf->path);
219 /* Don't delete any more if we've reached the minimum */
220 if (--archives_size <= min_archive)
225 int archive_vg(struct volume_group *vg,
226 const char *dir, const char *desc,
227 uint32_t retain_days, uint32_t min_archive)
229 int i, fd, rnum, renamed = 0;
231 struct archive_file *last;
233 char temp_file[PATH_MAX], archive_name[PATH_MAX];
234 struct dm_list *archives;
237 * Write the vg out to a temporary file.
239 if (!create_temp_name(dir, temp_file, sizeof(temp_file), &fd,
240 &vg->cmd->rand_seed)) {
241 log_error("Couldn't create temporary archive name.");
245 if (!(fp = fdopen(fd, "w"))) {
246 log_error("Couldn't create FILE object for archive.");
248 log_sys_error("close", temp_file);
252 if (!text_vg_export_file(vg, desc, fp)) {
254 log_sys_error("fclose", temp_file);
258 if (lvm_fclose(fp, temp_file))
259 return_0; /* Leave file behind as evidence of failure */
262 * Now we want to rename this file to <vg>_index.vg.
264 if (!(archives = _scan_archive(vg->cmd->mem, vg->name, dir)))
267 if (dm_list_empty(archives))
270 last = dm_list_item(dm_list_first(archives), struct archive_file);
271 ix = last->index + 1;
274 rnum = rand_r(&vg->cmd->rand_seed);
276 for (i = 0; i < 10; i++) {
277 if (dm_snprintf(archive_name, sizeof(archive_name),
279 dir, vg->name, ix, rnum) < 0) {
280 log_error("Archive file name too long.");
284 if ((renamed = lvm_rename(temp_file, archive_name)))
291 log_error("Archive rename failed for %s", temp_file);
293 _remove_expired(archives, dm_list_size(archives) + renamed, retain_days,
299 static void _display_archive(struct cmd_context *cmd, struct archive_file *af)
301 struct volume_group *vg = NULL;
302 struct format_instance *tf;
308 log_print("File:\t\t%s", af->path);
310 if (!(context = create_text_context(cmd, af->path, NULL)) ||
311 !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
313 log_error("Couldn't create text instance object.");
318 * Read the archive file to ensure that it is valid, and
319 * retrieve the archive time and description.
321 /* FIXME Use variation on _vg_read */
322 if (!(vg = text_vg_import_file(tf, af->path, &when, &desc))) {
323 log_error("Unable to read archive file.");
324 tf->fmt->ops->destroy_instance(tf);
328 log_print("VG name: \t%s", vg->name);
329 log_print("Description:\t%s", desc ? : "<No description>");
330 log_print("Backup Time:\t%s", ctime(&when));
333 tf->fmt->ops->destroy_instance(tf);
336 int archive_list(struct cmd_context *cmd, const char *dir, const char *vgname)
338 struct dm_list *archives;
339 struct archive_file *af;
341 if (!(archives = _scan_archive(cmd->mem, vgname, dir)))
344 if (dm_list_empty(archives))
345 log_print("No archives found in %s.", dir);
347 dm_list_iterate_back_items(af, archives)
348 _display_archive(cmd, af);
350 dm_pool_free(cmd->mem, archives);
355 int archive_list_file(struct cmd_context *cmd, const char *file)
357 struct archive_file af;
361 if (!path_exists(af.path)) {
362 log_error("Archive file %s not found.", af.path);
366 _display_archive(cmd, &af);
371 int backup_list(struct cmd_context *cmd, const char *dir, const char *vgname)
373 struct archive_file af;
375 if (!(af.path = _join_file_to_dir(cmd->mem, dir, vgname)))
378 if (path_exists(af.path))
379 _display_archive(cmd, &af);