import source from lvm2 2.02.79
[external/device-mapper.git] / lib / format_text / archive.c
1 /*
2  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
4  *
5  * This file is part of LVM2.
6  *
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.
10  *
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
14  */
15
16 #include "lib.h"
17 #include "format-text.h"
18
19 #include "config.h"
20 #include "import-export.h"
21 #include "lvm-string.h"
22 #include "lvm-file.h"
23 #include "toolcontext.h"
24
25 #include <dirent.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <sys/file.h>
29 #include <fcntl.h>
30 #include <time.h>
31
32 #define SECS_PER_DAY 86400      /* 24*60*60 */
33
34 /*
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.
39  *
40  * The prefix ($1 from the above regex) of the config file gives
41  * the volume group name.
42  *
43  * Backup files that have expired will be removed.
44  */
45
46 /*
47  * A list of these is built up for our volume group.  Ordered
48  * with the least recent at the head.
49  */
50 struct archive_file {
51         struct dm_list list;
52
53         const char *path;
54         uint32_t index;
55 };
56
57 /*
58  * Extract vg name and version number from a filename.
59  */
60 static int _split_vg(const char *filename, char *vgname, size_t vgsize,
61                      uint32_t *ix)
62 {
63         size_t len, vg_len;
64         const char *dot, *underscore;
65
66         len = strlen(filename);
67         if (len < 7)
68                 return 0;
69
70         dot = (filename + len - 3);
71         if (strcmp(".vg", dot))
72                 return 0;
73
74         if (!(underscore = strrchr(filename, '_')))
75                 return 0;
76
77         if (sscanf(underscore + 1, "%u", ix) != 1)
78                 return 0;
79
80         vg_len = underscore - filename;
81         if (vg_len + 1 > vgsize)
82                 return 0;
83
84         strncpy(vgname, filename, vg_len);
85         vgname[vg_len] = '\0';
86
87         return 1;
88 }
89
90 static void _insert_archive_file(struct dm_list *head, struct archive_file *b)
91 {
92         struct archive_file *bf = NULL;
93
94         if (dm_list_empty(head)) {
95                 dm_list_add(head, &b->list);
96                 return;
97         }
98
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);
103                         return;
104                 }
105         }
106
107         dm_list_add_h(&bf->list, &b->list);
108 }
109
110 static char *_join_file_to_dir(struct dm_pool *mem, const char *dir, const char *name)
111 {
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))
117                 return_NULL;
118
119         return dm_pool_end_object(mem);
120 }
121
122 /*
123  * Returns a list of archive_files.
124  */
125 static struct dm_list *_scan_archive(struct dm_pool *mem,
126                                   const char *vgname, const char *dir)
127 {
128         int i, count;
129         uint32_t ix;
130         char vgname_found[64], *path;
131         struct dirent **dirent;
132         struct archive_file *af;
133         struct dm_list *results;
134
135         if (!(results = dm_pool_alloc(mem, sizeof(*results))))
136                 return_NULL;
137
138         dm_list_init(results);
139
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);
143                 return 0;
144         }
145
146         for (i = 0; i < count; i++) {
147                 if (!strcmp(dirent[i]->d_name, ".") ||
148                     !strcmp(dirent[i]->d_name, ".."))
149                         continue;
150
151                 /* check the name is the correct format */
152                 if (!_split_vg(dirent[i]->d_name, vgname_found,
153                                sizeof(vgname_found), &ix))
154                         continue;
155
156                 /* is it the vg we're interested in ? */
157                 if (strcmp(vgname, vgname_found))
158                         continue;
159
160                 if (!(path = _join_file_to_dir(mem, dir, dirent[i]->d_name)))
161                         goto_out;
162
163                 /*
164                  * Create a new archive_file.
165                  */
166                 if (!(af = dm_pool_alloc(mem, sizeof(*af)))) {
167                         log_error("Couldn't create new archive file.");
168                         results = NULL;
169                         goto out;
170                 }
171
172                 af->index = ix;
173                 af->path = path;
174
175                 /*
176                  * Insert it to the correct part of the list.
177                  */
178                 _insert_archive_file(results, af);
179         }
180
181       out:
182         for (i = 0; i < count; i++)
183                 free(dirent[i]);
184         free(dirent);
185
186         return results;
187 }
188
189 static void _remove_expired(struct dm_list *archives, uint32_t archives_size,
190                             uint32_t retain_days, uint32_t min_archive)
191 {
192         struct archive_file *bf;
193         struct stat sb;
194         time_t retain_time;
195
196         /* Make sure there are enough archives to even bother looking for
197          * expired ones... */
198         if (archives_size <= min_archive)
199                 return;
200
201         /* Convert retain_days into the time after which we must retain */
202         retain_time = time(NULL) - (time_t) retain_days *SECS_PER_DAY;
203
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);
209                         continue;
210                 }
211
212                 if (sb.st_mtime > retain_time)
213                         return;
214
215                 log_very_verbose("Expiring archive %s", bf->path);
216                 if (unlink(bf->path))
217                         log_sys_error("unlink", bf->path);
218
219                 /* Don't delete any more if we've reached the minimum */
220                 if (--archives_size <= min_archive)
221                         return;
222         }
223 }
224
225 int archive_vg(struct volume_group *vg,
226                const char *dir, const char *desc,
227                uint32_t retain_days, uint32_t min_archive)
228 {
229         int i, fd, rnum, renamed = 0;
230         uint32_t ix = 0;
231         struct archive_file *last;
232         FILE *fp = NULL;
233         char temp_file[PATH_MAX], archive_name[PATH_MAX];
234         struct dm_list *archives;
235
236         /*
237          * Write the vg out to a temporary file.
238          */
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.");
242                 return 0;
243         }
244
245         if (!(fp = fdopen(fd, "w"))) {
246                 log_error("Couldn't create FILE object for archive.");
247                 if (close(fd))
248                         log_sys_error("close", temp_file);
249                 return 0;
250         }
251
252         if (!text_vg_export_file(vg, desc, fp)) {
253                 if (fclose(fp))
254                         log_sys_error("fclose", temp_file);
255                 return_0;
256         }
257
258         if (lvm_fclose(fp, temp_file))
259                 return_0; /* Leave file behind as evidence of failure */
260
261         /*
262          * Now we want to rename this file to <vg>_index.vg.
263          */
264         if (!(archives = _scan_archive(vg->cmd->mem, vg->name, dir)))
265                 return_0;
266
267         if (dm_list_empty(archives))
268                 ix = 0;
269         else {
270                 last = dm_list_item(dm_list_first(archives), struct archive_file);
271                 ix = last->index + 1;
272         }
273
274         rnum = rand_r(&vg->cmd->rand_seed);
275
276         for (i = 0; i < 10; i++) {
277                 if (dm_snprintf(archive_name, sizeof(archive_name),
278                                  "%s/%s_%05u-%d.vg",
279                                  dir, vg->name, ix, rnum) < 0) {
280                         log_error("Archive file name too long.");
281                         return 0;
282                 }
283
284                 if ((renamed = lvm_rename(temp_file, archive_name)))
285                         break;
286
287                 ix++;
288         }
289
290         if (!renamed)
291                 log_error("Archive rename failed for %s", temp_file);
292
293         _remove_expired(archives, dm_list_size(archives) + renamed, retain_days,
294                         min_archive);
295
296         return 1;
297 }
298
299 static void _display_archive(struct cmd_context *cmd, struct archive_file *af)
300 {
301         struct volume_group *vg = NULL;
302         struct format_instance *tf;
303         time_t when;
304         char *desc;
305         void *context;
306
307         log_print(" ");
308         log_print("File:\t\t%s", af->path);
309
310         if (!(context = create_text_context(cmd, af->path, NULL)) ||
311             !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
312                                                          NULL, context))) {
313                 log_error("Couldn't create text instance object.");
314                 return;
315         }
316
317         /*
318          * Read the archive file to ensure that it is valid, and
319          * retrieve the archive time and description.
320          */
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);
325                 return;
326         }
327
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));
331
332         free_vg(vg);
333         tf->fmt->ops->destroy_instance(tf);
334 }
335
336 int archive_list(struct cmd_context *cmd, const char *dir, const char *vgname)
337 {
338         struct dm_list *archives;
339         struct archive_file *af;
340
341         if (!(archives = _scan_archive(cmd->mem, vgname, dir)))
342                 return_0;
343
344         if (dm_list_empty(archives))
345                 log_print("No archives found in %s.", dir);
346
347         dm_list_iterate_back_items(af, archives)
348                 _display_archive(cmd, af);
349
350         dm_pool_free(cmd->mem, archives);
351
352         return 1;
353 }
354
355 int archive_list_file(struct cmd_context *cmd, const char *file)
356 {
357         struct archive_file af;
358
359         af.path = file;
360
361         if (!path_exists(af.path)) {
362                 log_error("Archive file %s not found.", af.path);
363                 return 0;
364         }
365
366         _display_archive(cmd, &af);
367
368         return 1;
369 }
370
371 int backup_list(struct cmd_context *cmd, const char *dir, const char *vgname)
372 {
373         struct archive_file af;
374
375         if (!(af.path = _join_file_to_dir(cmd->mem, dir, vgname)))
376                 return_0;
377
378         if (path_exists(af.path))
379                 _display_archive(cmd, &af);
380
381         return 1;
382 }