1/*	$NetBSD: archiver.c,v 1.2 2011/01/05 14:57:28 haad Exp $	*/
2
3/*
4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6 *
7 * This file is part of LVM2.
8 *
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.
12 *
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
16 */
17
18#include "lib.h"
19#include "archiver.h"
20#include "format-text.h"
21#include "lvm-file.h"
22#include "lvm-string.h"
23#include "lvmcache.h"
24#include "toolcontext.h"
25#include "locking.h"
26
27#include <unistd.h>
28
29struct archive_params {
30	int enabled;
31	char *dir;
32	unsigned int keep_days;
33	unsigned int keep_number;
34};
35
36struct backup_params {
37	int enabled;
38	char *dir;
39};
40
41int archive_init(struct cmd_context *cmd, const char *dir,
42		 unsigned int keep_days, unsigned int keep_min,
43		 int enabled)
44{
45	if (!(cmd->archive_params = dm_pool_zalloc(cmd->libmem,
46						sizeof(*cmd->archive_params)))) {
47		log_error("archive_params alloc failed");
48		return 0;
49	}
50
51	cmd->archive_params->dir = NULL;
52
53	if (!*dir)
54		return 1;
55
56	if (!(cmd->archive_params->dir = dm_strdup(dir))) {
57		log_error("Couldn't copy archive directory name.");
58		return 0;
59	}
60
61	cmd->archive_params->keep_days = keep_days;
62	cmd->archive_params->keep_number = keep_min;
63	archive_enable(cmd, enabled);
64
65	return 1;
66}
67
68void archive_exit(struct cmd_context *cmd)
69{
70	if (!cmd->archive_params)
71		return;
72	if (cmd->archive_params->dir)
73		dm_free(cmd->archive_params->dir);
74	memset(cmd->archive_params, 0, sizeof(*cmd->archive_params));
75}
76
77void archive_enable(struct cmd_context *cmd, int flag)
78{
79	cmd->archive_params->enabled = flag;
80}
81
82static char *_build_desc(struct dm_pool *mem, const char *line, int before)
83{
84	size_t len = strlen(line) + 32;
85	char *buffer;
86
87	if (!(buffer = dm_pool_zalloc(mem, strlen(line) + 32)))
88		return_NULL;
89
90	if (snprintf(buffer, len,
91		     "Created %s executing '%s'",
92		     before ? "*before*" : "*after*", line) < 0)
93		return_NULL;
94
95	return buffer;
96}
97
98static int __archive(struct volume_group *vg)
99{
100	char *desc;
101
102	if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 1)))
103		return_0;
104
105	return archive_vg(vg, vg->cmd->archive_params->dir, desc,
106			  vg->cmd->archive_params->keep_days,
107			  vg->cmd->archive_params->keep_number);
108}
109
110int archive(struct volume_group *vg)
111{
112	if (!vg->cmd->archive_params->enabled || !vg->cmd->archive_params->dir)
113		return 1;
114
115	if (test_mode()) {
116		log_verbose("Test mode: Skipping archiving of volume group.");
117		return 1;
118	}
119
120#ifdef __NetBSD__
121	if (is_operator()) {
122		log_verbose("Operator usage: Skipping archiving of volume group.");
123		return 1;
124	}
125#endif
126	if (!dm_create_dir(vg->cmd->archive_params->dir))
127		return 0;
128
129	/* Trap a read-only file system */
130	if ((access(vg->cmd->archive_params->dir, R_OK | W_OK | X_OK) == -1) &&
131	     (errno == EROFS))
132		return 0;
133
134	log_verbose("Archiving volume group \"%s\" metadata (seqno %u).", vg->name,
135		    vg->seqno);
136	if (!__archive(vg)) {
137		log_error("Volume group \"%s\" metadata archive failed.",
138			  vg->name);
139		return 0;
140	}
141
142	return 1;
143}
144
145int archive_display(struct cmd_context *cmd, const char *vg_name)
146{
147	int r1, r2;
148
149	r1 = archive_list(cmd, cmd->archive_params->dir, vg_name);
150	r2 = backup_list(cmd, cmd->backup_params->dir, vg_name);
151
152	return r1 && r2;
153}
154
155int archive_display_file(struct cmd_context *cmd, const char *file)
156{
157	int r;
158
159	r = archive_list_file(cmd, file);
160
161	return r;
162}
163
164int backup_init(struct cmd_context *cmd, const char *dir,
165		int enabled)
166{
167	if (!(cmd->backup_params = dm_pool_zalloc(cmd->libmem,
168					       sizeof(*cmd->backup_params)))) {
169		log_error("backup_params alloc failed");
170		return 0;
171	}
172
173	cmd->backup_params->dir = NULL;
174	if (!*dir)
175		return 1;
176
177	if (!(cmd->backup_params->dir = dm_strdup(dir))) {
178		log_error("Couldn't copy backup directory name.");
179		return 0;
180	}
181	backup_enable(cmd, enabled);
182
183	return 1;
184}
185
186void backup_exit(struct cmd_context *cmd)
187{
188	if (!cmd->backup_params)
189		return;
190	if (cmd->backup_params->dir)
191		dm_free(cmd->backup_params->dir);
192	memset(cmd->backup_params, 0, sizeof(*cmd->backup_params));
193}
194
195void backup_enable(struct cmd_context *cmd, int flag)
196{
197	cmd->backup_params->enabled = flag;
198}
199
200static int __backup(struct volume_group *vg)
201{
202	char name[PATH_MAX];
203	char *desc;
204
205	if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 0)))
206		return_0;
207
208	if (dm_snprintf(name, sizeof(name), "%s/%s",
209			 vg->cmd->backup_params->dir, vg->name) < 0) {
210		log_error("Failed to generate volume group metadata backup "
211			  "filename.");
212		return 0;
213	}
214
215	return backup_to_file(name, desc, vg);
216}
217
218int backup_locally(struct volume_group *vg)
219{
220	if (!vg->cmd->backup_params->enabled || !vg->cmd->backup_params->dir) {
221		log_warn("WARNING: This metadata update is NOT backed up");
222		return 1;
223	}
224
225	if (test_mode()) {
226		log_verbose("Test mode: Skipping volume group backup.");
227		return 1;
228	}
229
230#ifdef __NetBSD__
231	if (is_operator()) {
232		log_verbose("Operator usage: Skipping archiving of volume group.");
233		return 1;
234	}
235#endif
236	if (!dm_create_dir(vg->cmd->backup_params->dir))
237		return 0;
238
239	/* Trap a read-only file system */
240	if ((access(vg->cmd->backup_params->dir, R_OK | W_OK | X_OK) == -1) &&
241	    (errno == EROFS))
242		return 0;
243
244	if (!__backup(vg)) {
245		log_error("Backup of volume group %s metadata failed.",
246			  vg->name);
247		return 0;
248	}
249
250	return 1;
251}
252
253int backup(struct volume_group *vg)
254{
255	if (vg_is_clustered(vg))
256		remote_backup_metadata(vg);
257
258	return backup_locally(vg);
259}
260
261int backup_remove(struct cmd_context *cmd, const char *vg_name)
262{
263	char path[PATH_MAX];
264
265	if (dm_snprintf(path, sizeof(path), "%s/%s",
266			 cmd->backup_params->dir, vg_name) < 0) {
267		log_error("Failed to generate backup filename (for removal).");
268		return 0;
269	}
270
271	/*
272	 * Let this fail silently.
273	 */
274	unlink(path);
275	return 1;
276}
277
278struct volume_group *backup_read_vg(struct cmd_context *cmd,
279				    const char *vg_name, const char *file)
280{
281	struct volume_group *vg = NULL;
282	struct format_instance *tf;
283	struct metadata_area *mda;
284	void *context;
285
286	if (!(context = create_text_context(cmd, file,
287					    cmd->cmd_line)) ||
288	    !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
289							 NULL, context))) {
290		log_error("Couldn't create text format object.");
291		return NULL;
292	}
293
294	dm_list_iterate_items(mda, &tf->metadata_areas) {
295		if (!(vg = mda->ops->vg_read(tf, vg_name, mda)))
296			stack;
297		break;
298	}
299
300	tf->fmt->ops->destroy_instance(tf);
301	return vg;
302}
303
304/* ORPHAN and VG locks held before calling this */
305int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg)
306{
307	struct pv_list *pvl;
308	struct physical_volume *pv;
309	struct lvmcache_info *info;
310
311	/*
312	 * FIXME: Check that the PVs referenced in the backup are
313	 * not members of other existing VGs.
314	 */
315
316	/* Attempt to write out using currently active format */
317	if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg->name,
318						       NULL, NULL))) {
319		log_error("Failed to allocate format instance");
320		return 0;
321	}
322
323	/* Add any metadata areas on the PVs */
324	dm_list_iterate_items(pvl, &vg->pvs) {
325		pv = pvl->pv;
326		if (!(info = info_from_pvid(pv->dev->pvid, 0))) {
327			log_error("PV %s missing from cache",
328				  pv_dev_name(pv));
329			return 0;
330		}
331		if (cmd->fmt != info->fmt) {
332			log_error("PV %s is a different format (seqno %s)",
333				  pv_dev_name(pv), info->fmt->name);
334			return 0;
335		}
336		if (!vg->fid->fmt->ops->
337		    pv_setup(vg->fid->fmt, UINT64_C(0), 0, 0, 0, 0, 0UL,
338			     UINT64_C(0), &vg->fid->metadata_areas, pv, vg)) {
339			log_error("Format-specific setup for %s failed",
340				  pv_dev_name(pv));
341			return 0;
342		}
343	}
344
345	if (!vg_write(vg) || !vg_commit(vg))
346		return_0;
347
348	return 1;
349}
350
351/* ORPHAN and VG locks held before calling this */
352int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name,
353			     const char *file)
354{
355	struct volume_group *vg;
356	int missing_pvs, r = 0;
357
358	/*
359	 * Read in the volume group from the text file.
360	 */
361	if (!(vg = backup_read_vg(cmd, vg_name, file)))
362		return_0;
363
364	missing_pvs = vg_missing_pv_count(vg);
365	if (missing_pvs == 0)
366		r = backup_restore_vg(cmd, vg);
367	else
368		log_error("Cannot restore Volume Group %s with %i PVs "
369			  "marked as missing.", vg->name, missing_pvs);
370
371	vg_release(vg);
372	return r;
373}
374
375int backup_restore(struct cmd_context *cmd, const char *vg_name)
376{
377	char path[PATH_MAX];
378
379	if (dm_snprintf(path, sizeof(path), "%s/%s",
380			 cmd->backup_params->dir, vg_name) < 0) {
381		log_error("Failed to generate backup filename (for restore).");
382		return 0;
383	}
384
385	return backup_restore_from_file(cmd, vg_name, path);
386}
387
388int backup_to_file(const char *file, const char *desc, struct volume_group *vg)
389{
390	int r = 0;
391	struct format_instance *tf;
392	struct metadata_area *mda;
393	void *context;
394	struct cmd_context *cmd;
395
396	cmd = vg->cmd;
397
398	log_verbose("Creating volume group backup \"%s\" (seqno %u).", file, vg->seqno);
399
400	if (!(context = create_text_context(cmd, file, desc)) ||
401	    !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
402							 NULL, context))) {
403		log_error("Couldn't create backup object.");
404		return 0;
405	}
406
407	/* Write and commit the metadata area */
408	dm_list_iterate_items(mda, &tf->metadata_areas) {
409		if (!(r = mda->ops->vg_write(tf, vg, mda))) {
410			stack;
411			continue;
412		}
413		if (mda->ops->vg_commit &&
414		    !(r = mda->ops->vg_commit(tf, vg, mda))) {
415			stack;
416		}
417	}
418
419	tf->fmt->ops->destroy_instance(tf);
420	return r;
421}
422
423/*
424 * Update backup (and archive) if they're out-of-date or don't exist.
425 */
426void check_current_backup(struct volume_group *vg)
427{
428	char path[PATH_MAX];
429	struct volume_group *vg_backup;
430	int old_suppress;
431
432	if (vg_is_exported(vg))
433		return;
434
435	if (dm_snprintf(path, sizeof(path), "%s/%s",
436			 vg->cmd->backup_params->dir, vg->name) < 0) {
437		log_debug("Failed to generate backup filename.");
438		return;
439	}
440
441	old_suppress = log_suppress(1);
442	/* Up-to-date backup exists? */
443	if ((vg_backup = backup_read_vg(vg->cmd, vg->name, path)) &&
444	    (vg->seqno == vg_backup->seqno) &&
445	    (id_equal(&vg->id, &vg_backup->id))) {
446		log_suppress(old_suppress);
447		vg_release(vg_backup);
448		return;
449	}
450	log_suppress(old_suppress);
451
452	if (vg_backup) {
453		archive(vg_backup);
454		vg_release(vg_backup);
455	}
456	archive(vg);
457	backup_locally(vg);
458}
459