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 
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 
archive_init(struct cmd_context * cmd,const char * dir,unsigned int keep_days,unsigned int keep_min,int enabled)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 
archive_exit(struct cmd_context * cmd)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 
archive_enable(struct cmd_context * cmd,int flag)77 void archive_enable(struct cmd_context *cmd, int flag)
78 {
79           cmd->archive_params->enabled = flag;
80 }
81 
_build_desc(struct dm_pool * mem,const char * line,int before)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 
__archive(struct volume_group * vg)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 
archive(struct volume_group * vg)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 #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 
archive_display(struct cmd_context * cmd,const char * vg_name)145 int 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 
archive_display_file(struct cmd_context * cmd,const char * file)155 int 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 
backup_init(struct cmd_context * cmd,const char * dir,int enabled)164 int 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 
backup_exit(struct cmd_context * cmd)186 void 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 
backup_enable(struct cmd_context * cmd,int flag)195 void backup_enable(struct cmd_context *cmd, int flag)
196 {
197           cmd->backup_params->enabled = flag;
198 }
199 
__backup(struct volume_group * vg)200 static 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 
backup_locally(struct volume_group * vg)218 int 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 
backup(struct volume_group * vg)253 int 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 
backup_remove(struct cmd_context * cmd,const char * vg_name)261 int 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 
backup_read_vg(struct cmd_context * cmd,const char * vg_name,const char * file)278 struct 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 */
backup_restore_vg(struct cmd_context * cmd,struct volume_group * vg)305 int 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 */
backup_restore_from_file(struct cmd_context * cmd,const char * vg_name,const char * file)352 int 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 
backup_restore(struct cmd_context * cmd,const char * vg_name)375 int 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 
backup_to_file(const char * file,const char * desc,struct volume_group * vg)388 int 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  */
check_current_backup(struct volume_group * vg)426 void 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