4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
7 * This file is part of LVM2.
9 * This copyrighted material is made available to anyone wishing to use,
10 * modify, copy, or redistribute it subject to the terms and conditions
11 * of the GNU Lesser General Public License v.2.1.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "format-text.h"
22 #include "import-export.h"
23 #include "lvm-string.h"
25 #include "toolcontext.h"
34 #define SECS_PER_DAY 86400 /* 24*60*60 */
37 * The format instance is given a directory path upon creation.
38 * Each file in this directory whose name is of the form
39 * '(.*)_[0-9]*.vg' is a config file (see lib/config.[hc]), which
40 * contains a description of a single volume group.
42 * The prefix ($1 from the above regex) of the config file gives
43 * the volume group name.
45 * Backup files that have expired will be removed.
49 * A list of these is built up for our volume group. Ordered
50 * with the least recent at the head.
60 * Extract vg name and version number from a filename.
62 static int _split_vg(const char *filename
, char *vgname
, size_t vgsize
,
66 const char *dot
, *underscore
;
68 len
= strlen(filename
);
72 dot
= (filename
+ len
- 3);
73 if (strcmp(".vg", dot
))
76 if (!(underscore
= strrchr(filename
, '_')))
79 if (sscanf(underscore
+ 1, "%u", ix
) != 1)
82 vg_len
= underscore
- filename
;
83 if (vg_len
+ 1 > vgsize
)
86 strncpy(vgname
, filename
, vg_len
);
87 vgname
[vg_len
] = '\0';
92 static void _insert_archive_file(struct dm_list
*head
, struct archive_file
*b
)
94 struct archive_file
*bf
= NULL
;
96 if (dm_list_empty(head
)) {
97 dm_list_add(head
, &b
->list
);
101 /* index reduces through list */
102 dm_list_iterate_items(bf
, head
) {
103 if (b
->index
> bf
->index
) {
104 dm_list_add(&bf
->list
, &b
->list
);
109 dm_list_add_h(&bf
->list
, &b
->list
);
112 static char *_join_file_to_dir(struct dm_pool
*mem
, const char *dir
, const char *name
)
114 if (!dm_pool_begin_object(mem
, 32) ||
115 !dm_pool_grow_object(mem
, dir
, strlen(dir
)) ||
116 !dm_pool_grow_object(mem
, "/", 1) ||
117 !dm_pool_grow_object(mem
, name
, strlen(name
)) ||
118 !dm_pool_grow_object(mem
, "\0", 1))
121 return dm_pool_end_object(mem
);
125 * Returns a list of archive_files.
127 static struct dm_list
*_scan_archive(struct dm_pool
*mem
,
128 const char *vgname
, const char *dir
)
132 char vgname_found
[64], *path
;
133 struct dirent
**dirent
;
134 struct archive_file
*af
;
135 struct dm_list
*results
;
137 if (!(results
= dm_pool_alloc(mem
, sizeof(*results
))))
140 dm_list_init(results
);
142 /* Sort fails beyond 5-digit indexes */
143 if ((count
= scandir(dir
, &dirent
, NULL
, alphasort
)) < 0) {
144 log_error("Couldn't scan the archive directory (%s).", dir
);
148 for (i
= 0; i
< count
; i
++) {
149 if (!strcmp(dirent
[i
]->d_name
, ".") ||
150 !strcmp(dirent
[i
]->d_name
, ".."))
153 /* check the name is the correct format */
154 if (!_split_vg(dirent
[i
]->d_name
, vgname_found
,
155 sizeof(vgname_found
), &ix
))
158 /* is it the vg we're interested in ? */
159 if (strcmp(vgname
, vgname_found
))
162 if (!(path
= _join_file_to_dir(mem
, dir
, dirent
[i
]->d_name
)))
166 * Create a new archive_file.
168 if (!(af
= dm_pool_alloc(mem
, sizeof(*af
)))) {
169 log_error("Couldn't create new archive file.");
178 * Insert it to the correct part of the list.
180 _insert_archive_file(results
, af
);
184 for (i
= 0; i
< count
; i
++)
191 static void _remove_expired(struct dm_list
*archives
, uint32_t archives_size
,
192 uint32_t retain_days
, uint32_t min_archive
)
194 struct archive_file
*bf
;
198 /* Make sure there are enough archives to even bother looking for
200 if (archives_size
<= min_archive
)
203 /* Convert retain_days into the time after which we must retain */
204 retain_time
= time(NULL
) - (time_t) retain_days
*SECS_PER_DAY
;
206 /* Assume list is ordered newest first (by index) */
207 dm_list_iterate_back_items(bf
, archives
) {
208 /* Get the mtime of the file and unlink if too old */
209 if (stat(bf
->path
, &sb
)) {
210 log_sys_error("stat", bf
->path
);
214 if (sb
.st_mtime
> retain_time
)
217 log_very_verbose("Expiring archive %s", bf
->path
);
218 if (unlink(bf
->path
))
219 log_sys_error("unlink", bf
->path
);
221 /* Don't delete any more if we've reached the minimum */
222 if (--archives_size
<= min_archive
)
227 int archive_vg(struct volume_group
*vg
,
228 const char *dir
, const char *desc
,
229 uint32_t retain_days
, uint32_t min_archive
)
231 int i
, fd
, renamed
= 0;
233 struct archive_file
*last
;
235 char temp_file
[PATH_MAX
], archive_name
[PATH_MAX
];
236 struct dm_list
*archives
;
239 * Write the vg out to a temporary file.
241 if (!create_temp_name(dir
, temp_file
, sizeof(temp_file
), &fd
,
242 &vg
->cmd
->rand_seed
)) {
243 log_error("Couldn't create temporary archive name.");
247 if (!(fp
= fdopen(fd
, "w"))) {
248 log_error("Couldn't create FILE object for archive.");
250 log_sys_error("close", temp_file
);
254 if (!text_vg_export_file(vg
, desc
, fp
)) {
256 log_sys_error("fclose", temp_file
);
260 if (lvm_fclose(fp
, temp_file
))
261 return_0
; /* Leave file behind as evidence of failure */
264 * Now we want to rename this file to <vg>_index.vg.
266 if (!(archives
= _scan_archive(vg
->cmd
->mem
, vg
->name
, dir
)))
269 if (dm_list_empty(archives
))
272 last
= dm_list_item(dm_list_first(archives
), struct archive_file
);
273 ix
= last
->index
+ 1;
276 for (i
= 0; i
< 10; i
++) {
277 if (dm_snprintf(archive_name
, sizeof(archive_name
),
278 "%s/%s_%05u.vg", dir
, vg
->name
, ix
) < 0) {
279 log_error("Archive file name too long.");
283 if ((renamed
= lvm_rename(temp_file
, archive_name
)))
290 log_error("Archive rename failed for %s", temp_file
);
292 _remove_expired(archives
, dm_list_size(archives
) + renamed
, retain_days
,
298 static void _display_archive(struct cmd_context
*cmd
, struct archive_file
*af
)
300 struct volume_group
*vg
= NULL
;
301 struct format_instance
*tf
;
307 log_print("File:\t\t%s", af
->path
);
309 if (!(context
= create_text_context(cmd
, af
->path
, NULL
)) ||
310 !(tf
= cmd
->fmt_backup
->ops
->create_instance(cmd
->fmt_backup
, NULL
,
312 log_error("Couldn't create text instance object.");
317 * Read the archive file to ensure that it is valid, and
318 * retrieve the archive time and description.
320 /* FIXME Use variation on _vg_read */
321 if (!(vg
= text_vg_import_file(tf
, af
->path
, &when
, &desc
))) {
322 log_print("Unable to read archive file.");
323 tf
->fmt
->ops
->destroy_instance(tf
);
327 log_print("VG name: \t%s", vg
->name
);
328 log_print("Description:\t%s", desc
? : "<No description>");
329 log_print("Backup Time:\t%s", ctime(&when
));
332 tf
->fmt
->ops
->destroy_instance(tf
);
335 int archive_list(struct cmd_context
*cmd
, const char *dir
, const char *vgname
)
337 struct dm_list
*archives
;
338 struct archive_file
*af
;
340 if (!(archives
= _scan_archive(cmd
->mem
, vgname
, dir
)))
343 if (dm_list_empty(archives
))
344 log_print("No archives found in %s.", dir
);
346 dm_list_iterate_back_items(af
, archives
)
347 _display_archive(cmd
, af
);
349 dm_pool_free(cmd
->mem
, archives
);
354 int archive_list_file(struct cmd_context
*cmd
, const char *file
)
356 struct archive_file af
;
358 af
.path
= (char *)file
;
360 if (!path_exists(af
.path
)) {
361 log_error("Archive file %s not found.", af
.path
);
365 _display_archive(cmd
, &af
);
370 int backup_list(struct cmd_context
*cmd
, const char *dir
, const char *vgname
)
372 struct archive_file af
;
374 if (!(af
.path
= _join_file_to_dir(cmd
->mem
, dir
, vgname
)))
377 if (path_exists(af
.path
))
378 _display_archive(cmd
, &af
);