1 /*	$NetBSD: archiver.c,v 1.1.1.3 2009/12/02 00:26:29 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 
29 struct archive_params {
30 	int enabled;
31 	char *dir;
32 	unsigned int keep_days;
33 	unsigned int keep_number;
34 };
35 
36 struct backup_params {
37 	int enabled;
38 	char *dir;
39 };
40 
41 int 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 
68 void 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 
77 void archive_enable(struct cmd_context *cmd, int flag)
78 {
79 	cmd->archive_params->enabled = flag;
80 }
81 
82 static 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 
98 static 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 
110 int 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 	if (!dm_create_dir(vg->cmd->archive_params->dir))
121 		return 0;
122 
123 	/* Trap a read-only file system */
124 	if ((access(vg->cmd->archive_params->dir, R_OK | W_OK | X_OK) == -1) &&
125 	     (errno == EROFS))
126 		return 0;
127 
128 	log_verbose("Archiving volume group \"%s\" metadata (seqno %u).", vg->name,
129 		    vg->seqno);
130 	if (!__archive(vg)) {
131 		log_error("Volume group \"%s\" metadata archive failed.",
132 			  vg->name);
133 		return 0;
134 	}
135 
136 	return 1;
137 }
138 
139 int archive_display(struct cmd_context *cmd, const char *vg_name)
140 {
141 	int r1, r2;
142 
143 	r1 = archive_list(cmd, cmd->archive_params->dir, vg_name);
144 	r2 = backup_list(cmd, cmd->backup_params->dir, vg_name);
145 
146 	return r1 && r2;
147 }
148 
149 int archive_display_file(struct cmd_context *cmd, const char *file)
150 {
151 	int r;
152 
153 	r = archive_list_file(cmd, file);
154 
155 	return r;
156 }
157 
158 int backup_init(struct cmd_context *cmd, const char *dir,
159 		int enabled)
160 {
161 	if (!(cmd->backup_params = dm_pool_zalloc(cmd->libmem,
162 					       sizeof(*cmd->backup_params)))) {
163 		log_error("backup_params alloc failed");
164 		return 0;
165 	}
166 
167 	cmd->backup_params->dir = NULL;
168 	if (!*dir)
169 		return 1;
170 
171 	if (!(cmd->backup_params->dir = dm_strdup(dir))) {
172 		log_error("Couldn't copy backup directory name.");
173 		return 0;
174 	}
175 	backup_enable(cmd, enabled);
176 
177 	return 1;
178 }
179 
180 void backup_exit(struct cmd_context *cmd)
181 {
182 	if (!cmd->backup_params)
183 		return;
184 	if (cmd->backup_params->dir)
185 		dm_free(cmd->backup_params->dir);
186 	memset(cmd->backup_params, 0, sizeof(*cmd->backup_params));
187 }
188 
189 void backup_enable(struct cmd_context *cmd, int flag)
190 {
191 	cmd->backup_params->enabled = flag;
192 }
193 
194 static int __backup(struct volume_group *vg)
195 {
196 	char name[PATH_MAX];
197 	char *desc;
198 
199 	if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 0)))
200 		return_0;
201 
202 	if (dm_snprintf(name, sizeof(name), "%s/%s",
203 			 vg->cmd->backup_params->dir, vg->name) < 0) {
204 		log_error("Failed to generate volume group metadata backup "
205 			  "filename.");
206 		return 0;
207 	}
208 
209 	return backup_to_file(name, desc, vg);
210 }
211 
212 int backup_locally(struct volume_group *vg)
213 {
214 	if (!vg->cmd->backup_params->enabled || !vg->cmd->backup_params->dir) {
215 		log_warn("WARNING: This metadata update is NOT backed up");
216 		return 1;
217 	}
218 
219 	if (test_mode()) {
220 		log_verbose("Test mode: Skipping volume group backup.");
221 		return 1;
222 	}
223 
224 	if (!dm_create_dir(vg->cmd->backup_params->dir))
225 		return 0;
226 
227 	/* Trap a read-only file system */
228 	if ((access(vg->cmd->backup_params->dir, R_OK | W_OK | X_OK) == -1) &&
229 	    (errno == EROFS))
230 		return 0;
231 
232 	if (!__backup(vg)) {
233 		log_error("Backup of volume group %s metadata failed.",
234 			  vg->name);
235 		return 0;
236 	}
237 
238 	return 1;
239 }
240 
241 int backup(struct volume_group *vg)
242 {
243 	if (vg_is_clustered(vg))
244 		remote_backup_metadata(vg);
245 
246 	return backup_locally(vg);
247 }
248 
249 int backup_remove(struct cmd_context *cmd, const char *vg_name)
250 {
251 	char path[PATH_MAX];
252 
253 	if (dm_snprintf(path, sizeof(path), "%s/%s",
254 			 cmd->backup_params->dir, vg_name) < 0) {
255 		log_error("Failed to generate backup filename (for removal).");
256 		return 0;
257 	}
258 
259 	/*
260 	 * Let this fail silently.
261 	 */
262 	unlink(path);
263 	return 1;
264 }
265 
266 struct volume_group *backup_read_vg(struct cmd_context *cmd,
267 				    const char *vg_name, const char *file)
268 {
269 	struct volume_group *vg = NULL;
270 	struct format_instance *tf;
271 	struct metadata_area *mda;
272 	void *context;
273 
274 	if (!(context = create_text_context(cmd, file,
275 					    cmd->cmd_line)) ||
276 	    !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
277 							 NULL, context))) {
278 		log_error("Couldn't create text format object.");
279 		return NULL;
280 	}
281 
282 	dm_list_iterate_items(mda, &tf->metadata_areas) {
283 		if (!(vg = mda->ops->vg_read(tf, vg_name, mda)))
284 			stack;
285 		break;
286 	}
287 
288 	tf->fmt->ops->destroy_instance(tf);
289 	return vg;
290 }
291 
292 /* ORPHAN and VG locks held before calling this */
293 int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg)
294 {
295 	struct pv_list *pvl;
296 	struct physical_volume *pv;
297 	struct lvmcache_info *info;
298 
299 	/*
300 	 * FIXME: Check that the PVs referenced in the backup are
301 	 * not members of other existing VGs.
302 	 */
303 
304 	/* Attempt to write out using currently active format */
305 	if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg->name,
306 						       NULL, NULL))) {
307 		log_error("Failed to allocate format instance");
308 		return 0;
309 	}
310 
311 	/* Add any metadata areas on the PVs */
312 	dm_list_iterate_items(pvl, &vg->pvs) {
313 		pv = pvl->pv;
314 		if (!(info = info_from_pvid(pv->dev->pvid, 0))) {
315 			log_error("PV %s missing from cache",
316 				  pv_dev_name(pv));
317 			return 0;
318 		}
319 		if (cmd->fmt != info->fmt) {
320 			log_error("PV %s is a different format (seqno %s)",
321 				  pv_dev_name(pv), info->fmt->name);
322 			return 0;
323 		}
324 		if (!vg->fid->fmt->ops->
325 		    pv_setup(vg->fid->fmt, UINT64_C(0), 0, 0, 0, 0, 0UL,
326 			     UINT64_C(0), &vg->fid->metadata_areas, pv, vg)) {
327 			log_error("Format-specific setup for %s failed",
328 				  pv_dev_name(pv));
329 			return 0;
330 		}
331 	}
332 
333 	if (!vg_write(vg) || !vg_commit(vg))
334 		return_0;
335 
336 	return 1;
337 }
338 
339 /* ORPHAN and VG locks held before calling this */
340 int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name,
341 			     const char *file)
342 {
343 	struct volume_group *vg;
344 	int missing_pvs, r = 0;
345 
346 	/*
347 	 * Read in the volume group from the text file.
348 	 */
349 	if (!(vg = backup_read_vg(cmd, vg_name, file)))
350 		return_0;
351 
352 	missing_pvs = vg_missing_pv_count(vg);
353 	if (missing_pvs == 0)
354 		r = backup_restore_vg(cmd, vg);
355 	else
356 		log_error("Cannot restore Volume Group %s with %i PVs "
357 			  "marked as missing.", vg->name, missing_pvs);
358 
359 	vg_release(vg);
360 	return r;
361 }
362 
363 int backup_restore(struct cmd_context *cmd, const char *vg_name)
364 {
365 	char path[PATH_MAX];
366 
367 	if (dm_snprintf(path, sizeof(path), "%s/%s",
368 			 cmd->backup_params->dir, vg_name) < 0) {
369 		log_error("Failed to generate backup filename (for restore).");
370 		return 0;
371 	}
372 
373 	return backup_restore_from_file(cmd, vg_name, path);
374 }
375 
376 int backup_to_file(const char *file, const char *desc, struct volume_group *vg)
377 {
378 	int r = 0;
379 	struct format_instance *tf;
380 	struct metadata_area *mda;
381 	void *context;
382 	struct cmd_context *cmd;
383 
384 	cmd = vg->cmd;
385 
386 	log_verbose("Creating volume group backup \"%s\" (seqno %u).", file, vg->seqno);
387 
388 	if (!(context = create_text_context(cmd, file, desc)) ||
389 	    !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
390 							 NULL, context))) {
391 		log_error("Couldn't create backup object.");
392 		return 0;
393 	}
394 
395 	/* Write and commit the metadata area */
396 	dm_list_iterate_items(mda, &tf->metadata_areas) {
397 		if (!(r = mda->ops->vg_write(tf, vg, mda))) {
398 			stack;
399 			continue;
400 		}
401 		if (mda->ops->vg_commit &&
402 		    !(r = mda->ops->vg_commit(tf, vg, mda))) {
403 			stack;
404 		}
405 	}
406 
407 	tf->fmt->ops->destroy_instance(tf);
408 	return r;
409 }
410 
411 /*
412  * Update backup (and archive) if they're out-of-date or don't exist.
413  */
414 void check_current_backup(struct volume_group *vg)
415 {
416 	char path[PATH_MAX];
417 	struct volume_group *vg_backup;
418 	int old_suppress;
419 
420 	if (vg_is_exported(vg))
421 		return;
422 
423 	if (dm_snprintf(path, sizeof(path), "%s/%s",
424 			 vg->cmd->backup_params->dir, vg->name) < 0) {
425 		log_debug("Failed to generate backup filename.");
426 		return;
427 	}
428 
429 	old_suppress = log_suppress(1);
430 	/* Up-to-date backup exists? */
431 	if ((vg_backup = backup_read_vg(vg->cmd, vg->name, path)) &&
432 	    (vg->seqno == vg_backup->seqno) &&
433 	    (id_equal(&vg->id, &vg_backup->id))) {
434 		log_suppress(old_suppress);
435 		vg_release(vg_backup);
436 		return;
437 	}
438 	log_suppress(old_suppress);
439 
440 	if (vg_backup) {
441 		archive(vg_backup);
442 		vg_release(vg_backup);
443 	}
444 	archive(vg);
445 	backup_locally(vg);
446 }
447