source: MondoRescue/trunk/mondo/src/common/libmondo-filelist.c@ 900

Last change on this file since 900 was 900, checked in by Bruno Cornec, 18 years ago

Huge patch to introduce low level functions that will bw used everywhere (mr_free, mr_asprintf, ...)
Nearly linking now due to that.

  • Property svn:keywords set to Id
File size: 49.3 KB
Line 
1/* $Id: libmondo-filelist.c 900 2006-10-24 06:49:18Z bruno $
2
3for subroutines which manipulate the filelist
4*/
5
6/**
7 * @file
8 * Functions which create, chop, and edit the filelist.
9 */
10
11#include "my-stuff.h"
12#include "mondostructures.h"
13#include "libmondo-filelist.h"
14#include "libmondo-string-EXT.h"
15#include "libmondo-files-EXT.h"
16#include "libmondo-fork-EXT.h"
17#include "newt-specific-EXT.h"
18#include "libmondo-tools-EXT.h"
19#include "mr_str.h"
20#include "mr_mem.h"
21
22#include <time.h>
23#include <stdio.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <dirent.h>
27#include <errno.h>
28#include <stdio.h>
29
30
31/**
32 * The maximum depth of directories to put in the skeleton filelist.
33 * This is a balance between performance and a good progress indicator.
34 */
35#define MAX_SKEL_DEPTH 3
36
37
38int mondo_makefilelist(char *logfile, char *tmpdir, char *scratchdir,
39 char *include_paths, char *excp, int differential,
40 char *userdef_filelist);
41
42
43/*@unused@*/
44//static char cvsid[] = "$Id: libmondo-filelist.c 900 2006-10-24 06:49:18Z bruno $";
45
46/**
47 * Number of lines in the filelist last loaded.
48 * @warning This implies that two filesets cannot be loaded at once.
49 * @ingroup globalGroup
50 */
51long g_original_noof_lines_in_filelist = 0;
52
53/**
54 * Number of filesets in the current backup.
55 * @ingroup globalGroup
56 */
57long g_noof_sets = 0;
58
59extern bool g_text_mode;
60extern newtComponent g_progressForm;
61extern int g_currentY;
62extern int g_noof_rows;
63
64
65/**
66 * @addtogroup filelistGroup
67 * @{
68 */
69/**
70 * Call chop_filelist() to chop the filelist into sets.
71 * @param bkpinfo The backup information structure. Fields used:
72 * - @c bkpinfo->image_devs
73 * - @c bkpinfo->optimal_set_size
74 * - @c bkpinfo->scratchdir
75 * - @c bkpinfo->tmpdir
76 * @see chop_filelist
77 */
78int call_filelist_chopper(struct s_bkpinfo *bkpinfo)
79{
80 /*@ buffers *********************** */
81 char *dev;
82 char *filelist;
83 char *tempfile;
84 long noof_sets;
85
86 /*@ pointers ********************** */
87 char *ptr;
88 FILE *fout;
89
90 /*@ int *************************** */
91 int i, retval = 0;
92
93 mvaddstr_and_log_it(g_currentY, 0, _("Dividing filelist into sets"));
94
95 log_to_screen(_("Dividing filelist into sets. Please wait."));
96 i = 0;
97/*
98 if (find_home_of_exe("getfattr"))
99 { i++; log_to_screen ("NEW! Recording extended attributes."); }
100 if (find_home_of_exe("getfacl"))
101 { i++; log_to_screen ("NEW! Recording ACL information."); }
102 if (i)
103 { i=0; log_to_screen ("This will take more time. Please be patient."); }
104*/
105 mr_asprintf(&filelist, "%s/archives/filelist.full", bkpinfo->scratchdir);
106 if (!does_file_exist(filelist)) {
107 log_it("filelist %s not found", filelist);
108 fatal_error("call_filelist_chopper() -- filelist not found!");
109 }
110
111 noof_sets =
112 chop_filelist(filelist, bkpinfo->tmpdir,
113 bkpinfo->optimal_set_size);
114 mr_free(filelist);
115
116 estimate_noof_media_required(bkpinfo, noof_sets); // for cosmetic purposes
117
118 mr_asprintf(&tempfile, "%s/biggielist.txt", bkpinfo->tmpdir);
119 if (!(fout = fopen(tempfile, "a"))) {
120 log_OS_error("Cannot append to biggielist");
121 retval++;
122 }
123 mr_free(tempfile);
124 log_it(bkpinfo->image_devs);
125
126 ptr = bkpinfo->image_devs;
127
128 while (ptr && *ptr) {
129 mr_asprintf(&dev, ptr);
130 log_it("Examining imagedev %s", dev);
131 for (i = 0; i < (int) strlen(dev) && dev[i] != ' '; i++);
132 dev[i] = '\0';
133 if (!strlen(dev)) {
134 continue;
135 }
136 fprintf(fout, "%s\n", dev);
137 log_it("Adding '%s' to biggielist", dev);
138 mr_free(dev);
139
140 if ((ptr = strchr(ptr, ' '))) {
141 ptr++;
142 }
143 }
144 paranoid_fclose(fout);
145 mvaddstr_and_log_it(g_currentY++, 74, "Done.");
146
147 return (retval);
148}
149
150
151
152int sort_file(char *orig_fname)
153{
154 char *tmp_fname;
155 char *command;
156 int retval = 0;
157
158 log_msg(1, "Sorting file %s", orig_fname);
159 mr_asprintf(&tmp_fname, "/tmp/sort.%d.%d.%d", (int) (random() % 32768),
160 (int) (random() % 32768), (int) (random() % 32768));
161
162 if (!does_file_exist(orig_fname)) {
163 return (0);
164 } // no sense in trying to sort an empty file
165
166 mr_asprintf(&command, "sort %s > %s 2>> %s", orig_fname, tmp_fname,
167 MONDO_LOGFILE);
168 retval = system(command);
169 mr_free(command);
170
171 if (retval) {
172 log_msg(2, "Failed to sort %s - oh dear", orig_fname);
173 } else {
174 log_msg(2, "Sorted %s --> %s OK. Copying it back to %s now",
175 orig_fname, tmp_fname, orig_fname);
176 mr_asprintf(&command, "mv -f %s %s", tmp_fname, orig_fname);
177 retval += run_program_and_log_output(command, 2);
178 mr_free(command);
179
180 if (retval) {
181 log_msg(2, "Failed to copy %s back to %s - oh dear", tmp_fname,
182 orig_fname);
183 } else {
184 log_msg(2, "%s was sorted OK.", orig_fname);
185 }
186 }
187 mr_free(tmp_fname);
188 log_msg(1, "Finished sorting file %s", orig_fname);
189 return (retval);
190}
191
192
193/**
194 * Chop the filelist into sets.
195 * Each fileset is a list of files whose total (uncompressed) size is usually
196 * about X KB. Files bigger than 8X KB are placed in a "biggielist"; they will
197 * be sliced and compressed separately from the regular files.
198 *
199 * @param filelist The big filelist (filelist.full) to chop up.
200 * @param outdir The directory to place the files (filelist.N where N is
201 * an integer, biggielist.txt, and LAST-FILELIST-NUMBER) created
202 * @param maxsetsizeK Optimal size of a fileset (X above).
203 * @return number of errors encountered (0 for success).
204 */
205int chop_filelist(char *filelist, char *outdir, long maxsetsizeK)
206{
207/*@ long ****************************************/
208 long lino = 0;
209 long max_sane_size_for_a_file;
210 long curr_set_size;
211 long noof_lines;
212 long siz;
213
214 /*@ int **************************************** */
215 int i;
216 size_t n = 0;
217 long curr_set_no;
218
219 /*@ buffers ************************************* */
220 char *outfname;
221 char *biggie_fname;
222 char *incoming = NULL;
223 char *tmp;
224
225 /*@ pointers *********************************** */
226 FILE *fin;
227 FILE *fout;
228 FILE *fbig;
229
230 /*@ structures ********************************* */
231 struct stat buf;
232
233 assert_string_is_neither_NULL_nor_zerolength(filelist);
234 assert_string_is_neither_NULL_nor_zerolength(outdir);
235 assert(maxsetsizeK > 0);
236
237 max_sane_size_for_a_file = 32L * 1024L;
238// max_sane_size_for_a_file = maxsetsizeK*2;
239// if (max_sane_size_for_a_file > 32*1024)
240// { max_sane_size_for_a_file = 32*1024; }
241
242 log_it("filelist=%s;", filelist);
243 open_evalcall_form("Dividing filelist into sets");
244 noof_lines = count_lines_in_file(filelist);
245 if (!(fin = fopen(filelist, "r"))) {
246 log_OS_error("Cannot openin filelist");
247 return (0);
248 }
249 curr_set_no = 0;
250 curr_set_size = 0;
251 mr_asprintf(&outfname, "%s/filelist.%ld", outdir, curr_set_no);
252 mr_asprintf(&biggie_fname, "%s/biggielist.txt", outdir);
253 log_it("outfname=%s; biggie_fname=%s", outfname, biggie_fname);
254 if (!(fbig = fopen(biggie_fname, "w"))) {
255 log_OS_error("Cannot openout biggie_fname");
256 return (0);
257 }
258 if (!(fout = fopen(outfname, "w"))) {
259 log_OS_error("Cannot openout outfname");
260 return (0);
261 }
262 mr_getline(&incoming, &n, fin);
263 while (!feof(fin)) {
264 lino++;
265 i = strlen(incoming) - 1;
266 if (i < 0) {
267 i = 0;
268 }
269 if (incoming[i] < 32) {
270 incoming[i] = '\0';
271 }
272 if (!strncmp(incoming, "/dev/", 5)) {
273 siz = 1;
274 } else if (lstat(incoming, &buf) != 0) {
275 siz = 0;
276 } else {
277 siz = (long) (buf.st_size >> 10);
278 }
279 if (siz > max_sane_size_for_a_file)
280// && strcmp(incoming+strlen(incoming)-4, ".bz2") && strcmp(incoming+strlen(incoming)-4, ".tbz"))
281 {
282 fprintf(fbig, "%s\n", incoming);
283 } else {
284 curr_set_size += siz;
285 fprintf(fout, "%s\n", incoming);
286 if (curr_set_size > maxsetsizeK) {
287 paranoid_fclose(fout);
288 sort_file(outfname);
289 mr_free(outfname);
290
291 curr_set_no++;
292 curr_set_size = 0;
293 mr_asprintf(&outfname, "%s/filelist.%ld", outdir,
294 curr_set_no);
295 if (!(fout = fopen(outfname, "w"))) {
296 log_OS_error("Unable to openout outfname");
297 return (0);
298 }
299 update_evalcall_form((int) (lino * 100 / noof_lines));
300 }
301 }
302 mr_getline(&incoming, &n, fin);
303 }
304 paranoid_fclose(fin);
305 paranoid_fclose(fout);
306 paranoid_fclose(fbig);
307
308 if (length_of_file(outfname) <= 2) {
309 unlink(outfname);
310 g_noof_sets--;
311 }
312 g_noof_sets = curr_set_no;
313 sort_file(outfname);
314 sort_file(biggie_fname);
315 mr_free(biggie_fname);
316 mr_free(outfname);
317
318 mr_asprintf(&outfname, "%s/LAST-FILELIST-NUMBER", outdir);
319 mr_asprintf(&tmp, "%ld", curr_set_no);
320 if (write_one_liner_data_file(outfname, tmp)) {
321 log_OS_error
322 ("Unable to echo write one-liner to LAST-FILELIST-NUMBER");
323 return (0);
324 }
325 mr_free(outfname);
326 mr_free(tmp);
327
328 if (curr_set_no == 0) {
329 mr_asprintf(&tmp, "Only one fileset. Fine.");
330 } else {
331 mr_asprintf(&tmp, "Filelist divided into %ld sets", curr_set_no + 1);
332 }
333 log_msg(1, tmp);
334 mr_free(tmp);
335 close_evalcall_form();
336 /* This is to work around an obscure bug in Newt; open a form, close it,
337 carry on... I don't know why it works but it works. If you don't do this
338 then update_progress_form() won't show the "time taken / time remaining"
339 line. The bug only crops up AFTER the call to chop_filelist(). Weird. */
340#ifndef _XWIN
341 if (!g_text_mode) {
342 open_progress_form("", "", "", "", 100);
343 newtPopHelpLine();
344 newtFormDestroy(g_progressForm);
345 newtPopWindow();
346 }
347#endif
348 return (curr_set_no + 1);
349}
350
351
352/**
353 * Free all the memory used by a filelist structure.
354 * Since this may take a long time for large filelists, a progress bar will be displayed.
355 * @param filelist The filelist to free.
356 */
357void free_filelist(struct s_node *filelist)
358{
359 /*@ int's ******************************************************* */
360 static int depth = 0;
361 int percentage;
362
363 /*@ long's ****************************************************** */
364 static long i = 0;
365
366 /*@ end vars **************************************************** */
367
368 assert(filelist != NULL);
369 if (depth == 0) {
370 open_evalcall_form("Freeing memory");
371 log_to_screen(_("Freeing memory formerly occupied by filelist"));
372 }
373 depth++;
374
375 if (filelist->ch == '\0') {
376 if (!(i++ % 1111)) {
377 percentage =
378 (int) (i * 100 / g_original_noof_lines_in_filelist);
379 update_evalcall_form(percentage);
380
381 }
382 }
383
384 if (filelist->right) {
385 free_filelist(filelist->right);
386 filelist->right = NULL;
387 }
388 if (filelist->down) {
389/* if (!(i++ %39999)) { update_evalcall_form(0); } */
390 free_filelist(filelist->down);
391 filelist->down = NULL;
392 }
393 filelist->ch = '\0';
394 mr_free((void *)filelist);
395 depth--;
396 if (depth == 0) {
397 close_evalcall_form();
398 log_it("Finished freeing memory");
399 }
400}
401
402
403int call_exe_and_pipe_output_to_fd(char *syscall, FILE * pout)
404{
405 FILE *pattr;
406 char *tmp = NULL;
407 size_t n = 0;
408
409 pattr = popen(syscall, "r");
410 if (!pattr) {
411 log_msg(1, "Failed to open fattr() %s", syscall);
412 return (1);
413 }
414 if (feof(pattr)) {
415 log_msg(1, "Failed to call fattr() %s", syscall);
416 paranoid_pclose(pattr);
417 return (2);
418 }
419 for (mr_getline(&tmp, &n, pattr); !feof(pattr); mr_getline(&tmp, &n, pattr)) {
420 fputs(tmp, pout);
421 }
422 paranoid_pclose(pattr);
423 mr_free(tmp);
424 return (0);
425}
426
427
428int gen_aux_list(char *filelist, char *syscall_sprintf,
429 char *auxlist_fname)
430{
431 FILE *fin;
432 FILE *pout;
433 char *pout_command;
434 char *syscall;
435 char *file_to_analyze = NULL;
436 int i;
437 size_t n = 0;
438
439 if (!(fin = fopen(filelist, "r"))) {
440 log_msg(1, "Cannot openin filelist %s", filelist);
441 return (1);
442 }
443 mr_asprintf(&pout_command, "gzip -c1 > %s", auxlist_fname);
444 if (!(pout = popen(pout_command, "w"))) {
445 log_msg(1, "Cannot openout auxlist_fname %s", auxlist_fname);
446 fclose(fin);
447 mr_free(pout_command);
448 return (4);
449 }
450 mr_free(pout_command);
451
452 for (mr_getline(&file_to_analyze, &n, fin); !feof(fin);
453 mr_getline(&file_to_analyze, &n, fin)) {
454 i = strlen(file_to_analyze);
455 if (i > 0 && file_to_analyze[i - 1] < 32) {
456 file_to_analyze[i - 1] = '\0';
457 }
458 log_msg(8, "Analyzing %s", file_to_analyze);
459 /* BERLIOS : to be checked */
460 mr_asprintf(&syscall, "%s %s 2>> /dev/null", syscall_sprintf,mr_stresc(file_to_analyze, "`$\\\"", '\\'));
461 call_exe_and_pipe_output_to_fd(syscall, pout);
462 mr_free(syscall);
463 }
464 paranoid_fclose(fin);
465 paranoid_pclose(pout);
466 mr_free(file_to_analyze);
467 return (0);
468}
469
470
471int get_acl_list(char *filelist, char *facl_fname)
472{
473 char *command = NULL;
474 char *tmp = NULL;
475 int retval = 0;
476
477 mr_asprintf(&command, "touch %s", facl_fname);
478 run_program_and_log_output(command, 8);
479 mr_free(command);
480
481 tmp = find_home_of_exe("getfacl");
482 if (tmp) {
483// sort_file(filelist); // FIXME - filelist chopper sorts, so this isn't necessary
484 mr_asprintf(&command,
485 "getfacl --all-effective -P %s 2>> %s | gzip -c1 > %s 2>> %s",
486 filelist, MONDO_LOGFILE, facl_fname, MONDO_LOGFILE);
487 iamhere(command);
488 retval = system(command);
489 mr_free(command);
490 }
491 mr_free(tmp);
492 return (retval);
493}
494
495
496int get_fattr_list(char *filelist, char *fattr_fname)
497{
498 char *command = NULL;
499 char *tmp = NULL;
500 int retval = 0;
501
502 mr_asprintf(&command, "touch %s", fattr_fname);
503 run_program_and_log_output(command, 8);
504 mr_free(command);
505
506 tmp = find_home_of_exe("getfattr");
507 if (tmp) {
508// sort_file(filelist); // FIXME - filelist chopper sorts, so this isn't necessary
509 retval =
510 gen_aux_list(filelist, "getfattr --en=hex -P -d \"%s\"",
511 fattr_fname);
512 }
513 mr_free(tmp);
514 return (retval);
515}
516
517
518/*
519int set_acl_list(char*masklist, char*acl_fname)
520{
521 char*command;
522 int retval=0;
523
524 if (length_of_file(acl_fname) <= 0) { return(0); }
525 log_msg(1, "FIXME - not using masklist");
526 malloc_string(command);
527 if (find_home_of_exe("setfacl"))
528 {
529 sprintf(command, "gzip -dc %s | setfacl --restore - 2>> %s", acl_fname, MONDO_LOGFILE);
530 log_msg(1, "command = %s", command);
531 retval = system(command);
532 }
533 mr_free(command);
534 return(retval);
535}
536*/
537
538
539int set_EXAT_list(char *orig_msklist, char *original_exat_fname,
540 char *executable)
541{
542 const int my_depth = 8;
543 char *command, *syscall_pin, *syscall_pout;
544 char *incoming = NULL;
545 char *current_subset_file = NULL;
546 char *current_master_file, *masklist;
547 int retval = 0;
548 int i;
549 size_t n = 0;
550 char *p, *q;
551 FILE *pin, *pout, *faclin;
552
553 log_msg(1, "set_EXAT_list(%s, %s, %s)", orig_msklist,
554 original_exat_fname, executable);
555 if (!orig_msklist || !orig_msklist[0]
556 || !does_file_exist(orig_msklist)) {
557 log_msg(1,
558 "No masklist provided. I shall therefore set ALL attributes.");
559 mr_asprintf(&command, "gzip -dc %s | %s --restore - 2>> %s",
560 original_exat_fname, executable, MONDO_LOGFILE);
561 log_msg(1, "command = %s", command);
562 retval = system(command);
563 mr_free(command);
564 log_msg(1, "Returning w/ retval=%d", retval);
565 return (retval);
566 }
567 if (length_of_file(original_exat_fname) <= 0) {
568 log_msg(1,
569 "original_exat_fname %s is empty or missing, so no need to set EXAT list",
570 original_exat_fname);
571 return (0);
572 }
573 mr_asprintf(&masklist, "/tmp/%d.%d.mask", (int) (random() % 32768),
574 (int) (random() % 32768));
575 mr_asprintf(&command, "cp -f %s %s", orig_msklist, masklist);
576 run_program_and_log_output(command, 1);
577 mr_free(command);
578
579 sort_file(masklist);
580 mr_asprintf(&syscall_pin, "gzip -dc %s", original_exat_fname);
581 mr_asprintf(&syscall_pout, "%s --restore - 2>> %s", executable,
582 MONDO_LOGFILE);
583
584 log_msg(1, "syscall_pin = %s", syscall_pin);
585 log_msg(1, "syscall_pout = %s", syscall_pout);
586 pout = popen(syscall_pout, "w");
587 if (!pout) {
588 iamhere("Unable to openout to syscall_pout");
589 return (1);
590 }
591 mr_free(syscall_pout);
592
593 pin = popen(syscall_pin, "r");
594 if (!pin) {
595 pclose(pout);
596 iamhere("Unable to openin from syscall");
597 return (1);
598 }
599 mr_free(syscall_pin);
600
601 faclin = fopen(masklist, "r");
602 if (!faclin) {
603 pclose(pin);
604 pclose(pout);
605 iamhere("Unable to openin masklist");
606 return (1);
607 }
608// printf("Hi there. Starting the loop\n");
609
610 mr_getline(&current_subset_file, &n, faclin);
611 mr_getline(&incoming, &n, pin);
612 while (!feof(pin) && !feof(faclin)) {
613// printf("incoming = %s", incoming);
614
615 mr_asprintf(&current_master_file, incoming + 8);
616
617 p = current_subset_file;
618 if (*p == '/') {
619 p++;
620 }
621 i = strlen(p);
622 if (i > 0 && p[i - 1] < 32) {
623 p[i - 1] = '\0';
624 }
625
626
627 q = current_master_file;
628 if (*q == '/') {
629 q++;
630 }
631 i = strlen(q);
632 if (i > 0 && q[i - 1] < 32) {
633 q[i - 1] = '\0';
634 }
635
636 i = strcmp(p, q);
637 log_msg(my_depth, "'%s' v '%s' --> %d\n", p, q, i);
638
639// printf("%s v %s --> %d\n", p, q, i);
640
641 if (i < 0) { // read another subset file in.
642 log_msg(my_depth, "Reading next subset line in\n\n");
643 mr_getline(&current_subset_file, &n, faclin);
644 continue;
645 }
646
647 if (!i) {
648 fputs(incoming, pout);
649 }
650 mr_getline(&incoming, &n, pin);
651 if (!i) {
652 log_msg(my_depth, "Copying master %s", q);
653 }
654// if (!i) { printf("Match --- %s\n", q); }
655
656 while (!feof(pin) && strncmp(incoming, "# file: ", 8)) {
657 if (!i) {
658
659// printf("%s", incoming);
660
661 fputs(incoming, pout);
662 }
663 mr_getline(&incoming, &n, pin);
664 }
665 if (!i) {
666 mr_getline(&current_subset_file, &n, faclin);
667 }
668 mr_free(current_master_file);
669 }
670 mr_free(current_subset_file);
671
672 while (!feof(pin)) {
673 mr_getline(&incoming, &n, pin);
674 }
675 mr_free(incoming);
676 fclose(faclin);
677 pclose(pin);
678 pclose(pout);
679
680// printf("OK, loop is done\n");
681
682 unlink(masklist);
683 mr_free(masklist);
684
685 return (retval);
686}
687
688
689int set_fattr_list(char *masklist, char *fattr_fname)
690{
691 return (set_EXAT_list(masklist, fattr_fname, "setfattr"));
692}
693
694
695
696int set_acl_list(char *masklist, char *acl_fname)
697{
698 return (set_EXAT_list(masklist, acl_fname, "setfacl"));
699}
700
701
702/**
703 * Get the number of the last fileset in the backup.
704 * @param bkpinfo The backup information structure. Only the @c bkpinfo->tmpdir field is used.
705 * @return The last filelist number.
706 * @note This function should only be called at restore-time.
707 */
708int get_last_filelist_number(struct s_bkpinfo *bkpinfo)
709{
710 /*@ buffers ***************************************************** */
711 char *val_sz = NULL;
712 char *cfg_fname = NULL;
713
714 /*@ long ******************************************************** */
715 int val_i = 0;
716
717 /*@ end vars **************************************************** */
718
719 assert(bkpinfo != NULL);
720
721 mr_asprintf(&cfg_fname, "%s/mondo-restore.cfg", bkpinfo->tmpdir);
722 read_cfg_var(cfg_fname, "last-filelist-number", val_sz);
723 val_i = atoi(val_sz);
724 mr_free(val_sz);
725
726 if (val_i <= 0) {
727 val_i = 500;
728 }
729 mr_free(cfg_fname);
730 return (val_i);
731}
732
733
734/**
735 * Add a string at @p startnode.
736 * @param startnode The node to start at when searching for where to add the string.
737 * @param string_to_add The string to add.
738 * @return 0 for success, 1 for failure.
739 * @bug I don't understand this function. Would someone care to explain it?
740 */
741
742int add_string_at_node(struct s_node *startnode, char *string_to_add)
743{
744
745 int noof_chars;
746 int i;
747 int res;
748
749 struct s_node *node, *newnode;
750
751 char char_to_add;
752
753 const bool sosodef = FALSE;
754
755 static int depth = 0;
756 static char original_string[MAX_STR_LEN];
757
758 assert(startnode != NULL);
759 assert(string_to_add != NULL);
760
761 if (!depth) {
762 strcpy(original_string, string_to_add);
763 }
764
765 noof_chars = strlen(string_to_add) + 1; // we include the '\0'
766
767 // walk across tree if necessary
768 node = startnode;
769 char_to_add = string_to_add[0];
770 if (node->right != NULL && node->ch < char_to_add) {
771 log_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth,
772 char_to_add, node->ch, (node->right)->ch);
773 return (add_string_at_node(node->right, string_to_add));
774 }
775 // walk down tree if appropriate
776 if (node->down != NULL && node->ch == char_to_add) {
777 log_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_add);
778 depth++;
779 res = add_string_at_node(node->down, string_to_add + 1);
780 depth--;
781 return (res);
782 }
783
784 if (char_to_add == '\0' && node->ch == '\0') {
785 log_msg(6, "%s already in tree", original_string);
786 return (1);
787 }
788 // add here
789 if (!(newnode = (struct s_node *) malloc(sizeof(struct s_node)))) {
790 log_to_screen(_("failed to malloc"));
791 depth--;
792 return (1);
793 }
794 if (char_to_add < node->ch) // add to the left of node
795 {
796 log_msg(7, "depth=%d char=%c --- adding (left)", depth,
797 char_to_add);
798 memcpy((void *) newnode, (void *) node, sizeof(struct s_node));
799 node->right = newnode;
800 } else if (char_to_add > node->ch) // add to the right of node
801 {
802 log_msg(7, "depth=%d char=%c --- adding (right)", depth,
803 char_to_add);
804 newnode->right = node->right; // newnode is to the RIGHT of node
805 node->right = newnode;
806 node = newnode;
807 }
808 // from now on, we're working on 'node'
809 node->down = NULL;
810 node->ch = char_to_add;
811 node->expanded = node->selected = FALSE;
812 if (char_to_add == '\0') {
813 log_msg(6, "Added %s OK", original_string);
814 return (0);
815 }
816 // add the rest
817 log_msg(6, "Adding remaining chars ('%s')", string_to_add + 1);
818 for (i = 1; i < noof_chars; i++) {
819 if (!
820 (node->down =
821 (struct s_node *) malloc(sizeof(struct s_node)))) {
822 log_to_screen(_("%s - failed to malloc"), string_to_add);
823 return (1);
824 }
825 node = node->down;
826 char_to_add = string_to_add[i];
827 log_msg(6, "Adding '%c'", char_to_add);
828 node->ch = char_to_add;
829 node->right = node->down = NULL;
830 node->expanded = node->selected = FALSE;
831 if (!node->ch) {
832 node->selected = sosodef;
833 }
834 }
835 log_msg(6, "Finally - added %s OK", original_string);
836 return (0);
837}
838
839
840/**
841 * Load a filelist into a <tt>struct s_node</tt>.
842 * When you are done with the filelist, call free_filelist().
843 * @param filelist_fname The file to load the filelist from.
844 * @return A filelist tree structure.
845 */
846struct s_node *load_filelist(char *filelist_fname)
847{
848
849 struct s_node *filelist = NULL;
850 FILE *pin = NULL;
851 char *command_to_open_fname = NULL;
852 char *fname = NULL;
853 char *tmp = NULL;
854 char *tmp1 = NULL;
855 int pos_in_fname = 0;
856 size_t n = 0;
857 int percentage = 0;
858
859 long lines_in_filelist = 0L;
860 long lino = 0L;
861
862 assert_string_is_neither_NULL_nor_zerolength(filelist_fname);
863
864 if (!does_file_exist(filelist_fname)) {
865 fatal_error("filelist does not exist -- cannot load it");
866 }
867 log_to_screen(_("Loading filelist"));
868 mr_asprintf(&command_to_open_fname, "gzip -dc %s", filelist_fname);
869 mr_asprintf(&tmp, "zcat %s | wc -l", filelist_fname);
870 log_msg(6, "tmp = %s", tmp);
871 tmp1 = call_program_and_get_last_line_of_output(tmp);
872 mr_free(tmp);
873
874 lines_in_filelist = atol(tmp1);
875 mr_free(tmp1);
876
877 if (lines_in_filelist < 3) {
878 log_to_screen(_("Warning - surprisingly short filelist."));
879 }
880 g_original_noof_lines_in_filelist = lines_in_filelist;
881 if (!(filelist = (struct s_node *) malloc(sizeof(struct s_node)))) {
882 return (NULL);
883 }
884 filelist->ch = '/';
885 filelist->right = NULL;
886 filelist->down = malloc(sizeof(struct s_node));
887 filelist->expanded = filelist->selected = FALSE;
888 (filelist->down)->ch = '\0';
889 (filelist->down)->right = (filelist->down)->down = FALSE;
890 (filelist->down)->expanded = (filelist->down)->selected = FALSE;
891 if (!(pin = popen(command_to_open_fname, "r"))) {
892 log_OS_error("Unable to openin filelist_fname");
893 return (NULL);
894 }
895 mr_free(command_to_open_fname);
896
897 open_evalcall_form(_("Loading filelist from disk"));
898 for (mr_getline(&fname, &n, pin); !feof(pin); mr_getline(&fname, &n, pin)) {
899 if ((fname[strlen(fname) - 1] == 13
900 || fname[strlen(fname) - 1] == 10) && strlen(fname) > 0) {
901 fname[strlen(fname) - 1] = '\0';
902 }
903 if (!strlen(fname)) {
904 continue;
905 }
906 for (pos_in_fname = 0; fname[pos_in_fname] != '\0'; pos_in_fname++) {
907 if (fname[pos_in_fname] != '/') {
908 continue;
909 }
910 mr_asprintf(&tmp, fname);
911 tmp[pos_in_fname] = '\0';
912 if (strlen(tmp)) {
913 add_string_at_node(filelist, tmp);
914 }
915 mr_free(tmp);
916 }
917 add_string_at_node(filelist, fname);
918 if (!(++lino % 1111)) {
919 percentage = (int) (lino * 100 / lines_in_filelist);
920 update_evalcall_form(percentage);
921 }
922 }
923 mr_free(fname);
924 paranoid_pclose(pin);
925 close_evalcall_form();
926 log_it("Finished loading filelist");
927 return (filelist);
928}
929
930
931/**
932 * Log a list of files in @p node.
933 * @param node The toplevel node to use.
934 */
935void show_filelist(struct s_node *node)
936{
937 static int depth = 0;
938 static char current_string[200];
939
940 if (depth == 0) {
941 log_msg(0, "----------------show filelist--------------");
942 }
943 current_string[depth] = node->ch;
944
945 log_msg(3, "depth=%d", depth);
946 if (node->down) {
947 log_msg(3, "moving down");
948 depth++;
949 show_filelist(node->down);
950 depth--;
951 }
952
953 if (!node->ch) {
954 log_msg(0, "%s\n", current_string);
955 }
956
957 if (node->right) {
958 log_msg(3, "moving right");
959 show_filelist(node->right);
960 }
961 if (depth == 0) {
962 log_msg(0, "----------------show filelist--------------");
963 }
964 return;
965}
966
967
968/**
969 * Reset the filelist to the state it was when it was loaded. This does not
970 * touch the file on disk.
971 * @param filelist The filelist tree structure.
972 */
973void reload_filelist(struct s_node *filelist)
974{
975 assert(filelist != NULL);
976 toggle_node_selection(filelist, FALSE);
977 toggle_path_expandability(filelist, "/", FALSE);
978 toggle_all_root_dirs_on(filelist);
979}
980
981
982/**
983 * Save a filelist tree structure to disk.
984 * @param filelist The filelist tree structure to save.
985 * @param outfname Where to save it.
986 */
987void save_filelist(struct s_node *filelist, char *outfname)
988{
989 /*@ int ********************************************************* */
990 static int percentage;
991 static int depth = 0;
992
993 /*@ buffers ***************************************************** */
994 static char str[MAX_STR_LEN];
995
996 /*@ structures ************************************************** */
997 struct s_node *node;
998
999 /*@ pointers **************************************************** */
1000 static FILE *fout = NULL;
1001
1002 /*@ long ******************************************************** */
1003 static long lines_in_filelist = 0;
1004 static long lino = 0;
1005
1006 /*@ end vars *************************************************** */
1007
1008 assert(filelist != NULL);
1009 assert(outfname != NULL); // will be zerolength if save_filelist() is called by itself
1010 if (depth == 0) {
1011 log_to_screen(_("Saving filelist"));
1012 if (!(fout = fopen(outfname, "w"))) {
1013 fatal_error("Cannot openout/save filelist");
1014 }
1015 lines_in_filelist = g_original_noof_lines_in_filelist; /* set by load_filelist() */
1016 open_evalcall_form(_("Saving selection to disk"));
1017 }
1018 for (node = filelist; node != NULL; node = node->right) {
1019 str[depth] = node->ch;
1020 log_msg(5, "depth=%d ch=%c", depth, node->ch);
1021 if (!node->ch) {
1022// if (node->selected)
1023// {
1024 fprintf(fout, "%s\n", str);
1025// }
1026 if (!(++lino % 1111)) {
1027 percentage = (int) (lino * 100 / lines_in_filelist);
1028 update_evalcall_form(percentage);
1029 }
1030 }
1031 if (node->down) {
1032 depth++;
1033 save_filelist(node->down, "");
1034 depth--;
1035 }
1036 }
1037 if (depth == 0) {
1038 paranoid_fclose(fout);
1039 close_evalcall_form();
1040 log_it("Finished saving filelist");
1041 }
1042}
1043
1044
1045/**
1046 * Toggle all root dirs on.
1047 * @param filelist The filelist tree structure to operate on.
1048 * @bug I don't understand this function. Would someone care to explain it?
1049 */
1050void toggle_all_root_dirs_on(struct s_node *filelist)
1051{
1052 /*@ structures ************************************************** */
1053 struct s_node *node;
1054
1055 /*@ int ********************************************************* */
1056 static int depth = 0;
1057 static int root_dirs_expanded;
1058
1059 /*@ buffers ***************************************************** */
1060 static char filename[MAX_STR_LEN];
1061
1062 /*@ end vars *************************************************** */
1063
1064 assert(filelist != NULL);
1065 if (depth == 0) {
1066 log_it("Toggling all root dirs ON");
1067 root_dirs_expanded = 0;
1068 }
1069 for (node = filelist; node != NULL; node = node->right) {
1070 filename[depth] = node->ch;
1071 if (node->ch == '\0' && strlen(filename) > 1
1072 && (!strchr(filename + 1, '/'))) {
1073 node->selected = FALSE;
1074 node->expanded = TRUE;
1075// log_it (filename);
1076 root_dirs_expanded++;
1077 }
1078 if (node->down) {
1079 depth++;
1080 toggle_all_root_dirs_on(node->down);
1081 depth--;
1082 }
1083 }
1084 if (depth == 0) {
1085 log_it("Finished toggling all root dirs ON");
1086 }
1087}
1088
1089
1090/**
1091 * Toggle the expandability of a path.
1092 * @param filelist The filelist tree to operate on.
1093 * @param pathname The path to toggle expandability of.
1094 * @param on_or_off Whether to toggle it on or off.
1095 * @bug I don't understand this function. Would someone care to explain it?
1096 */
1097void
1098toggle_path_expandability(struct s_node *filelist, char *pathname,
1099 bool on_or_off)
1100{
1101
1102 /*@ int ******************************************************** */
1103 static int depth = 0;
1104 static int total_expanded;
1105 static int root_depth;
1106 int j;
1107 /*@ structures ************************************************* */
1108 struct s_node *node;
1109
1110 /*@ buffers **************************************************** */
1111 static char current_filename[MAX_STR_LEN];
1112
1113 /*@ end vars *************************************************** */
1114
1115 assert(filelist != NULL);
1116 assert_string_is_neither_NULL_nor_zerolength(pathname);
1117 if (depth == 0) {
1118 total_expanded = 0;
1119// log_it ("Toggling path's expandability");
1120 for (root_depth = (int) strlen(pathname);
1121 root_depth > 0 && pathname[root_depth - 1] != '/';
1122 root_depth--);
1123 if (root_depth < 2) {
1124 root_depth = (int) strlen(pathname);
1125 }
1126 }
1127 for (node = filelist; node != NULL; node = node->right) {
1128 current_filename[depth] = node->ch;
1129 if (node->down) {
1130 depth++;
1131 toggle_path_expandability(node->down, pathname, on_or_off);
1132 depth--;
1133 }
1134 if (node->ch == '\0') {
1135 if (!strncmp(pathname, current_filename, strlen(pathname))) {
1136 for (j = root_depth;
1137 current_filename[j] != '/'
1138 && current_filename[j] != '\0'; j++);
1139 if (current_filename[j] != '\0') {
1140 for (j++;
1141 current_filename[j] != '/'
1142 && current_filename[j] != '\0'; j++);
1143 }
1144 if (current_filename[j] == '\0') {
1145 node->expanded =
1146 (!strcmp(pathname, current_filename) ? TRUE :
1147 on_or_off);
1148 }
1149 }
1150 }
1151 if (node->expanded) {
1152 if (total_expanded < ARBITRARY_MAXIMUM - 32
1153 || !strrchr(current_filename + strlen(pathname), '/')) {
1154 total_expanded++;
1155 } else {
1156 node->expanded = FALSE;
1157 }
1158 }
1159 }
1160 if (depth == 0) {
1161// log_it ("Finished toggling expandability");
1162 }
1163}
1164
1165
1166/**
1167 * Toggle whether a path is selected.
1168 * @param filelist The filelist tree to operate on.
1169 * @param pathname The path to toggle selection of.
1170 * @param on_or_off Whether to toggle it on or off.
1171 * @bug I don't understand this function. Would someone care to explain it?
1172 */
1173void
1174toggle_path_selection(struct s_node *filelist, char *pathname,
1175 bool on_or_off)
1176{
1177 /*@ int ********************************************************* */
1178 static int depth = 0;
1179 int j;
1180
1181 /*@ structures ************************************************** */
1182 struct s_node *node;
1183
1184 /*@ buffers ***************************************************** */
1185 static char current_filename[MAX_STR_LEN];
1186
1187 /*@ end vars *************************************************** */
1188 assert(filelist != NULL);
1189 assert_string_is_neither_NULL_nor_zerolength(pathname);
1190 if (depth == 0) {
1191 log_it("Toggling path's selection");
1192 }
1193 for (node = filelist; node != NULL; node = node->right) {
1194 current_filename[depth] = node->ch;
1195 if (node->down) {
1196 depth++;
1197 toggle_path_selection(node->down, pathname, on_or_off);
1198 depth--;
1199 }
1200 if (node->ch == '\0') {
1201 if (!strncmp(pathname, current_filename, strlen(pathname))) {
1202 for (j = 0;
1203 pathname[j] != '\0'
1204 && pathname[j] == current_filename[j]; j++);
1205 if (current_filename[j] == '/'
1206 || current_filename[j] == '\0') {
1207 node->selected = on_or_off;
1208 }
1209 }
1210 }
1211 }
1212 if (depth == 0) {
1213 log_it("Finished toggling selection");
1214 }
1215}
1216
1217
1218/**
1219 * Toggle node selection of a filelist tree.
1220 * @param filelist The filelist tree to operate on.
1221 * @param on_or_off Whether to toggle selection on or off.
1222 * @bug I don't understand this function. Would someone care to explain it?
1223 */
1224void toggle_node_selection(struct s_node *filelist, bool on_or_off)
1225{
1226 /*@ structure ************************************************** */
1227 struct s_node *node;
1228
1229 /*@ end vars *************************************************** */
1230 assert(filelist != NULL);
1231 for (node = filelist; node != NULL; node = node->right) {
1232 if (node->ch == '/') {
1233 continue;
1234 } /* don't go deep */
1235 if (node->ch == '\0') {
1236 node->selected = on_or_off;
1237 }
1238 if (node->down) {
1239 toggle_node_selection(node->down, on_or_off);
1240 }
1241 }
1242}
1243
1244
1245/**
1246 * Number of entries in the skeleton filelist.
1247 */
1248long g_skeleton_entries = 0;
1249
1250
1251/**
1252 * Wrapper around mondo_makefilelist().
1253 * @param bkpinfo The backup information structure. Fields used:
1254 * - @c bkpinfo->differential
1255 * - @c bkpinfo->exclude_paths
1256 * - @c bkpinfo->include_paths
1257 * - @c bkpinfo->make_filelist
1258 * - @c bkpinfo->scratchdir
1259 * - @c bkpinfo->tmpdir
1260 * @return 0 for success, nonzero for failure.
1261 * @see mondo_makefilelist
1262 */
1263int prepare_filelist(struct s_bkpinfo *bkpinfo)
1264{
1265
1266 /*@ int **************************************************** */
1267 int res = 0;
1268 /*@ buffers ************************************************ */
1269
1270 assert(bkpinfo != NULL);
1271 log_it("tmpdir=%s; scratchdir=%s", bkpinfo->tmpdir,
1272 bkpinfo->scratchdir);
1273 if (bkpinfo->make_filelist) {
1274 mvaddstr_and_log_it(g_currentY, 0,
1275 _("Making catalog of files to be backed up"));
1276 } else {
1277 mvaddstr_and_log_it(g_currentY, 0,
1278 _("Using supplied catalog of files to be backed up"));
1279 }
1280
1281 if (bkpinfo->make_filelist) {
1282 res =
1283 mondo_makefilelist(MONDO_LOGFILE, bkpinfo->tmpdir,
1284 bkpinfo->scratchdir, bkpinfo->include_paths,
1285 bkpinfo->exclude_paths,
1286 bkpinfo->differential, NULL);
1287 } else {
1288 res =
1289 mondo_makefilelist(MONDO_LOGFILE, bkpinfo->tmpdir,
1290 bkpinfo->scratchdir, NULL,
1291 bkpinfo->exclude_paths,
1292 bkpinfo->differential,
1293 bkpinfo->include_paths);
1294 }
1295
1296 if (res) {
1297 log_OS_error("Call to mondo_makefilelist failed");
1298 mvaddstr_and_log_it(g_currentY++, 74, _("Failed."));
1299 } else {
1300 mvaddstr_and_log_it(g_currentY++, 74, _("Done."));
1301 }
1302 return (res);
1303}
1304
1305
1306/**
1307 * Recursively list all files in @p dir newer than @p time_of_last_full_backup to @p fout.
1308 * @param dir The directory to list to @p fout.
1309 * @param sth The directories to skip (exclude).
1310 * @param fout The file to write everything to.
1311 * @param time_of_last_full_backup Only backup files newer than this (0 to disable).
1312 * @return 0, always.
1313 * @bug Return value should be @c void.
1314 */
1315int open_and_list_dir(char *dir, char *sth, FILE * fout,
1316 time_t time_of_last_full_backup,
1317 char *skeleton_filelist)
1318{
1319 const char delims[] = " ";
1320 DIR *dip = NULL;
1321 struct dirent *dit = NULL;
1322
1323 struct stat statbuf;
1324 char *new = NULL;
1325 char *tmp = NULL;
1326 char *tmp1 = NULL;
1327 static int percentage = 0;
1328 char *skip_these = NULL;
1329 char *new_with_spaces = NULL;
1330 static char *name_of_evalcall_form = NULL;
1331 int i = 0;
1332 char *strtmp = NULL;
1333 char *token = NULL;
1334 char *find_excludes = NULL;
1335 int lastpos = 0;
1336 static int depth = 0;
1337 char *p = NULL;
1338 static int counter = 0;
1339 static int uberctr = 0;
1340 static char *find_skeleton_marker = NULL;
1341 static long skeleton_lino = 0;
1342 static time_t last_time = 0;
1343 time_t this_time;
1344
1345 p = strrchr(dir, '/');
1346 if (p) {
1347 if (!strcmp(p, "/.") || !strcmp(p, "/..")) {
1348 return (0);
1349 }
1350 }
1351
1352 if (!depth) {
1353 malloc_string(find_skeleton_marker);
1354 mr_asprintf(&find_excludes, " ");
1355 while((token = mr_strtok (sth, delims, &lastpos))) {
1356 mr_asprintf(&strtmp,"%s", find_excludes);
1357 mr_free(find_excludes);
1358 mr_asprintf(&find_excludes,"%s -path %s -prune -o", strtmp, token);
1359 mr_free(strtmp);
1360 mr_free(token);
1361 }
1362#if linux
1363 // 2.6 has /sys as a proc-type thing -- must be excluded
1364 mr_asprintf(&strtmp,
1365 "find %s -maxdepth %d -fstype mvfs -prune -o -path /dev/shm -prune -o %s -type d -print > %s 2> /dev/null",
1366 dir, MAX_SKEL_DEPTH, find_excludes, skeleton_filelist);
1367#else
1368 // On BSD, for example, /sys is the kernel sources -- don't exclude
1369 mr_asprintf(&strtmp,
1370 "find %s -maxdepth %d -fstype mvfs -prune -o -path /proc -prune -o %s -type d -print > %s 2> /dev/null",
1371 dir, MAX_SKEL_DEPTH, find_excludes, skeleton_filelist);
1372#endif
1373 mr_free(find_excludes);
1374 log_msg(5, "find command = %s", strtmp);
1375 system(strtmp);
1376 mr_free(strtmp);
1377 mr_free(tmp);
1378
1379 mr_asprintf(&tmp, "wc -l %s | awk '{print $1;}'", skeleton_filelist);
1380 tmp1 = call_program_and_get_last_line_of_output(tmp);
1381 mr_free(tmp);
1382
1383 g_skeleton_entries = 1 + atol(tmp1);
1384 mr_free(tmp1);
1385
1386 mr_asprintf(&name_of_evalcall_form, "Making catalog of %s", dir);
1387 open_evalcall_form(name_of_evalcall_form);
1388 find_skeleton_marker[0] = '\0';
1389 skeleton_lino = 1;
1390 log_msg(5, "entries = %ld", g_skeleton_entries);
1391 percentage = 0;
1392 } else if (depth <= MAX_SKEL_DEPTH) // update evalcall form if appropriate
1393 {
1394 sprintf(find_skeleton_marker,
1395 "grep -Fv \"%s\" %s > %s.new 2> /dev/null", dir,
1396 skeleton_filelist, skeleton_filelist);
1397// log_msg(0, "fsm = %s", find_skeleton_marker);
1398 if (!system(find_skeleton_marker)) {
1399 percentage = (int) (skeleton_lino * 100 / g_skeleton_entries);
1400 skeleton_lino++;
1401// log_msg(5, "Found %s", dir);
1402// log_msg(2, "Incrementing skeleton_lino; now %ld/%ld (%d%%)", skeleton_lino, g_skeleton_entries, percentage);
1403 sprintf(find_skeleton_marker, "mv -f %s.new %s",
1404 skeleton_filelist, skeleton_filelist);
1405// log_msg(6, "fsm = %s", find_skeleton_marker);
1406 run_program_and_log_output(find_skeleton_marker, 8);
1407 time(&this_time);
1408 if (this_time != last_time) {
1409 last_time = this_time;
1410#ifndef _XWIN
1411 if (!g_text_mode) {
1412 mr_asprintf(&tmp, _("Reading %-68s"), dir);
1413 newtDrawRootText(0, g_noof_rows - 3, tmp);
1414 mr_free(tmp);
1415 }
1416#endif
1417 update_evalcall_form(percentage);
1418 }
1419 }
1420 }
1421
1422 depth++;
1423
1424// log_msg(0, "Cataloguing %s", dir);
1425 if (sth[0] == ' ') {
1426 mr_asprintf(&skip_these, "%s", sth);
1427 } else {
1428 mr_asprintf(&skip_these, " %s ", sth);
1429 }
1430 mr_asprintf(&new_with_spaces, " %s ", dir);
1431 if ((dip = opendir(dir)) == NULL) {
1432 log_OS_error("opendir");
1433 } else if (strstr(skip_these, new_with_spaces)) {
1434 fprintf(fout, "%s\n", dir); // if excluded dir then print dir ONLY
1435 } else {
1436 fprintf(fout, "%s\n", dir);
1437 while ((dit = readdir(dip)) != NULL) {
1438 i++;
1439 if (strcmp(dir, "/")) {
1440 mr_asprintf(&new, "%s/%s", dir, dit->d_name);
1441 } else {
1442 mr_asprintf(&new, "%s%s", dir, dit->d_name);
1443 }
1444 mr_free(new_with_spaces);
1445 mr_asprintf(&new_with_spaces, " %s ", new);
1446 /* BERLIOS: Old code
1447 new_with_spaces[0] = ' ';
1448 str-cpy(new_with_spaces + 1, new);
1449 str-cat(new_with_spaces, " ");
1450 */
1451 if (strstr(skip_these, new_with_spaces)) {
1452 fprintf(fout, "%s\n", new);
1453 } else {
1454 if (!lstat(new, &statbuf)) {
1455 if (!S_ISLNK(statbuf.st_mode)
1456 && S_ISDIR(statbuf.st_mode)) {
1457 open_and_list_dir(new, skip_these, fout,
1458 time_of_last_full_backup,
1459 skeleton_filelist);
1460 } else {
1461 if (time_of_last_full_backup == 0
1462 || time_of_last_full_backup <
1463 statbuf.st_ctime) {
1464 fprintf(fout, "%s\n", new);
1465 if ((counter++) > 128) {
1466 counter = 0;
1467 uberctr++;
1468 mr_asprintf(&tmp, " %c ",
1469 special_dot_char(uberctr));
1470#ifndef _XWIN
1471 if (!g_text_mode) {
1472 newtDrawRootText(77, g_noof_rows - 3,
1473 tmp);
1474 newtRefresh();
1475 }
1476 mr_free(tmp);
1477#endif
1478 }
1479 }
1480 }
1481 }
1482 }
1483 mr_free(new);
1484 }
1485 }
1486 mr_free(new_with_spaces);
1487 mr_free(skip_these);
1488
1489 if (dip) {
1490 if (closedir(dip) == -1) {
1491 log_OS_error("closedir");
1492 }
1493 }
1494 depth--;
1495 if (!depth) {
1496 close_evalcall_form();
1497 mr_free(name_of_evalcall_form);
1498 mr_free(find_skeleton_marker);
1499 unlink(skeleton_filelist);
1500 log_msg(5, "g_skeleton_entries = %ld", g_skeleton_entries);
1501 }
1502 return (0);
1503}
1504
1505
1506/**
1507 * Get the next entry in the space-separated list in @p incoming.
1508 * So if @p incoming was '"one and two" three four', we would
1509 * return "one and two".
1510 * @param incoming The list to get the next entry from.
1511 * @return The first item in the list (respecting double quotes).
1512 * @note The returned string points to static data that will be overwritten with each call.
1513 */
1514char *next_entry(char *incoming)
1515{
1516 static char sz_res[MAX_STR_LEN];
1517 char *p = NULL;
1518 bool in_quotes = FALSE;
1519
1520 strcpy(sz_res, incoming);
1521 p = sz_res;
1522 while ((*p != ' ' || in_quotes) && *p != '\0') {
1523 if (*p == '\"') {
1524 in_quotes = !in_quotes;
1525 }
1526 p++;
1527 }
1528 *p = '\0';
1529 return (sz_res);
1530}
1531
1532
1533/**
1534 * Create the filelist for the backup. It will be stored in [scratchdir]/archives/filelist.full.
1535 * @param logfile Unused.
1536 * @param tmpdir The tmpdir of the backup.
1537 * @param scratchdir The scratchdir of the backup.
1538 * @param include_paths The paths to back up, or NULL if you're using a user-defined filelist.
1539 * @param excp The paths to NOT back up.
1540 * @param differential The differential level (currently only 0 and 1 are supported).
1541 * @param userdef_filelist The user-defined filelist, or NULL if you're using @p include_paths.
1542 * @return 0, always.
1543 * @bug @p logfile is unused.
1544 * @bug Return value is meaningless.
1545 */
1546int mondo_makefilelist(char *logfile, char *tmpdir, char *scratchdir,
1547 char *include_paths, char *excp, int differential,
1548 char *userdef_filelist)
1549{
1550 char sz_datefile_wildcard[] = "/var/cache/mondo/difflevel.%d";
1551 char *p = NULL;
1552 char *q = NULL;
1553 char *tmp = NULL;
1554 char *tmp1 = NULL;
1555 char *tmp2 = NULL;
1556 char *sz_datefile;
1557 char *sz_filelist = NULL;
1558 char *exclude_paths = NULL;
1559 int i = 0;
1560 FILE *fout = NULL;
1561 char *command = NULL;
1562 time_t time_of_last_full_backup = 0;
1563 struct stat statbuf;
1564 char *skeleton_filelist = NULL;
1565
1566 // The pathname to the skeleton filelist, used to give better progress reporting for mondo_makefilelist().
1567 mr_asprintf(&sz_datefile, sz_datefile_wildcard, 0);
1568 if (!include_paths && !userdef_filelist) {
1569 fatal_error
1570 ("Please supply either include_paths or userdef_filelist");
1571 }
1572// make hole for filelist
1573 mr_asprintf(&command, "mkdir -p %s/archives", scratchdir);
1574 paranoid_system(command);
1575 mr_free(command);
1576
1577 mr_asprintf(&sz_filelist, "%s/tmpfs/filelist.full", tmpdir);
1578 make_hole_for_file(sz_filelist);
1579
1580 if (differential == 0) {
1581 // restore last good datefile if it exists
1582 mr_asprintf(&command, "cp -f %s.aborted %s", sz_datefile,
1583 sz_datefile);
1584 run_program_and_log_output(command, 3);
1585 mr_free(command);
1586
1587 // backup last known good datefile just in case :)
1588 if (does_file_exist(sz_datefile)) {
1589 mr_asprintf(&command, "mv -f %s %s.aborted", sz_datefile,
1590 sz_datefile);
1591 paranoid_system(command);
1592 mr_free(command);
1593 }
1594 make_hole_for_file(sz_datefile);
1595 tmp = call_program_and_get_last_line_of_output("date +%s");
1596 write_one_liner_data_file(sz_datefile, tmp);
1597 mr_free(tmp);
1598 } else if (lstat(sz_datefile, &statbuf)) {
1599 log_msg(2,
1600 "Warning - unable to find date of previous backup. Full backup instead.");
1601 differential = 0;
1602 time_of_last_full_backup = 0;
1603 } else {
1604 time_of_last_full_backup = statbuf.st_mtime;
1605 log_msg(2, "Differential backup. Yay.");
1606 }
1607 mr_free(sz_datefile);
1608
1609// use user-specified filelist (if specified)
1610 if (userdef_filelist) {
1611 log_msg(1,
1612 "Using the user-specified filelist - %s - instead of calculating one",
1613 userdef_filelist);
1614 mr_asprintf(&command, "cp -f %s %s", userdef_filelist, sz_filelist);
1615 if (run_program_and_log_output(command, 3)) {
1616 fatal_error("Failed to copy user-specified filelist");
1617 }
1618 mr_free(command);
1619 } else {
1620 log_msg(2, "include_paths = '%s'", include_paths);
1621 log_msg(1, "Calculating filelist");
1622 tmp = call_program_and_get_last_line_of_output("locate /win386.swp 2> /dev/null");
1623 tmp1 = call_program_and_get_last_line_of_output("locate /hiberfil.sys 2> /dev/null");
1624 tmp2 = call_program_and_get_last_line_of_output("locate /pagefile.sys 2> /dev/null");
1625 mr_asprintf(&exclude_paths, " %s %s %s %s %s %s . .. \
1626" MNT_CDROM " " MNT_FLOPPY " /media/cdrom /media/cdrecorder \
1627/proc /sys /tmp /var/cache/mondo /var/cache/mindi", excp, tmp, tmp1, tmp2, (tmpdir[0] == '/' && tmpdir[1] == '/') ? (tmpdir + 1) : tmpdir, (scratchdir[0] == '/' && scratchdir[1] == '/') ? (scratchdir + 1) : scratchdir);
1628 mr_free(tmp);
1629 mr_free(tmp1);
1630 mr_free(tmp2);
1631
1632 log_msg(2, "Excluding paths = '%s'", exclude_paths);
1633 log_msg(2,
1634 "Generating skeleton filelist so that we can track our progress");
1635 mr_asprintf(&skeleton_filelist, "%s/tmpfs/skeleton.txt", tmpdir);
1636 make_hole_for_file(skeleton_filelist);
1637
1638 log_msg(4, "g_skeleton_entries = %ld", g_skeleton_entries);
1639 log_msg(2, "Opening out filelist to %s", sz_filelist);
1640 if (!(fout = fopen(sz_filelist, "w"))) {
1641 fatal_error("Cannot openout to sz_filelist");
1642 }
1643 i = 0;
1644 if (strlen(include_paths) == 0) {
1645 log_msg(1, "Including only '/' in %s", sz_filelist);
1646 open_and_list_dir("/", exclude_paths, fout,
1647 time_of_last_full_backup, skeleton_filelist);
1648 } else {
1649 p = include_paths;
1650 while (*p) {
1651 q = next_entry(p);
1652 log_msg(1, "Including %s in filelist %s", q, sz_filelist);
1653 open_and_list_dir(q, exclude_paths, fout,
1654 time_of_last_full_backup,
1655 skeleton_filelist);
1656 p += strlen(q);
1657 while (*p == ' ') {
1658 p++;
1659 }
1660 }
1661 }
1662 paranoid_fclose(fout);
1663 mr_free(skeleton_filelist);
1664 mr_free(exclude_paths);
1665 }
1666 log_msg(2, "Copying new filelist to scratchdir");
1667 mr_asprintf(&command, "mkdir -p %s/archives", scratchdir);
1668 paranoid_system(command);
1669 mr_free(command);
1670
1671 mr_asprintf(&command, "cp -f %s %s/archives/", sz_filelist, scratchdir);
1672 paranoid_system(command);
1673 mr_free(command);
1674
1675 mr_asprintf(&command, "mv -f %s %s", sz_filelist, tmpdir);
1676 paranoid_system(command);
1677 mr_free(command);
1678 mr_free(sz_filelist);
1679
1680 log_msg(2, "Exiting");
1681 return (0);
1682}
1683
1684
1685/**
1686 * Locate the string @p string_to_find in the tree rooted at @p startnode.
1687 * @param startnode The node containing the root of the directory tree.
1688 * @param string_to_find The string to look for at @p startnode.
1689 * @return The node containing the last element of @p string_to_find, or NULL if
1690 * it was not found.
1691 */
1692struct s_node *find_string_at_node(struct s_node *startnode,
1693 char *string_to_find)
1694{
1695 /*@ int ******************************************************** */
1696 int noof_chars;
1697 static int depth = 0;
1698 static char original_string[MAX_STR_LEN];
1699
1700 /*@ sturctures ************************************************* */
1701 struct s_node *node;
1702
1703 /*@ char ****************************************************** */
1704 char char_to_find;
1705
1706 /*@ bools ****************************************************** */
1707
1708 if (!depth) {
1709 strcpy(original_string, string_to_find);
1710 }
1711
1712 assert(startnode != NULL);
1713 assert(string_to_find != NULL);
1714
1715 noof_chars = strlen(string_to_find) + 1; /* we include the '\0' */
1716
1717 log_msg(7, "starting --- str=%s", string_to_find);
1718
1719/* walk across tree if necessary */
1720 node = startnode;
1721 char_to_find = string_to_find[0];
1722 if (node->right != NULL && node->ch < char_to_find) {
1723 log_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth,
1724 char_to_find, node->ch, (node->right)->ch);
1725 return (find_string_at_node(node->right, string_to_find));
1726 }
1727
1728/* walk down tree if appropriate */
1729 if (node->down != NULL && node->ch == char_to_find) {
1730 log_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_find);
1731 depth++;
1732 node = find_string_at_node(node->down, string_to_find + 1);
1733 depth--;
1734 return (node);
1735 }
1736
1737 if (char_to_find == '\0' && node->ch == '\0') {
1738 log_msg(7, "%s is in tree", original_string);
1739 return (node);
1740 } else {
1741 log_msg(7, "%s is NOT in tree", original_string);
1742 return (NULL);
1743 }
1744}
1745
1746
1747/**
1748 * Write all entries in @p needles_list_fname which are also in
1749 * @p filelist to @p matches_list_fname.
1750 * @param needles_list_fname A file containing strings to look for, 1 per line.
1751 * @param filelist The node for the root of the directory structure to search in.
1752 * @param matches_list_fname The filename where we should put the matches.
1753 * @return The number of matches found.
1754 */
1755long save_filelist_entries_in_common(char *needles_list_fname,
1756 struct s_node *filelist,
1757 char *matches_list_fname,
1758 bool use_star)
1759{
1760 int retval = 0;
1761 struct s_node *found_node;
1762 FILE *fin;
1763 FILE *fout;
1764 char *fname = NULL;
1765 char *tmp;
1766 size_t len = 0; // Scrub's patch doesn't work without that
1767
1768// log_msg(1, "use_star = %s", (use_star)?"TRUE":"FALSE");
1769 log_msg(5, "starting");
1770 log_msg(5, "needles_list_fname = %s", needles_list_fname);
1771 log_msg(5, "matches_list_fname = %s", matches_list_fname);
1772 if (!(fin = fopen(needles_list_fname, "r"))) {
1773 fatal_error("Cannot openin needles_list_fname");
1774 }
1775 if (!(fout = fopen(matches_list_fname, "w"))) {
1776 fatal_error("Cannot openout matches_list_fname");
1777 }
1778 while (!feof(fin)) {
1779 mr_getline(&fname, &len, fin);
1780 if (!use_star) {
1781 if (fname[0] == '/') {
1782 mr_asprintf(&tmp, fname);
1783 } else {
1784 mr_asprintf(&tmp, "/%s", fname);
1785 }
1786 mr_free(fname);
1787 fname = tmp;
1788 }
1789 while (strlen(fname) > 0 && fname[strlen(fname) - 1] < 32) {
1790 fname[strlen(fname) - 1] = '\0';
1791 }
1792
1793 log_msg(5, "Looking for '%s'", fname);
1794 found_node = find_string_at_node(filelist, fname);
1795 if (found_node) {
1796 if (found_node->selected) {
1797 if (fname[0] == '/') {
1798 mr_asprintf(&tmp, fname + 1);
1799 mr_free(fname);
1800 fname = tmp;
1801 }
1802 log_msg(5, "Found '%s'", fname);
1803 turn_wildcard_chars_into_literal_chars(tmp, fname);
1804 fprintf(fout, "%s\n", tmp);
1805 mr_free(tmp);
1806 retval++;
1807 }
1808 }
1809 mr_free(fname);
1810 }
1811 paranoid_fclose(fout);
1812 paranoid_fclose(fin);
1813 return (retval);
1814}
1815
1816
1817/**
1818 * Add all files listed in @p list_of_files_fname to the directory structure rooted at
1819 * @p filelist.
1820 * @param filelist The top node of the directory structure to add the files to.
1821 * @param list_of_files_fname The file containing the files to add, 1 per line.
1822 * @param flag_em If TRUE, then flag the added files for restoration.
1823 * @return 0 for success, nonzero for failure.
1824 */
1825int add_list_of_files_to_filelist(struct s_node *filelist,
1826 char *list_of_files_fname, bool flag_em)
1827{
1828 FILE *fin;
1829 char *tmp = NULL;
1830 size_t n = 0;
1831 struct s_node *nod;
1832
1833 log_msg(3, "Adding %s to filelist", list_of_files_fname);
1834 if (!(fin = fopen(list_of_files_fname, "r"))) {
1835 iamhere(list_of_files_fname);
1836 return (1);
1837 }
1838 for (mr_getline(&tmp, &n, fin); !feof(fin); mr_getline(&tmp, &n, fin)) {
1839 if (!tmp[0]) {
1840 continue;
1841 }
1842 if ((tmp[strlen(tmp) - 1] == 13 || tmp[strlen(tmp) - 1] == 10)
1843 && strlen(tmp) > 0) {
1844 tmp[strlen(tmp) - 1] = '\0';
1845 }
1846 log_msg(2, "tmp = '%s'", tmp);
1847 if (!tmp[0]) {
1848 continue;
1849 }
1850 if ((nod = find_string_at_node(filelist, tmp))) {
1851 log_msg(5, "Found '%s' in filelist already. Cool.", tmp);
1852 } else {
1853 add_string_at_node(filelist, tmp);
1854 nod = find_string_at_node(filelist, tmp);
1855 }
1856
1857 if (nod && flag_em) {
1858 toggle_path_selection(filelist, tmp, TRUE);
1859 log_msg(5, "Flagged '%s'", tmp);
1860 }
1861 }
1862 paranoid_fclose(fin);
1863 mr_free(tmp);
1864 return (0);
1865}
1866
1867/* @} - end of filelistGroup */
Note: See TracBrowser for help on using the repository browser.