source: branches/2.06/mondo/mondo/mondorestore/mondo-restore.c @ 296

Last change on this file since 296 was 296, checked in by andree, 15 years ago

Replaced partimagehack with ntfsclone from ntfsprogs package. Replaced
all occurrences of strings 'partimagehack' and 'partimage' with 'ntfsprog'.

  • Property svn:keywords set to Id
File size: 113.0 KB
Line 
1/***************************************************************************
2       mondo-restore.c  -  restores mondoarchive data
3                             -------------------
4    begin                : Fri May 19 2000
5    copyright            : (C) 2000 by Hugo Rabson
6    email                : Hugo Rabson <hugorabson@msn.com>
7    cvsid                : $Id: mondo-restore.c 296 2006-01-11 09:10:31Z andree $
8***************************************************************************/
9
10/***************************************************************************
11 *                                                                         *
12 *   This program is free software; you can redistribute it and/or modify  *
13 *   it under the terms of the GNU General Public License as published by  *
14 *   the Free Software Foundation; either version 2 of the License, or     *
15 *   (at your option) any later version.                                   *
16 *                                                                         *
17 ***************************************************************************/
18
19/***************************************************************************
20 *                         Change Log                                      *
21 ***************************************************************************
22.
23
24
2507/26
26- workaround - if not /dev then don't call partimagehack
27
2807/21
29- if switch to Interactive Mode during nuke then don't nuke twice :) just once
30
3107/18
32- better support of users who boot from LVM CD and nuke-restore non-LVM backups
33
3407/10
35- when using 'star', exclude all 'not removed.' messages from log (misleading, they are)
36- try w/ ACLs, then w/o ACLs if star fails w/ ACLs
37- added ACL, xattr support for afio
38
3906/26
40- remove make_relevant_partition_bootable(); roll into mondo-prep.c
41- changed various newtDrawRootText() calls to use g_noof_rows instead of
42  integers
43
4406/19
45- don't try to access biggiestruct before it is populated w/ data relating
46  to biggiefile; instead, use control char to find out if biggiefile is
47  to be restored w/ partimagehack or not
48- added AUX_VER
49
5006/15
51- read_cfg_file_into_bkpinfo() --- use user-supplied 'isodir' instead of
52  archive's 'isodir' if different
53  Conor Daly <conor.daly@met.ie>
54
5506/17
56- restore with partimagehack if NTFS device; dd, if non-NTFS device
57
5806/14
59- unmount all; remount, run grub-mr; unmount again
60
6104/09
62- fixed subset restoration bug introduced when I added 'star' support
63
6404/04
65- cleaned up restore_a_tarball_from_stream()
66
6704/03
68- added star support
69- pause for 3s between partitioning and formatting (if in Nuke Mode)
70
7103/28
72- check that g_mountlist_fname was correcfly set; abort if it wasn't
73
7403/25
75- removed dev_null code
76
7703/22/04
78- added mode_of_file()
79- added code to check for unauthorised modification of /dev/null
80  by afio (for instance)
81
8212/27/03
83- check /tmp/cmdline instead of /proc/cmdline if on FreeBSD
84
8511/15
86- added g_fprep
87- "Switch to interactive mode?" msg is now more informative
88
8911/05
90- after 'Are you sure?' when user specifies / as restore path, set
91  restore_path[] to "" so that files are restored to [blank]/file/name :)
92
9310/29
94- moved "No restoring or comparing will take place today" block
95  up to before iso_fiddly_bits (if iso) is called - fix bug
96  if you're in ISO Mode and you say "exit to shell"
97
9810/22
99- swapped calls to chmod() and chown() after restoration
100  of big files (Jens)
101
10210/21
103- changed "/mnt/cdrom" to MNT_CDROM
104
10510/19
106- restore biggiefiles selectively from CD properly
107- use find_my_editor() to find pico/vi/whatever
108- better use of call_program_and_get_last_line_of_output() to
109  scan /proc/cmdline
110
11110/18
112- don't report 'missing compressor' if no compressor used at all
113
11410/14
115- log afio's error messages to /var/log/mondo-archive.log
116  when restoring :)
117- call vi if pico is not available
118
11910/09
120- better logging if fatal error (cannot openout bigfile)
121- better GUI feedback when restoring big files
122- restore_everything() now uses s_node* instead of char*
123- ditto restore_all_*_from_*()
124
12510/02
126- succinct msg instead of pop-ups, if used -H
127
12809/27
129- tweaked restore-time gui
130
13109/26
132- proper reporting of DVD/CDR/etc. type in displayed dialogs
133
13409/23
135- malloc/free global strings in new subroutines - malloc_libmondo_global_strings()
136  and free_libmondo_global_strings() - which are in libmondo-tools.c
137
13809/21
139- trying to fix "mondorestore <no params>" segfault
140
14109/18
142- better remounting of /
143- cleaned up run_grub()
144- sensible command-line handling in Live Mode
145
14609/17
147- cleaned up GRUB installer script a bit
148
14909/15
150- remount / as r/w if in disaster recovery mode;
151  helps for b0rken distros
152
15309/13
154- major NTFS hackage
155
15609/12
157- changed some in-sub var decl'ns to malloc()'s
158
15909/05
160- don't let me run unless I'm root
161- finished NTFS/partimagehack support (CD only); working on tape now
162
16309/01
164- fixed cosmetic bug in biggiefile restore window
165
16606/01 - 08/31
167- added code to main() to make sure NFS
168  (backup) share is mounted in Nuke and Compare Modes
169- added code to run_grub() to mount /boot before running grub-install
170- fixed some stray assert()'s in restore_a_biggiefile_from_stream()
171- fixed bugs in extract_config_file_from_ramdisk()
172  and get_cfg_file_from_archive() which
173  stopped fape+floppy users from being able to
174  boot from floppy and leave floppy in drive :)
175- added hooks to partimage for doing imagedevs
176- fixed calls to popup_and_get_string()
177
17805/01 - 05/31
179- fixed biggiefile atime/utime dates restoration bug, I think
180- added misc clean-up (Steve Hindle)
181- fixed support for subdir-within-NFS-mount
182- if nuke mode fails & user reverts to interactive mode _and succeeds_,
183  don't claim nuke mode aborted :)
184- unmount_all_devices() uses mountlist->el[lino].mountpt
185  instead of mountlist->el[lino].device where possible
186- added Joshua Oreman's FreeBSD patches
187- copied missing paragraph from 1.6x's read_cfg_file_into_bkpinfo()
188  to 1.7x's; affected tape streamer users (badly!)
189- cleaned up some paranoid assert()'s
190- use which("post-nuke") instead of find_home_of_exe("post-nuke")
191- fixed "Don't eject while restoring" mode
192- get_cfg_file_from_archive() --- also recovers mountlist.txt now :)
193- don't eject if 'donteject' is in kernel's command line
194- added 'don't panic' msg to start of log
195
19604/01 - 04/30
197- added text mode (cat /proc/cmdline; if textonly then text mode is on)
198- delete /var/lock/subsys/ * when nuking
199- don't resize mountlist if "noresize" present in /proc/cmdline
200- changed from chmod -R 1777 tmp to chmod 1777 tmp
201- replace newtFinished() and newtInit() with
202  newtSuspend() and newtResume()
203- get_cfg_file_from_archive() returns 1 instead of aborting now
204- read_cfg_file_into_bkpinfo) --- if autorun CD but its config
205  file indicates a non-CD backup media then ask, just in case
206- sped up restore_a_tarball_from_CD() a bit
207- line 4469 --- if post-nuke not found then don't run it :)
208- replaced "/mnt/RESTORING" with MNT_RESTORING (#define'd)
209- moved compare_*() into mondorestore/mondo-rstr-compare.c
210- moved some RAID subroutines into common/libmondo-raid.c
211- fixed some iso live compare problems
212- replaced FILELIST_FULL with g_filelist_full and FILELIST_FULL_STUB;
213  g_filelist_full being the full path of the filelist.full text file and
214  FILELIST_FULL_STUB being "tmp/filelist.full" (relative path);
215- ditto BIGGIELIST_TXT, MONDO_CFG_FILE
216- added lots of assert()'s and log_OS_error()'s
217- in Nuke Mode, check mountlist's sanity before doing anything else;
218  if it fails sanity test, offer to revert to Interactive Mode (or abort)
219- copy log to /mnt/RESTORING/root at end
220- read_cfg_file_into_bkpinfo() --- read iso-dev and isodir if bkptype==iso
221- line 1701 --- delete ramdisk file after extracting config info
222- moved call to make_relevant_partitions_bootable() from
223  within run_boot_loader() to within interactive_mode() and
224  nuke_mode(), after unmounting disks
225- if editing fstab or *.conf, try to use pico if available
226- better calling of make-me-bootable
227- don't sort mountlist anywhere anymore except _locally_ in
228  mount_all_devices() and unmount_all_devices()
229- edit fstab, grub.conf _after_ stabgrub-me if it fails
230- run_boot_loader() --- backup all crucial files to /etc/ *.pristine first
231- added iso_fiddly_bits()
232- fixed ISO mode support
233- mount_cdrom() only searches for device if NOT in disaster recovery mode
234- changed lost of system()'s into run_program_and_log_output()'s
235- don't eject if bkpinfo->please_dont_eject_when_restoring
236- cleaned up post-nuke handling code
237- always eject CD at end, unless bkpinfo->please_dont_...
238- misc clean-up (Tom Mortell)
239- afio uses -c (1024L*1024L)/TAPE_BLOCK_SIZE now
240  instead of -c 1024
241
24201/01 - 03/31/2003
243- commented out sort_... line (BB)
244- clean-up (Stan Benoit)
245- added code for LVM and SW Raid (Brian Borgeson)
246- line 814 - added -p to 'mkdir -p tmp'
247- mount_cdrom() - calls find_cdrom_device() if
248  bkpinfo->media_device is blank (to fill it)
249
25011/01 - 12/31/2002
251- mount_cdrom() better at handling multiple CD drives
252- minor clean-up in restore_a_tarball_from_CD()
253- if --live-from-cd then assume restoring live from CD
254- tweaked it to run better w/ ArkLinux
255- create /mnt/RESTORING/mnt/.boot.d for Gentoo users
256  after restoring
257- cleaned up iso_mode(); no longer asks for NFS info 3 times
258- mount_cdrom() was trying to mount /mnt/isodir/%s/%d.iso;
259  is now just %s/%d.iso
260- mount/unmount /boot if necessary (Gentoo)
261- added RAW MBR support; added run_raw_mbr() for the purpose
262- unmount & remount supermounts at start/end of live restore
263- copy /tmp/mountlist.txt to /tmp/mountlist.txt.orig at start
264- cleaned up string-handling in get_cfg_info_from_archives()
265- fixed run_grub() to call new stabgrub-me script
266- popup list of changed files after Compare Mode
267- permit mondorestore --edit-mountlist even if live mode
268- create a repaired copy of grub-install which is RAID-friendly;
269  use it when initializing boot sector with run_grub()
270- use grub-MR instead of grub-install
271- fixed read_cfg_file_into_bkpinfo() to ignore cfg file's backup_media_type
272  if user has already specified a backup media type interactively
273
27410/01 - 10/31
275- run_grub() will let you specify the boot device as well as edit the system
276  files, if grub-install fails
277- fixed bug in fwrite() call in restore_biggiefile_from_CD()
278- fixed bug affecting restoration of bigfiles from CD's w/0 compression
279- run_grub() will run 'grub-install {boot device}' instead of
280  'grub-install (hd0)'
281
28209/01 - 09/30
283- use /tmp/tmpfs/mondo.tmp instead of /tmp/mondo.tmp
284- initialize MOUNTLIST_FNAME at start of main()
285- differential-related cleanup
286- better handling of CD-ROM drives which aren't /dev/cdrom :)
287- run_program_and_log_output() now takes boolean operator to specify
288  whether it will log its activities in the event of _success_
289- always load config file from archive before operating on it
290- moved some subroutines around; now closer to alphabetical order
291- changed mount.mindi to mount.bootisk
292- mount disks readonly if in Compare Mode
293- set /dev/null's permissions to 777, just in case it somehow gets mangled
294  ...which apparently happen with some devfs-based Linux distributions
295- remove /var/run/ *.pid after restoring
296- move spurious lockfiles from /home/ * to /home/ * /.disabled
297- if Interactive Mode then ask user which media, etc. (i.e. catchall mode
298  is now same as Interactive Mode)
299
30008/01 - 08/30
301- use data structure to store the fname, checksum, mods & perms of each bigfile
302  ... biggiestruct :)
303- if a filelist is tiny (2 bytes or less) then ignore it
304- insist_on_this_cd_number() --- now takes 2 params, not 1
305- re-enabled 'g_current_media_number = 1' in restore_everything()
306- added same to compare_mode()
307- replaced lots of global char[]'s with malloc()'s
308- if differential backup then don't permit formatting or fdisking,
309  whether Interactive or Nuke mode
310- added call to register_pid() at start of main()
311- if Nuke Mode & it succeeds then ask user if they have contributed yet
312- changed tape-size to media-size (config file)
313- changed using_* to backup_media_type
314- changed *_from_tape to *_from_stream
315
31607/01 - 07/31
317- added find_and_mount_actual_cdrom()
318- temp dir is always random
319- skip tarballs if they don't contain files we're looking for
320  (used to read the whole thing & _then_ skip)
321- use media_size[1] instead of media_size[0]
322- fixed serious bug in line 1546 - should have been !=, not ==; stopped
323  mondorestore from correctly restoring big files
324- bigfile piping enhancements (Philippe de Muyter)
325- unmount CD-ROM after restoring from live filesystem
326- TAPE_BLOCK_SIZE treated as %ld, not %d
327
32806/01 - 06/30
329- added signal-trapping
330- disabled 'nr-failed-disks' flag
331- fixed problem w/selective restore
332- don't change /tmp's permissions unless it doesn't exist & must be created
333- fixed bug in --mbr
334- is_file_in_list() enhanced to exclude /mnt/RESTORING or whatever
335- added support for uncompressed archives
336- --monitas-live now accepts path-to-restore_to_, not just path to restore
337- added some debugging/tracking code to the NFS section
338- various monitas-related enhancements
339- added --isonuke and --mbr switches
340- better logging in run_grub()
341- improved --monitas-live
342- mkdir -p /mnt/RESTORING/var/run/console just in case user excludes it
343- afio now uses 16MB buffer instead of 8MB
344- always use bkpinfo->media_size[0], now that -s has been expanded
345- popup and ask where to restore data, if restoring selectively
346
34705/01 - 05/31
348- add '--monitas' flag
349- don't run chmod -R 1777 /mnt/RESTORING/tmp before unmounting unless
350  restoring at the time...
351
35204/01 - 04/30
353- delete old /tmp/filelist.full,biggielist.txt if found when restoring to
354  live filesystem
355- replace MONDO_VERSION #define with VERSION from ../config.h
356- write fname of bigfile to screen when having trouble reading/comparing it
357- if restoring to live filesystem then wipe /tmp/tmpfs/ * afterwards
358- removed spurious finish(0) from main()
359
36003/01 - 03/31/2002
361- if /tmp/m*ndo-restore.cfg not found then assume live restore; restore
362  to / instead of /mnt/RESTORING
363- clean up is_file_in_list() to deal with the /mnt/RESTORING/ prefix
364- exclude leading '/' from filelist.restore-these
365- if /tmp/fstab.new exists then use _it_ instead of /tmp/fstab to label
366  ext2 or ext3 partitions
367- improved logging
368
369[...]
370
37107/10/2001 --- first incarnation
372*/
373
374
375/**
376 * @file
377 * The main file for mondorestore.
378 */
379
380/**************************************************************************
381 * #include statements                                                    *
382 **************************************************************************/
383#include <pthread.h>
384#include "../common/my-stuff.h"
385#include "../common/mondostructures.h"
386#include "../common/libmondo.h"
387#include "mr-externs.h"
388#include "mondo-restore.h"
389#include "mondo-rstr-compare-EXT.h"
390#include "mondo-rstr-tools-EXT.h"
391
392extern void success_message(void);
393extern void twenty_seconds_til_yikes(void);
394
395
396/* For use in other programs (ex. XMondo) */
397#ifdef MONDORESTORE_MODULE
398#define main __mondorestore_main
399#define g_ISO_restore_mode __mondorestore_g_ISO_restore_mode
400#endif
401
402//static char cvsid[] = "$Id: mondo-restore.c 296 2006-01-11 09:10:31Z andree $";
403
404/**************************************************************************
405 * Globals                                                                *
406 **************************************************************************/
407extern char *g_tmpfs_mountpt;   // declared in libmondo-tools.c
408extern struct s_bkpinfo *g_bkpinfo_DONTUSETHIS; // used by finish() to free
409                                                // up global bkpinfo struct
410extern bool g_text_mode;
411extern FILE *g_fprep;
412extern double g_kernel_version;
413extern int g_partition_table_locked_up;
414extern int g_noof_rows;
415
416extern int partition_everything(struct mountlist_itself *mountlist);
417
418
419/**
420 * @name Restore-Time Globals
421 * @ingroup globalGroup
422 * @{
423 */
424/**
425 * If TRUE, then SIGPIPE was just caught.
426 * Set by the signal handler; cleared after it's handled.
427 */
428bool g_sigpipe_caught = FALSE;
429
430/**
431 * If TRUE, then we're restoring from ISOs or an NFS server.
432 * If FALSE, then we're restoring from some kind of real media (tape, CD, etc.)
433 */
434bool g_ISO_restore_mode = FALSE;    /* are we in Iso Mode? */
435
436/**
437 * If TRUE, then we have had a successful "nuke" restore.
438 */
439bool g_I_have_just_nuked = FALSE;
440
441/**
442 * The device to mount to get at the ISO images. Ignored unless @p g_ISO_restore_mode.
443 */
444char *g_isodir_device;
445
446/**
447 * The format of @p g_isodir_device. Ignored unless @p g_ISO_restore_mode.
448 */
449char *g_isodir_format;
450
451/**
452 * The location of 'biggielist.txt', containing the biggiefiles on the current archive set.
453 */
454char *g_biggielist_txt;
455
456/**
457 * The location of 'filelist.full', containing all files (<em>including biggiefiles</em>) on
458 * the current archive set.
459 */
460char *g_filelist_full;
461
462/**
463 * The location of a file containing a list of the devices that were archived
464 * as images, not as individual files.
465 */
466char *g_filelist_imagedevs;
467
468/**
469 * The location of a file containing a list of imagedevs to actually restore.
470 * @see g_filelist_imagedevs
471 */
472char *g_imagedevs_restthese;
473
474/**
475 * The location of 'mondo-restore.cfg', containing the metadata
476 * information for this backup.
477 */
478char *g_mondo_cfg_file;
479
480/**
481 * The location of 'mountlist.txt', containing the information on the
482 * user's partitions and hard drives.
483 */
484char *g_mountlist_fname;
485
486/**
487 * Mondo's home directory during backup. Unused in mondo-restore; included
488 * to avoid link errors.
489 */
490char *g_mondo_home;
491
492/* @} - end of "Restore-Time Globals" in globalGroup */
493
494
495
496extern int copy_from_src_to_dest(FILE * f_orig, FILE * f_archived,
497                                 char direction);
498
499
500
501/**************************************************************************
502 * COMPAQ PROLIANT Stuff:  needs some special help                        *
503**************************************************************************/
504
505/**
506 * The message to display if we detect that the user is using a Compaq Proliant.
507 */
508#define COMPAQ_PROLIANTS_SUCK "Partition and format your disk using Compaq's disaster recovery CD. After you've done that, please reboot with your Mondo CD/floppy in Interactive Mode."
509
510
511
512
513/**
514 * Allow the user to modify the mountlist before we partition & format their drives.
515 * @param bkpinfo The backup information structure. @c disaster_recovery is the only field used.
516 * @param mountlist The mountlist to let the user modify.
517 * @param raidlist The raidlist that goes with @p mountlist.
518 * @return 0 for success, nonzero for failure.
519 * @ingroup restoreGuiGroup
520 */
521int let_user_edit_the_mountlist(struct s_bkpinfo *bkpinfo,
522                                struct mountlist_itself *mountlist,
523                                struct raidlist_itself *raidlist)
524{
525    int retval = 0, res = 0;
526
527    log_msg(2, "let_user_edit_the_mountlist() --- starting");
528
529    assert(bkpinfo != NULL);
530    assert(mountlist != NULL);
531    assert(raidlist != NULL);
532    if (!bkpinfo->disaster_recovery) {
533        strcpy(g_mountlist_fname, "/tmp/mountlist.txt");
534        log_msg(2, "I guess you're testing edit_mountlist()");
535    }
536    if (!does_file_exist(g_mountlist_fname)) {
537        log_to_screen(g_mountlist_fname);
538        log_to_screen("does not exist");
539        return (1);
540    }
541
542    retval = load_mountlist(mountlist, g_mountlist_fname);
543    load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME);
544    if (retval) {
545        log_to_screen
546            ("Warning - load_raidtab_into_raidlist returned an error");
547    }
548    res = edit_mountlist(g_mountlist_fname, mountlist, raidlist);
549    if (res) {
550        return (1);
551    }
552
553    save_mountlist_to_disk(mountlist, g_mountlist_fname);
554    save_raidlist_to_raidtab(raidlist, RAIDTAB_FNAME);
555
556    log_to_screen("I have finished editing the mountlist for you.");
557
558    return (retval);
559}
560
561
562
563
564
565/**
566 * Determine whether @p mountlist contains a Compaq diagnostic partition.
567 * @param mountlist The mountlist to examine.
568 * @return TRUE if there's a Compaq diagnostic partition; FALSE if not.
569 * @ingroup restoreUtilityGroup
570 */
571bool
572partition_table_contains_Compaq_diagnostic_partition(struct
573                                                     mountlist_itself *
574                                                     mountlist)
575{
576    int i;
577
578    assert(mountlist != NULL);
579
580    for (i = 0; i < mountlist->entries; i++) {
581        if (strstr(mountlist->el[i].format, "ompaq")) {
582            log_msg(2, "mountlist[%d] (%s) is %s (Compaq alert!)",
583                    i, mountlist->el[i].device, mountlist->el[i].format);
584
585            return (TRUE);
586        }
587    }
588    return (FALSE);
589}
590
591/**************************************************************************
592 *END_PARTITION_TABLE_CONTAINS_COMPAQ_DIAGNOSTIC_PARTITION                *
593 **************************************************************************/
594
595
596/**
597 * Allow the user to abort the backup if we find that there is a Compaq diagnostic partition.
598 * @note This function does not actually check for the presence of a Compaq partition.
599 * @ingroup restoreUtilityGroup
600 */
601void offer_to_abort_because_Compaq_Proliants_suck(void)
602{
603    popup_and_OK(COMPAQ_PROLIANTS_SUCK);
604    if (ask_me_yes_or_no
605        ("Would you like to reboot and use your Compaq CD to prep your hard drive?"))
606    {
607        fatal_error
608            ("Aborting. Please reboot and prep your hard drive with your Compaq CD.");
609    }
610}
611
612/**************************************************************************
613 *END_OFFER_TO_ABORT_BECAUSE_COMPAQ_PROLIANTS_SUCK                        *
614 **************************************************************************/
615
616
617
618/**
619 * Call interactive_mode(), nuke_mode(), or compare_mode() depending on the user's choice.
620 * @param bkpinfo The backup information structure. Most fields are used.
621 * @param mountlist The mountlist containing information about the user's partitions.
622 * @param raidlist The raidlist to go with @p mountlist.
623 * @return The return code from the mode function called.
624 * @ingroup restoreGroup
625 */
626int
627catchall_mode(struct s_bkpinfo *bkpinfo,
628              struct mountlist_itself *mountlist,
629              struct raidlist_itself *raidlist)
630{
631    char c, *tmp;
632    int retval = 0;
633
634    iamhere("inside catchall");
635    assert(bkpinfo != NULL);
636    assert(mountlist != NULL);
637    assert(raidlist != NULL);
638    malloc_string(tmp);
639    iamhere("pre wrm");
640    c = which_restore_mode();
641    iamhere("post wrm");
642    if (c == 'I' || c == 'N' || c == 'C') {
643        interactively_obtain_media_parameters_from_user(bkpinfo, FALSE);
644    } else {
645        popup_and_OK("No restoring or comparing will take place today.");
646        if (is_this_device_mounted("/mnt/cdrom")) {
647            run_program_and_log_output("umount /mnt/cdrom", FALSE);
648        }
649        if (g_ISO_restore_mode) {
650            sprintf(tmp, "umount %s", bkpinfo->isodir);
651            run_program_and_log_output(tmp, FALSE);
652        }
653        paranoid_MR_finish(0);
654    }
655
656    iamhere("post int");
657
658    if (bkpinfo->backup_media_type == iso) {
659        if (iso_fiddly_bits(bkpinfo, (c == 'N') ? TRUE : FALSE)) {
660            log_msg(2,
661                    "catchall_mode --- iso_fiddly_bits returned w/ error");
662            return (1);
663        } else {
664            log_msg(2, "catchall_mode --- iso_fiddly_bits ok");
665        }
666    }
667
668    if (c == 'I') {
669        log_msg(2, "IM selected");
670        retval += interactive_mode(bkpinfo, mountlist, raidlist);
671    } else if (c == 'N') {
672        log_msg(2, "NM selected");
673        retval += nuke_mode(bkpinfo, mountlist, raidlist);
674    } else if (c == 'C') {
675        log_msg(2, "CM selected");
676        retval += compare_mode(bkpinfo, mountlist, raidlist);
677    }
678    paranoid_free(tmp);
679    return (retval);
680}
681
682/**************************************************************************
683 *END_CATCHALL_MODE                                                      *
684 **************************************************************************/
685
686/**************************************************************************
687 *END_  EXTRACT_CONFIG_FILE_FROM_RAMDISK                                  *
688 **************************************************************************/
689
690
691/**
692 * Locate an executable in the directory structure rooted at @p restg.
693 * @param out_path Where to put the executable.
694 * @param fname The basename of the executable.
695 * @param restg The directory structure to look in.
696 * @note If it could not be found in @p restg then @p fname is put in @p out_path.
697 * @ingroup restoreUtilityGroup
698 */
699void
700find_pathname_of_executable_preferably_in_RESTORING(char *out_path,
701                                                    char *fname,
702                                                    char *restg)
703{
704    assert(out_path != NULL);
705    assert_string_is_neither_NULL_nor_zerolength(fname);
706
707    sprintf(out_path, "%s/sbin/%s", restg, fname);
708    if (does_file_exist(out_path)) {
709        sprintf(out_path, "%s/usr/sbin/%s", restg, fname);
710        if (does_file_exist(out_path)) {
711            sprintf(out_path, "%s/bin/%s", restg, fname);
712            if (does_file_exist(out_path)) {
713                sprintf(out_path, "%s/usr/bin/%s", restg, fname);
714                if (does_file_exist(out_path)) {
715                    strcpy(out_path, fname);
716                }
717            }
718        }
719    }
720}
721
722/**************************************************************************
723 *END_FIND_PATHNAME_OF_EXECUTABLE_PREFERABLY_IN_RESTORING                 *
724 **************************************************************************/
725
726
727
728
729/**
730 * @addtogroup restoreGroup
731 * @{
732 */
733/**
734 * Restore the user's data, in a disaster recovery situation, prompting the
735 * user about whether or not to do every step.
736 * The user can edit the mountlist, choose files to restore, etc.
737 * @param bkpinfo The backup information structure. Most fields are used.
738 * @param mountlist The mountlist containing information about the user's partitions.
739 * @param raidlist The raidlist to go with @p mountlist.
740 * @return 0 for success, or the number of errors encountered.
741 */
742int
743interactive_mode(struct s_bkpinfo *bkpinfo,
744                 struct mountlist_itself *mountlist,
745                 struct raidlist_itself *raidlist)
746{
747    int retval = 0;
748    int res;
749    int ptn_errs = 0;
750    int fmt_errs = 0;
751
752    bool done;
753    bool restore_all;
754
755  /** needs malloc **********/
756    char *tmp;
757    char *fstab_fname;
758    char *old_restpath;
759
760    struct s_node *filelist;
761
762    /* try to partition and format */
763
764    log_msg(2, "interactive_mode --- starting (great, assertions OK)");
765
766    malloc_string(tmp);
767    malloc_string(fstab_fname);
768    malloc_string(old_restpath);
769    assert(bkpinfo != NULL);
770    assert(mountlist != NULL);
771    assert(raidlist != NULL);
772
773    log_msg(2, "interactive_mode --- assertions OK");
774
775    if (g_text_mode) {
776        if (!ask_me_yes_or_no
777            ("Interactive Mode + textonly = experimental! Proceed anyway?"))
778        {
779            fatal_error("Wise move.");
780        }
781    }
782
783    iamhere("About to load config file");
784    get_cfg_file_from_archive_or_bust(bkpinfo);
785    read_cfg_file_into_bkpinfo(g_mondo_cfg_file, bkpinfo);
786    iamhere("Done loading config file; resizing ML");
787#ifdef __FreeBSD__
788    if (strstr
789        (call_program_and_get_last_line_of_output("cat /tmp/cmdline"),
790         "noresize"))
791#else
792    if (strstr
793        (call_program_and_get_last_line_of_output("cat /proc/cmdline"),
794         "noresize"))
795#endif
796    {
797        log_msg(1, "Not resizing mountlist.");
798    } else {
799        resize_mountlist_proportionately_to_suit_new_drives(mountlist);
800    }
801    for (done = FALSE; !done;) {
802        iamhere("About to edit mountlist");
803        if (g_text_mode) {
804            save_mountlist_to_disk(mountlist, g_mountlist_fname);
805            sprintf(tmp, "%s %s", find_my_editor(), g_mountlist_fname);
806            res = system(tmp);
807            load_mountlist(mountlist, g_mountlist_fname);
808        } else {
809            res = edit_mountlist(g_mountlist_fname, mountlist, raidlist);
810        }
811        iamhere("Finished editing mountlist");
812        if (res) {
813            paranoid_MR_finish(1);
814        }
815        log_msg(2, "Proceeding...");
816        save_mountlist_to_disk(mountlist, g_mountlist_fname);
817        save_raidlist_to_raidtab(raidlist, RAIDTAB_FNAME);
818        mvaddstr_and_log_it(1, 30, "Restoring Interactively");
819        if (bkpinfo->differential) {
820            log_to_screen("Because this is a differential backup, disk");
821            log_to_screen
822                (" partitioning and formatting will not take place.");
823            done = TRUE;
824        } else {
825            if (ask_me_yes_or_no
826                ("Do you want to erase and partition your hard drives?")) {
827                if (partition_table_contains_Compaq_diagnostic_partition
828                    (mountlist)) {
829                    offer_to_abort_because_Compaq_Proliants_suck();
830                    done = TRUE;
831                } else {
832                    twenty_seconds_til_yikes();
833                    g_fprep = fopen("/tmp/prep.sh", "w");
834                    ptn_errs = partition_everything(mountlist);
835                    if (ptn_errs) {
836                        log_to_screen
837                            ("Warning. Errors occurred during disk partitioning.");
838                    }
839
840                    fmt_errs = format_everything(mountlist, FALSE);
841                    if (!fmt_errs) {
842                        log_to_screen
843                            ("Errors during disk partitioning were handled OK.");
844                        log_to_screen
845                            ("Partitions were formatted OK despite those errors.");
846                        ptn_errs = 0;
847                    }
848                    if (!ptn_errs && !fmt_errs) {
849                        done = TRUE;
850                    }
851                }
852                paranoid_fclose(g_fprep);
853            } else {
854                mvaddstr_and_log_it(g_currentY++, 0,
855                                    "User opted not to partition the devices");
856                if (ask_me_yes_or_no
857                    ("Do you want to format your hard drives?")) {
858                    fmt_errs = format_everything(mountlist, TRUE);
859                    if (!fmt_errs) {
860                        done = TRUE;
861                    }
862                } else {
863                    ptn_errs = fmt_errs = 0;
864                    done = TRUE;
865                }
866            }
867            if (fmt_errs) {
868                mvaddstr_and_log_it(g_currentY++,
869                                    0,
870                                    "Errors occurred. Please repartition and format drives manually.");
871                done = FALSE;
872            }
873            if (ptn_errs & !fmt_errs) {
874                mvaddstr_and_log_it(g_currentY++,
875                                    0,
876                                    "Errors occurred during partitioning. Formatting, however, went OK.");
877                done = TRUE;
878            }
879            if (!done) {
880                if (!ask_me_yes_or_no("Re-edit the mountlist?")) {
881                    retval++;
882                    goto end_of_func;
883                }
884            }
885        }
886    }
887
888    /* mount */
889    if (mount_all_devices(mountlist, TRUE)) {
890        unmount_all_devices(mountlist);
891        retval++;
892        goto end_of_func;
893    }
894    /* restore */
895    if ((restore_all =
896         ask_me_yes_or_no("Do you want me to restore all of your data?")))
897    {
898        log_msg(1, "Restoring all data");
899        retval += restore_everything(bkpinfo, NULL);
900    } else
901        if ((restore_all =
902             ask_me_yes_or_no
903             ("Do you want me to restore _some_ of your data?"))) {
904        strcpy(old_restpath, bkpinfo->restore_path);
905        for (done = FALSE; !done;) {
906            unlink("/tmp/filelist.full");
907            filelist = process_filelist_and_biggielist(bkpinfo);
908            /* Now you have /tmp/tmpfs/filelist.restore-these and /tmp/tmpfs/biggielist.restore-these;
909               the former is a list of regular files; the latter, biggiefiles and imagedevs.
910             */
911            if (filelist) {
912              gotos_suck:
913                strcpy(tmp, old_restpath);
914// (NB: %s is where your filesystem is mounted now, by default)", MNT_RESTORING);
915                if (popup_and_get_string
916                    ("Restore path", "Restore files to where?", tmp,
917                     MAX_STR_LEN / 4)) {
918                    if (!strcmp(tmp, "/")) {
919                        if (!ask_me_yes_or_no("Are you sure?")) {
920                            goto gotos_suck;
921                        }
922                        tmp[0] = '\0';  // so we restore to [blank]/file/name :)
923                    }
924                    strcpy(bkpinfo->restore_path, tmp);
925                    log_msg(1, "Restoring subset");
926                    retval += restore_everything(bkpinfo, filelist);
927                    free_filelist(filelist);
928                } else {
929                    strcpy(bkpinfo->restore_path, old_restpath);
930                    free_filelist(filelist);
931                }
932                if (!ask_me_yes_or_no
933                    ("Restore another subset of your backup?")) {
934                    done = TRUE;
935                }
936            } else {
937                done = TRUE;
938            }
939        }
940        strcpy(old_restpath, bkpinfo->restore_path);
941    } else {
942        mvaddstr_and_log_it(g_currentY++,
943                            0,
944                            "User opted not to restore any data.                                  ");
945    }
946    if (retval) {
947        mvaddstr_and_log_it(g_currentY++,
948                            0,
949                            "Errors occurred during the restore phase.            ");
950    }
951
952    if (ask_me_yes_or_no("Initialize the boot loader?")) {
953        run_boot_loader(TRUE);
954    } else {
955        mvaddstr_and_log_it(g_currentY++,
956                            0,
957                            "User opted not to initialize the boot loader.");
958    }
959
960//  run_program_and_log_output("cp -af /etc/lvm " MNT_RESTORING "/etc/", 1);
961    protect_against_braindead_sysadmins();
962    //  modify_rclocal_one_time( MNT_RESTORING "/etc" );
963    retval += unmount_all_devices(mountlist);
964    /*  if (restore_some || restore_all || */
965    if (ask_me_yes_or_no
966        ("Label your ext2 and ext3 partitions if necessary?")) {
967        mvaddstr_and_log_it(g_currentY, 0,
968                            "Using e2label to label your ext2,3 partitions");
969        if (does_file_exist("/tmp/fstab.new")) {
970            strcpy(fstab_fname, "/tmp/fstab.new");
971        } else {
972            strcpy(fstab_fname, "/tmp/fstab");
973        }
974        sprintf(tmp,
975                "label-partitions-as-necessary %s < %s >> %s 2>> %s",
976                g_mountlist_fname, fstab_fname, MONDO_LOGFILE,
977                MONDO_LOGFILE);
978        res = system(tmp);
979        if (res) {
980            log_to_screen
981                ("label-partitions-as-necessary returned an error");
982            mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
983        } else {
984            mvaddstr_and_log_it(g_currentY++, 74, "Done.");
985        }
986        retval += res;
987    }
988
989    iamhere("About to leave interactive_mode()");
990    if (retval) {
991        mvaddstr_and_log_it(g_currentY++,
992                            0,
993                            "Warning - errors occurred during the restore phase.");
994    }
995  end_of_func:
996    paranoid_free(tmp);
997    paranoid_free(fstab_fname);
998    paranoid_free(old_restpath);
999    iamhere("Leaving interactive_mode()");
1000    return (retval);
1001}
1002
1003/**************************************************************************
1004 *END_INTERACTIVE_MODE                                                    *
1005 **************************************************************************/
1006
1007
1008
1009/**
1010 * Run an arbitrary restore mode (prompt the user), but from ISO images
1011 * instead of real media.
1012 * @param bkpinfo The backup information structure. Most fields are used.
1013 * @param mountlist The mountlist containing information about the user's partitions.
1014 * @param raidlist The raidlist that goes with @p mountlist.
1015 * @param nuke_me_please If TRUE, we plan to run Nuke Mode.
1016 * @return 0 for success, or the number of errors encountered.
1017 */
1018int
1019iso_mode(struct s_bkpinfo *bkpinfo,
1020         struct mountlist_itself *mountlist,
1021         struct raidlist_itself *raidlist, bool nuke_me_please)
1022{
1023    char c;
1024    int retval = 0;
1025
1026    assert(bkpinfo != NULL);
1027    assert(mountlist != NULL);
1028    assert(raidlist != NULL);
1029    if (iso_fiddly_bits(bkpinfo, nuke_me_please)) {
1030        log_msg(1, "iso_mode --- returning w/ error");
1031        return (1);
1032    } else {
1033        c = which_restore_mode();
1034        if (c == 'I' || c == 'N' || c == 'C') {
1035            interactively_obtain_media_parameters_from_user(bkpinfo,
1036                                                            FALSE);
1037        }
1038        if (c == 'I') {
1039            retval += interactive_mode(bkpinfo, mountlist, raidlist);
1040        } else if (c == 'N') {
1041            retval += nuke_mode(bkpinfo, mountlist, raidlist);
1042        } else if (c == 'C') {
1043            retval += compare_mode(bkpinfo, mountlist, raidlist);
1044        } else {
1045            log_to_screen("OK, I shan't restore/compare any files.");
1046        }
1047    }
1048    if (is_this_device_mounted(MNT_CDROM)) {
1049        paranoid_system("umount " MNT_CDROM);
1050    }
1051//  if (! already_mounted)
1052//    {
1053    if (system("umount /tmp/isodir 2> /dev/null")) {
1054        log_to_screen
1055            ("WARNING - unable to unmount device where the ISO files are stored.");
1056    }
1057//    }
1058    return (retval);
1059}
1060
1061/**************************************************************************
1062 *END_ISO_MODE                                                            *
1063 **************************************************************************/
1064
1065
1066/*            MONDO - saving your a$$ since Feb 18th, 2000            */
1067
1068
1069
1070
1071/**
1072 * Restore the user's data automatically (no prompts), after a twenty-second
1073 * warning period.
1074 * @param bkpinfo The backup information structure. Most fields are used.
1075 * @param mountlist The mountlist containing information about the user's partitions.
1076 * @param raidlist The raidlist that goes with @p mountlist.
1077 * @return 0 for success, or the number of errors encountered.
1078 * @warning <b><i>THIS WILL ERASE ALL EXISTING DATA!</i></b>
1079 */
1080int
1081nuke_mode(struct s_bkpinfo *bkpinfo,
1082          struct mountlist_itself *mountlist,
1083          struct raidlist_itself *raidlist)
1084{
1085    int retval = 0;
1086    int res = 0;
1087    bool boot_loader_installed = FALSE;
1088  /** malloc **/
1089    char tmp[MAX_STR_LEN], tmpA[MAX_STR_LEN], tmpB[MAX_STR_LEN],
1090        tmpC[MAX_STR_LEN];
1091
1092    assert(bkpinfo != NULL);
1093    assert(mountlist != NULL);
1094    assert(raidlist != NULL);
1095
1096    log_msg(2, "nuke_mode --- starting");
1097
1098    get_cfg_file_from_archive_or_bust(bkpinfo);
1099    load_mountlist(mountlist, g_mountlist_fname);   // in case read_cfg_file_into_bkpinfo updated the mountlist
1100#ifdef __FreeBSD__
1101    if (strstr
1102        (call_program_and_get_last_line_of_output("cat /tmp/cmdline"),
1103         "noresize"))
1104#else
1105    if (strstr
1106        (call_program_and_get_last_line_of_output("cat /proc/cmdline"),
1107         "noresize"))
1108#endif
1109    {
1110        log_msg(2, "Not resizing mountlist.");
1111    } else {
1112        resize_mountlist_proportionately_to_suit_new_drives(mountlist);
1113    }
1114    if (!evaluate_mountlist(mountlist, tmpA, tmpB, tmpC)) {
1115        sprintf(tmp,
1116                "Mountlist analyzed. Result: \"%s %s %s\" Switch to Interactive Mode?",
1117                tmpA, tmpB, tmpC);
1118        if (ask_me_yes_or_no(tmp)) {
1119            retval = interactive_mode(bkpinfo, mountlist, raidlist);
1120            finish(retval);
1121        } else {
1122            fatal_error("Nuke Mode aborted. ");
1123        }
1124    }
1125    save_mountlist_to_disk(mountlist, g_mountlist_fname);
1126    mvaddstr_and_log_it(1, 30, "Restoring Automatically");
1127    if (bkpinfo->differential) {
1128        log_to_screen("Because this is a differential backup, disk");
1129        log_to_screen("partitioning and formatting will not take place.");
1130        res = 0;
1131    } else {
1132        if (partition_table_contains_Compaq_diagnostic_partition
1133            (mountlist)) {
1134            offer_to_abort_because_Compaq_Proliants_suck();
1135        } else {
1136            twenty_seconds_til_yikes();
1137            g_fprep = fopen("/tmp/prep.sh", "w");
1138#ifdef __FreeBSD__
1139            if (strstr
1140                (call_program_and_get_last_line_of_output
1141                 ("cat /tmp/cmdline"), "nopart"))
1142#else
1143            if (strstr
1144                (call_program_and_get_last_line_of_output
1145                 ("cat /proc/cmdline"), "nopart"))
1146#endif
1147            {
1148                log_msg(2,
1149                        "Not partitioning drives due to 'nopart' option.");
1150                res = 0;
1151            } else {
1152                res = partition_everything(mountlist);
1153                if (res) {
1154                    log_to_screen
1155                        ("Warning. Errors occurred during partitioning.");
1156                    res = 0;
1157                }
1158            }
1159            retval += res;
1160            if (!res) {
1161                log_to_screen("Preparing to format your disk(s)");
1162                sleep(1);
1163                system("sync");
1164                log_to_screen("Please wait. This may take a few minutes.");
1165                res += format_everything(mountlist, FALSE);
1166            }
1167            paranoid_fclose(g_fprep);
1168        }
1169    }
1170    retval += res;
1171    if (res) {
1172        mvaddstr_and_log_it(g_currentY++,
1173                            0,
1174                            "Failed to partition and/or format your hard drives.");
1175
1176        if (ask_me_yes_or_no("Try in interactive mode instead?")) {
1177            retval = interactive_mode(bkpinfo, mountlist, raidlist);
1178            goto after_the_nuke;
1179        } else
1180            if (!ask_me_yes_or_no
1181                ("Would you like to try to proceed anyway?")) {
1182            return (retval);
1183        }
1184    }
1185    retval = mount_all_devices(mountlist, TRUE);
1186    if (retval) {
1187        unmount_all_devices(mountlist);
1188        log_to_screen
1189            ("Unable to mount all partitions. Sorry, I cannot proceed.");
1190        return (retval);
1191    }
1192    iamhere("Restoring everything");
1193    retval += restore_everything(bkpinfo, NULL);
1194    if (!run_boot_loader(FALSE)) {
1195        log_msg(1,
1196                "Great! Boot loader was installed. No need for msg at end.");
1197        boot_loader_installed = TRUE;
1198    }
1199    protect_against_braindead_sysadmins();
1200//  run_program_and_log_output("cp -af /etc/lvm " MNT_RESTORING "/etc/", 1);
1201    //  modify_rclocal_one_time( MNT_RESTORING "/etc" );
1202    retval += unmount_all_devices(mountlist);
1203    mvaddstr_and_log_it(g_currentY,
1204                        0,
1205                        "Using e2label to label your ext2,3 partitions");
1206
1207    sprintf(tmp, "label-partitions-as-necessary %s < /tmp/fstab",
1208            g_mountlist_fname);
1209    res = run_program_and_log_output(tmp, TRUE);
1210    if (res) {
1211        log_to_screen("label-partitions-as-necessary returned an error");
1212        mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
1213    } else {
1214        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1215    }
1216    retval += res;
1217
1218  after_the_nuke:
1219    if (retval) {
1220        log_to_screen("Errors occurred during the nuke phase.");
1221    } else if (strstr(call_program_and_get_last_line_of_output("cat /proc/cmdline"), "RESTORE"))    // Bruno's thing
1222    {
1223        log_to_screen
1224            ("PC was restored successfully. Thank you for using Mondo Rescue.");
1225        log_to_screen
1226            ("Please visit http://www.mondorescue.org and thank the dev team.");
1227    } else {
1228#ifdef FREELOADER
1229        success_message();
1230#else
1231        log_to_screen("PC was restored successfully!");
1232#endif
1233    }
1234    g_I_have_just_nuked = TRUE;
1235/*
1236  if (!boot_loader_installed && !does_file_exist(DO_MBR_PLEASE))
1237    {
1238      log_to_screen("PLEASE RUN 'mondorestore --mbr' NOW TO INITIALIZE YOUR BOOT SECTOR");
1239      write_one_liner_data_file(DO_MBR_PLEASE, "mondorestore --mbr");
1240    }
1241*/
1242    return (retval);
1243}
1244
1245/**************************************************************************
1246 *END_NUKE_MODE                                                           *
1247 **************************************************************************/
1248
1249
1250
1251/**
1252 * Restore the user's data (or a subset of it) to the live filesystem.
1253 * This should not be called if we're booted from CD!
1254 * @param bkpinfo The backup information structure. Most fields are used.
1255 * @return 0 for success, or the number of errors encountered.
1256 */
1257int restore_to_live_filesystem(struct s_bkpinfo *bkpinfo)
1258{
1259    int retval = 0;
1260
1261  /** malloc **/
1262    char *old_restpath;
1263
1264    struct mountlist_itself *mountlist;
1265//  static
1266    struct raidlist_itself *raidlist;
1267    struct s_node *filelist;
1268
1269    log_msg(1, "restore_to_live_filesystem() - starting");
1270    assert(bkpinfo != NULL);
1271    malloc_string(old_restpath);
1272    mountlist = malloc(sizeof(struct mountlist_itself));
1273    raidlist = malloc(sizeof(struct raidlist_itself));
1274    if (!mountlist || !raidlist) {
1275        fatal_error("Cannot malloc() mountlist and/or raidlist");
1276    }
1277
1278    strcpy(bkpinfo->restore_path, "/");
1279    if (!g_restoring_live_from_cd) {
1280        popup_and_OK
1281            ("Please insert tape/CD/boot floppy, then hit 'OK' to continue.");
1282        sleep(1);
1283    }
1284    interactively_obtain_media_parameters_from_user(bkpinfo, FALSE);
1285    log_msg(2, "bkpinfo->media_device = %s", bkpinfo->media_device);
1286    if (!bkpinfo->media_device[0]) {
1287        log_msg(2, "Warning - failed to find media dev");
1288    }
1289
1290
1291    log_msg(2, "bkpinfo->isodir = %s", bkpinfo->isodir);
1292
1293    open_evalcall_form("Thinking...");
1294
1295    get_cfg_file_from_archive_or_bust(bkpinfo);
1296    read_cfg_file_into_bkpinfo(g_mondo_cfg_file, bkpinfo);
1297    load_mountlist(mountlist, g_mountlist_fname);   // in case read_cfg_file_into_bkpinfo
1298
1299    close_evalcall_form();
1300    retval = load_mountlist(mountlist, g_mountlist_fname);
1301    load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME);
1302    filelist = process_filelist_and_biggielist(bkpinfo);
1303    if (filelist) {
1304        save_filelist(filelist, "/tmp/selected-files.txt");
1305        strcpy(old_restpath, bkpinfo->restore_path);
1306        if (popup_and_get_string("Restore path",
1307                                 "Restore files to where? )",
1308                                 bkpinfo->restore_path, MAX_STR_LEN / 4)) {
1309            iamhere("Restoring everything");
1310            retval += restore_everything(bkpinfo, filelist);
1311            free_filelist(filelist);
1312            strcpy(bkpinfo->restore_path, old_restpath);
1313        } else {
1314            free_filelist(filelist);
1315        }
1316        strcpy(bkpinfo->restore_path, old_restpath);
1317    }
1318    if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
1319        log_msg(2,
1320                "I probably don't need to unmount or eject the CD-ROM but I'm doing it anyway.");
1321    }
1322    run_program_and_log_output("umount " MNT_CDROM, FALSE);
1323    if (!bkpinfo->please_dont_eject) {
1324        eject_device(bkpinfo->media_device);
1325    }
1326    paranoid_free(old_restpath);
1327    free(mountlist);
1328    free(raidlist);
1329    return (retval);
1330}
1331
1332/**************************************************************************
1333 *END_RESTORE_TO_LIVE_FILESYSTEM                                          *
1334 **************************************************************************/
1335
1336/* @} - end of restoreGroup */
1337
1338
1339#include <utime.h>
1340/**
1341 * @addtogroup LLrestoreGroup
1342 * @{
1343 */
1344/**
1345 * Restore biggiefile @p bigfileno from the currently mounted CD.
1346 * @param bkpinfo The backup information structure. Fields used:
1347 * - @c bkpinfo->backup_media_type
1348 * - @c bkpinfo->restore_path
1349 * @param bigfileno The biggiefile number (starting from 0) to restore.
1350 * @param filelist The node structure containing the list of files to restore.
1351 * If the biggiefile is not in this list, it will be skipped (return value will
1352 * still indicate success).
1353 * @return 0 for success (or skip), nonzero for failure.
1354 */
1355int
1356restore_a_biggiefile_from_CD(struct s_bkpinfo *bkpinfo,
1357                             long bigfileno,
1358                             struct s_node *filelist,
1359                             char *pathname_of_last_file_restored)
1360{
1361    FILE *fin;
1362    FILE *fout;
1363    FILE *fbzip2;
1364
1365  /** malloc ***/
1366    char *checksum, *outfile_fname, *tmp, *bzip2_command,
1367        *ntfsprog_command, *suffix, *sz_devfile;
1368    char *bigblk;
1369    char *p;
1370    int retval = 0;
1371    int finished = FALSE;
1372    long sliceno;
1373    long siz;
1374    char ntfsprog_fifo[MAX_STR_LEN];
1375    char *file_to_openout = NULL;
1376    struct s_filename_and_lstat_info biggiestruct;
1377    struct utimbuf the_utime_buf, *ubuf;
1378    bool use_ntfsprog_hack = FALSE;
1379    pid_t pid;
1380    int res = 0;
1381    int old_loglevel;
1382    char sz_msg[MAX_STR_LEN];
1383    struct s_node *node;
1384
1385    old_loglevel = g_loglevel;
1386    ubuf = &the_utime_buf;
1387    assert(bkpinfo != NULL);
1388
1389    malloc_string(checksum);
1390    malloc_string(outfile_fname);
1391    malloc_string(tmp);
1392    malloc_string(bzip2_command);
1393    malloc_string(ntfsprog_command);
1394    malloc_string(suffix);
1395    malloc_string(sz_devfile);
1396
1397    pathname_of_last_file_restored[0] = '\0';
1398    if (!(bigblk = malloc(TAPE_BLOCK_SIZE))) {
1399        fatal_error("Cannot malloc bigblk");
1400    }
1401
1402    if (!(fin = fopen(slice_fname(bigfileno, 0, ARCHIVES_PATH, ""), "r"))) {
1403        log_to_screen("Cannot even open bigfile's info file");
1404        return (1);
1405    }
1406
1407    memset((void *) &biggiestruct, 0, sizeof(biggiestruct));
1408    if (fread((void *) &biggiestruct, 1, sizeof(biggiestruct), fin) <
1409        sizeof(biggiestruct)) {
1410        log_msg(2, "Warning - unable to get biggiestruct of bigfile #%d",
1411                bigfileno + 1);
1412    }
1413    paranoid_fclose(fin);
1414
1415    strcpy(checksum, biggiestruct.checksum);
1416
1417    if (!checksum[0]) {
1418        sprintf(tmp, "Warning - bigfile %ld does not have a checksum",
1419                bigfileno + 1);
1420        log_msg(3, tmp);
1421        p = checksum;
1422    }
1423
1424    if (!strncmp(biggiestruct.filename, "/dev/", 5))    // Whether NTFS or not :)
1425    {
1426        strcpy(outfile_fname, biggiestruct.filename);
1427    } else {
1428        sprintf(outfile_fname, "%s/%s", bkpinfo->restore_path,
1429                biggiestruct.filename);
1430    }
1431
1432    /* skip file if we have a selective restore subset & it doesn't match */
1433    if (filelist != NULL) {
1434        node = find_string_at_node(filelist, biggiestruct.filename);
1435        if (!node) {
1436            log_msg(0, "Skipping %s (name isn't in filelist)",
1437                    biggiestruct.filename);
1438            pathname_of_last_file_restored[0] = '\0';
1439            return (0);
1440        } else if (!(node->selected)) {
1441            log_msg(1, "Skipping %s (name isn't in biggielist subset)",
1442                    biggiestruct.filename);
1443            pathname_of_last_file_restored[0] = '\0';
1444            return (0);
1445        }
1446    }
1447    /* otherwise, continue */
1448
1449    log_msg(1, "DEFINITELY restoring %s", biggiestruct.filename);
1450    if (biggiestruct.use_ntfsprog) {
1451        if (strncmp(biggiestruct.filename, "/dev/", 5)) {
1452            log_msg(1,
1453                    "I was in error when I set biggiestruct.use_ntfsprog to TRUE.");
1454            log_msg(1, "%s isn't even in /dev", biggiestruct.filename);
1455            biggiestruct.use_ntfsprog = FALSE;
1456        }
1457    }
1458
1459    if (biggiestruct.use_ntfsprog)  // if it's an NTFS device
1460//  if (!strncmp ( biggiestruct.filename, "/dev/", 5))
1461    {
1462        g_loglevel = 4;
1463        use_ntfsprog_hack = TRUE;
1464        log_msg(2,
1465                "Calling ntfsclone in background because %s is an NTFS /dev entry",
1466                outfile_fname);
1467        sprintf(sz_devfile, "/tmp/%d.%d.000", (int) (random() % 32768),
1468                (int) (random() % 32768));
1469        mkfifo(sz_devfile, 0x770);
1470        strcpy(ntfsprog_fifo, sz_devfile);
1471        file_to_openout = ntfsprog_fifo;
1472        switch (pid = fork()) {
1473        case -1:
1474            fatal_error("Fork failure");
1475        case 0:
1476            log_msg(3,
1477                    "CHILD - fip - calling feed_outfrom_ntfsprog(%s, %s)",
1478                    biggiestruct.filename, ntfsprog_fifo);
1479            res =
1480                feed_outfrom_ntfsprog(biggiestruct.filename,
1481                                       ntfsprog_fifo);
1482//          log_msg(3, "CHILD - fip - exiting");
1483            exit(res);
1484            break;
1485        default:
1486            log_msg(3,
1487                    "feed_into_ntfsprog() called in background --- pid=%ld",
1488                    (long int) (pid));
1489        }
1490    } else {
1491        use_ntfsprog_hack = FALSE;
1492        ntfsprog_fifo[0] = '\0';
1493        file_to_openout = outfile_fname;
1494        if (!does_file_exist(outfile_fname))    // yes, it looks weird with the '!' but it's correct that way
1495        {
1496            make_hole_for_file(outfile_fname);
1497        }
1498    }
1499
1500    sprintf(tmp, "Reassembling big file %ld (%s)", bigfileno + 1,
1501            outfile_fname);
1502    log_msg(2, tmp);
1503
1504    /*
1505       last slice is zero-length and uncompressed; when we find it, we stop.
1506       We DON'T wait until there are no more slices; if we did that,
1507       We might stop at end of CD, not at last slice (which is 0-len and uncompd)
1508     */
1509
1510    strncpy(pathname_of_last_file_restored, biggiestruct.filename,
1511            MAX_STR_LEN - 1);
1512    pathname_of_last_file_restored[MAX_STR_LEN - 1] = '\0';
1513
1514    log_msg(3, "file_to_openout = %s", file_to_openout);
1515    if (!(fout = fopen(file_to_openout, "w"))) {
1516        log_to_screen("Cannot openout outfile_fname - hard disk full?");
1517        return (1);
1518    }
1519    log_msg(3, "Opened out to %s", outfile_fname);  // CD/DVD --> mondorestore --> ntfsclone --> hard disk itself
1520
1521    for (sliceno = 1, finished = FALSE; !finished;) {
1522        if (!does_file_exist
1523            (slice_fname(bigfileno, sliceno, ARCHIVES_PATH, ""))
1524            &&
1525            !does_file_exist(slice_fname
1526                             (bigfileno, sliceno, ARCHIVES_PATH, "lzo"))
1527            &&
1528            !does_file_exist(slice_fname
1529                             (bigfileno, sliceno, ARCHIVES_PATH, "bz2"))) {
1530            log_msg(3,
1531                    "Cannot find a data slice or terminator slice on CD %d",
1532                    g_current_media_number);
1533            g_current_media_number++;
1534            sprintf(tmp,
1535                    "Asking for %s #%d so that I may read slice #%ld\n",
1536                    media_descriptor_string(bkpinfo->backup_media_type),
1537                    g_current_media_number, sliceno);
1538            log_msg(2, tmp);
1539            sprintf(tmp, "Restoring from %s #%d",
1540                    media_descriptor_string(bkpinfo->backup_media_type),
1541                    g_current_media_number);
1542            log_to_screen(tmp);
1543            insist_on_this_cd_number(bkpinfo, g_current_media_number);
1544            log_to_screen("Continuing to restore.");
1545        } else {
1546            strcpy(tmp,
1547                   slice_fname(bigfileno, sliceno, ARCHIVES_PATH, ""));
1548            if (does_file_exist(tmp) && length_of_file(tmp) == 0) {
1549                log_msg(2,
1550                        "End of bigfile # %ld (slice %ld is the terminator)",
1551                        bigfileno + 1, sliceno);
1552                finished = TRUE;
1553                continue;
1554            } else {
1555                if (does_file_exist
1556                    (slice_fname
1557                     (bigfileno, sliceno, ARCHIVES_PATH, "lzo"))) {
1558                    strcpy(bzip2_command, "lzop");
1559                    strcpy(suffix, "lzo");
1560                } else
1561                    if (does_file_exist
1562                        (slice_fname
1563                         (bigfileno, sliceno, ARCHIVES_PATH, "bz2"))) {
1564                    strcpy(bzip2_command, "bzip2");
1565                    strcpy(suffix, "bz2");
1566                } else
1567                    if (does_file_exist
1568                        (slice_fname
1569                         (bigfileno, sliceno, ARCHIVES_PATH, ""))) {
1570                    strcpy(bzip2_command, "");
1571                    strcpy(suffix, "");
1572                } else {
1573                    log_to_screen("OK, that's pretty fsck0red...");
1574                    return (1);
1575                }
1576            }
1577            if (bzip2_command[0] != '\0') {
1578                sprintf(bzip2_command + strlen(bzip2_command),
1579                        " -dc %s 2>> %s",
1580                        slice_fname(bigfileno, sliceno, ARCHIVES_PATH,
1581                                    suffix), MONDO_LOGFILE);
1582            } else {
1583                sprintf(bzip2_command, "cat %s 2>> %s",
1584                        slice_fname(bigfileno, sliceno, ARCHIVES_PATH,
1585                                    suffix), MONDO_LOGFILE);
1586            }
1587            sprintf(tmp, "Working on %s #%d, file #%ld, slice #%ld    ",
1588                    media_descriptor_string(bkpinfo->backup_media_type),
1589                    g_current_media_number, bigfileno + 1, sliceno);
1590            log_msg(2, tmp);
1591
1592            if (!g_text_mode) {
1593                newtDrawRootText(0, g_noof_rows - 2, tmp);
1594                newtRefresh();
1595                strip_spaces(tmp);
1596                update_progress_form(tmp);
1597            }
1598            if (!(fbzip2 = popen(bzip2_command, "r"))) {
1599                fatal_error("Can't run popen command");
1600            }
1601            while (!feof(fbzip2)) {
1602                siz = fread(bigblk, 1, TAPE_BLOCK_SIZE, fbzip2);
1603                if (siz > 0) {
1604                    sprintf(sz_msg, "Read %ld from fbzip2", siz);
1605                    siz = fwrite(bigblk, 1, siz, fout);
1606                    sprintf(sz_msg + strlen(sz_msg),
1607                            "; written %ld to fout", siz);
1608//        log_msg(2. sz_msg);
1609                }
1610            }
1611            paranoid_pclose(fbzip2);
1612
1613
1614            sliceno++;
1615            g_current_progress++;
1616        }
1617    }
1618/*
1619  memset(bigblk, TAPE_BLOCK_SIZE, 1); // This all looks very fishy...
1620  fwrite( bigblk, 1, TAPE_BLOCK_SIZE, fout);
1621  fwrite( bigblk, 1, TAPE_BLOCK_SIZE, fout);
1622  fwrite( bigblk, 1, TAPE_BLOCK_SIZE, fout);
1623  fwrite( bigblk, 1, TAPE_BLOCK_SIZE, fout);
1624*/
1625    paranoid_fclose(fout);
1626    g_loglevel = old_loglevel;
1627
1628    if (use_ntfsprog_hack) {
1629        log_msg(3, "Waiting for ntfsclone to finish");
1630        sprintf(tmp,
1631                " ps ax | grep \" ntfsclone \" | grep -v grep > /dev/null 2> /dev/null");
1632        while (system(tmp) == 0) {
1633            sleep(1);
1634        }
1635        log_it("OK, ntfsclone has really finished");
1636    }
1637
1638    if (strcmp(outfile_fname, "/dev/null")) {
1639        chown(outfile_fname, biggiestruct.properties.st_uid,
1640              biggiestruct.properties.st_gid);
1641        chmod(outfile_fname, biggiestruct.properties.st_mode);
1642        ubuf->actime = biggiestruct.properties.st_atime;
1643        ubuf->modtime = biggiestruct.properties.st_mtime;
1644        utime(outfile_fname, ubuf);
1645    }
1646    paranoid_free(bigblk);
1647    paranoid_free(checksum);
1648    paranoid_free(outfile_fname);
1649    paranoid_free(tmp);
1650    paranoid_free(bzip2_command);
1651    paranoid_free(ntfsprog_command);
1652    paranoid_free(suffix);
1653    paranoid_free(sz_devfile);
1654
1655    return (retval);
1656}
1657
1658/**************************************************************************
1659 *END_ RESTORE_A_BIGGIEFILE_FROM_CD                                       *
1660 **************************************************************************/
1661
1662
1663
1664/**
1665 * Restore a biggiefile from the currently opened stream.
1666 * @param bkpinfo The backup information structure. Fields used:
1667 * - @c bkpinfo->restore_path
1668 * - @c bkpinfo->zip_exe
1669 * @param orig_bf_fname The original filename of the biggiefile.
1670 * @param biggiefile_number The number of the biggiefile (starting from 0).
1671 * @param orig_checksum Unused.
1672 * @param biggiefile_size Unused.
1673 * @param filelist The node structure containing the list of files to be restored.
1674 * If @p orig_bf_fname is not in the list, it will be ignored.
1675 * @return 0 for success (or skip), nonzero for failure.
1676 * @bug orig_checksum and biggiefile_size are unused (except to check that they are non-NULL).
1677 */
1678int restore_a_biggiefile_from_stream(struct s_bkpinfo *bkpinfo, char *orig_bf_fname, long biggiefile_number, char *orig_checksum,   //UNUSED
1679                                     long long biggiefile_size, //UNUSED
1680                                     struct s_node *filelist,
1681                                     int use_ntfsprog,
1682                                     char *pathname_of_last_file_restored)
1683{
1684    FILE *pout;
1685    FILE *fin;
1686
1687  /** mallocs ********/
1688    char *tmp;
1689    char *command;
1690    char *outfile_fname;
1691    char *ntfsprog_command;
1692    char *sz_devfile;
1693    char *ntfsprog_fifo;
1694    char *file_to_openout = NULL;
1695
1696    struct s_node *node;
1697
1698    int old_loglevel;
1699    long current_slice_number = 0;
1700    int retval = 0;
1701    int res = 0;
1702    int ctrl_chr = '\0';
1703    long long slice_siz;
1704    bool dummy_restore = FALSE;
1705    bool use_ntfsprog_hack = FALSE;
1706    pid_t pid;
1707    struct s_filename_and_lstat_info biggiestruct;
1708    struct utimbuf the_utime_buf, *ubuf;
1709    ubuf = &the_utime_buf;
1710
1711    malloc_string(tmp);
1712    malloc_string(ntfsprog_fifo);
1713    malloc_string(outfile_fname);
1714    malloc_string(command);
1715    malloc_string(sz_devfile);
1716    malloc_string(ntfsprog_command);
1717    old_loglevel = g_loglevel;
1718    assert(bkpinfo != NULL);
1719    assert(orig_bf_fname != NULL);
1720    assert(orig_checksum != NULL);
1721
1722    pathname_of_last_file_restored[0] = '\0';
1723    if (use_ntfsprog == BLK_START_A_PIHBIGGIE) {
1724        use_ntfsprog = 1;
1725        log_msg(1, "%s --- pih=YES", orig_bf_fname);
1726    } else if (use_ntfsprog == BLK_START_A_NORMBIGGIE) {
1727        use_ntfsprog = 0;
1728        log_msg(1, "%s --- pih=NO", orig_bf_fname);
1729    } else {
1730        use_ntfsprog = 0;
1731        log_msg(1, "%s --- pih=NO (weird marker though)", orig_bf_fname);
1732    }
1733
1734    strncpy(pathname_of_last_file_restored, orig_bf_fname,
1735            MAX_STR_LEN - 1);
1736    pathname_of_last_file_restored[MAX_STR_LEN - 1] = '\0';
1737
1738    /* open out to biggiefile to be restored (or /dev/null if biggiefile is not to be restored) */
1739
1740    if (filelist != NULL) {
1741        node = find_string_at_node(filelist, orig_bf_fname);
1742        if (!node) {
1743            dummy_restore = TRUE;
1744            log_msg(1,
1745                    "Skipping big file %ld (%s) - not in biggielist subset",
1746                    biggiefile_number + 1, orig_bf_fname);
1747            pathname_of_last_file_restored[0] = '\0';
1748        } else if (!(node->selected)) {
1749            dummy_restore = TRUE;
1750            log_msg(1, "Skipping %s (name isn't in biggielist subset)",
1751                    orig_bf_fname);
1752            pathname_of_last_file_restored[0] = '\0';
1753        }
1754    }
1755
1756    if (use_ntfsprog) {
1757        if (strncmp(orig_bf_fname, "/dev/", 5)) {
1758            log_msg(1,
1759                    "I was in error when I set use_ntfsprog to TRUE.");
1760            log_msg(1, "%s isn't even in /dev", orig_bf_fname);
1761            use_ntfsprog = FALSE;
1762        }
1763    }
1764
1765    if (use_ntfsprog) {
1766        g_loglevel = 4;
1767        strcpy(outfile_fname, orig_bf_fname);
1768        use_ntfsprog_hack = TRUE;
1769        log_msg(2,
1770                "Calling ntfsclone in background because %s is a /dev entry",
1771                outfile_fname);
1772        sprintf(sz_devfile, "/tmp/%d.%d.000", (int) (random() % 32768),
1773                (int) (random() % 32768));
1774        mkfifo(sz_devfile, 0x770);
1775        strcpy(ntfsprog_fifo, sz_devfile);
1776        file_to_openout = ntfsprog_fifo;
1777        switch (pid = fork()) {
1778        case -1:
1779            fatal_error("Fork failure");
1780        case 0:
1781            log_msg(3,
1782                    "CHILD - fip - calling feed_outfrom_ntfsprog(%s, %s)",
1783                    outfile_fname, ntfsprog_fifo);
1784            res =
1785                feed_outfrom_ntfsprog(outfile_fname, ntfsprog_fifo);
1786//          log_msg(3, "CHILD - fip - exiting");
1787            exit(res);
1788            break;
1789        default:
1790            log_msg(3,
1791                    "feed_into_ntfsprog() called in background --- pid=%ld",
1792                    (long int) (pid));
1793        }
1794    } else {
1795        if (!strncmp(orig_bf_fname, "/dev/", 5))    // non-NTFS partition
1796        {
1797            strcpy(outfile_fname, orig_bf_fname);
1798        } else                  // biggiefile
1799        {
1800            sprintf(outfile_fname, "%s/%s", bkpinfo->restore_path,
1801                    orig_bf_fname);
1802        }
1803        use_ntfsprog_hack = FALSE;
1804        ntfsprog_fifo[0] = '\0';
1805        file_to_openout = outfile_fname;
1806        if (!does_file_exist(outfile_fname))    // yes, it looks weird with the '!' but it's correct that way
1807        {
1808            make_hole_for_file(outfile_fname);
1809        }
1810        sprintf(tmp, "Reassembling big file %ld (%s)",
1811                biggiefile_number + 1, orig_bf_fname);
1812        log_msg(2, tmp);
1813    }
1814
1815    if (dummy_restore) {
1816        sprintf(outfile_fname, "/dev/null");
1817    }
1818
1819    if (!bkpinfo->zip_exe[0]) {
1820        sprintf(command, "cat > \"%s\"", file_to_openout);
1821    } else {
1822        sprintf(command, "%s -dc > \"%s\" 2>> %s", bkpinfo->zip_exe,
1823                file_to_openout, MONDO_LOGFILE);
1824    }
1825    sprintf(tmp, "Pipe command = '%s'", command);
1826    log_msg(3, tmp);
1827
1828    /* restore biggiefile, one slice at a time */
1829    if (!(pout = popen(command, "w"))) {
1830        fatal_error("Cannot pipe out");
1831    }
1832    for (res = read_header_block_from_stream(&slice_siz, tmp, &ctrl_chr);
1833         ctrl_chr != BLK_STOP_A_BIGGIE;
1834         res = read_header_block_from_stream(&slice_siz, tmp, &ctrl_chr)) {
1835        if (ctrl_chr != BLK_START_AN_AFIO_OR_SLICE) {
1836            wrong_marker(BLK_START_AN_AFIO_OR_SLICE, ctrl_chr);
1837        }
1838        sprintf(tmp, "Working on file #%ld, slice #%ld    ",
1839                biggiefile_number + 1, current_slice_number);
1840        log_msg(2, tmp);
1841        if (!g_text_mode) {
1842            newtDrawRootText(0, g_noof_rows - 2, tmp);
1843            newtRefresh();
1844        }
1845        strip_spaces(tmp);
1846        update_progress_form(tmp);
1847        if (current_slice_number == 0) {
1848            res =
1849                read_file_from_stream_to_file(bkpinfo,
1850                                              "/tmp/biggie-blah.txt",
1851                                              slice_siz);
1852            if (!(fin = fopen("/tmp/biggie-blah.txt", "r"))) {
1853                log_OS_error("blah blah");
1854            } else {
1855                if (fread
1856                    ((void *) &biggiestruct, 1, sizeof(biggiestruct),
1857                     fin) < sizeof(biggiestruct)) {
1858                    log_msg(2,
1859                            "Warning - unable to get biggiestruct of bigfile #%d",
1860                            biggiefile_number + 1);
1861                }
1862                paranoid_fclose(fin);
1863            }
1864        } else {
1865            res =
1866                read_file_from_stream_to_stream(bkpinfo, pout, slice_siz);
1867        }
1868        retval += res;
1869        res = read_header_block_from_stream(&slice_siz, tmp, &ctrl_chr);
1870        if (ctrl_chr != BLK_STOP_AN_AFIO_OR_SLICE) {
1871            wrong_marker(BLK_STOP_AN_AFIO_OR_SLICE, ctrl_chr);
1872        }
1873        current_slice_number++;
1874        g_current_progress++;
1875    }
1876    paranoid_pclose(pout);
1877
1878    log_msg(1, "pathname_of_last_file_restored is now %s",
1879            pathname_of_last_file_restored);
1880
1881    if (use_ntfsprog_hack) {
1882        log_msg(3, "Waiting for ntfsclone to finish");
1883        sprintf(tmp,
1884                " ps ax | grep \" ntfsclone \" | grep -v grep > /dev/null 2> /dev/null");
1885        while (system(tmp) == 0) {
1886            sleep(1);
1887        }
1888        log_msg(3, "OK, ntfsclone has really finished");
1889    }
1890
1891    log_msg(3, "biggiestruct.filename = %s", biggiestruct.filename);
1892    log_msg(3, "biggiestruct.checksum = %s", biggiestruct.checksum);
1893    if (strcmp(outfile_fname, "/dev/null")) {
1894        chmod(outfile_fname, biggiestruct.properties.st_mode);
1895        chown(outfile_fname, biggiestruct.properties.st_uid,
1896              biggiestruct.properties.st_gid);
1897        ubuf->actime = biggiestruct.properties.st_atime;
1898        ubuf->modtime = biggiestruct.properties.st_mtime;
1899        utime(outfile_fname, ubuf);
1900    }
1901
1902    paranoid_free(tmp);
1903    paranoid_free(outfile_fname);
1904    paranoid_free(command);
1905    paranoid_free(ntfsprog_command);
1906    paranoid_free(sz_devfile);
1907    paranoid_free(ntfsprog_fifo);
1908    g_loglevel = old_loglevel;
1909    return (retval);
1910}
1911
1912/**************************************************************************
1913 *END_RESTORE_A_BIGGIEFILE_FROM_STREAM                                    *
1914 **************************************************************************/
1915
1916
1917
1918/**
1919 * Restore @p tarball_fname from CD.
1920 * @param tarball_fname The filename of the tarball to restore (in /mnt/cdrom).
1921 * This will be used unmodified.
1922 * @param current_tarball_number The number (starting from 0) of the fileset
1923 * we're restoring now.
1924 * @param filelist The node structure containing the list of files to be
1925 * restored. If no file in the afioball is in this list, afio will still be
1926 * called, but nothing will be written.
1927 * @return 0 for success, nonzero for failure.
1928 */
1929int
1930restore_a_tarball_from_CD(char *tarball_fname,
1931                          long current_tarball_number,
1932                          struct s_node *filelist)
1933{
1934    int retval = 0;
1935    int res;
1936    char *p;
1937
1938  /** malloc **/
1939    char *command;
1940    char *tmp;
1941    char *filelist_name;
1942    char *filelist_subset_fname;
1943    char *executable;
1944    char *temp_log;
1945    char screen_message[100];
1946    long matches = 0;
1947    bool use_star;
1948    char *xattr_fname;
1949    char *acl_fname;
1950//  char files_to_restore_this_time_fname[MAX_STR_LEN];
1951
1952    assert_string_is_neither_NULL_nor_zerolength(tarball_fname);
1953    malloc_string(command);
1954    malloc_string(tmp);
1955    malloc_string(filelist_name);
1956    malloc_string(filelist_subset_fname);
1957    malloc_string(executable);
1958    malloc_string(temp_log);
1959    malloc_string(xattr_fname);
1960    malloc_string(acl_fname);
1961
1962    log_msg(5, "Entering");
1963    filelist_subset_fname[0] = '\0';
1964    use_star = (strstr(tarball_fname, ".star")) ? TRUE : FALSE;
1965//  sprintf(files_to_restore_this_time_fname, "/tmp/ftrttf.%d.%d", (int)getpid(), (int)random());
1966    sprintf(command, "mkdir -p %s/tmp", MNT_RESTORING);
1967    run_program_and_log_output(command, 9);
1968    sprintf(temp_log, "/tmp/%d.%d", (int) (random() % 32768),
1969            (int) (random() % 32768));
1970
1971    sprintf(filelist_name, MNT_CDROM "/archives/filelist.%ld",
1972            current_tarball_number);
1973    if (length_of_file(filelist_name) <= 2) {
1974        log_msg(2, "There are _zero_ files in filelist '%s'",
1975                filelist_name);
1976        log_msg(2,
1977                "This is a bit silly (ask Hugo to fix mondo-makefilelist, please)");
1978        log_msg(2,
1979                "but it's non-critical. It's cosmetic. Don't worry about it.");
1980        retval = 0;
1981        goto leave_sub;
1982    }
1983    if (count_lines_in_file(filelist_name) <= 0
1984        || length_of_file(tarball_fname) <= 0) {
1985        log_msg(3, "length_of_file(%s) = %ld", tarball_fname,
1986                length_of_file(tarball_fname));
1987        sprintf(tmp, "Unable to restore fileset #%ld (CD I/O error)",
1988                current_tarball_number);
1989        log_to_screen(tmp);
1990        retval = 1;
1991        goto leave_sub;
1992    }
1993
1994    if (filelist) {
1995        sprintf(filelist_subset_fname, "/tmp/filelist-subset-%ld.tmp",
1996                current_tarball_number);
1997        if ((matches =
1998             save_filelist_entries_in_common(filelist_name, filelist,
1999                                             filelist_subset_fname,
2000                                             use_star))
2001            <= 0) {
2002            sprintf(tmp, "Skipping fileset %ld", current_tarball_number);
2003            log_msg(1, tmp);
2004        } else {
2005            log_msg(3, "Saved fileset %ld's subset to %s",
2006                    current_tarball_number, filelist_subset_fname);
2007        }
2008        sprintf(screen_message, "Tarball #%ld --- %ld matches",
2009                current_tarball_number, matches);
2010        log_to_screen(screen_message);
2011    } else {
2012        filelist_subset_fname[0] = '\0';
2013    }
2014
2015    if (filelist == NULL || matches > 0) {
2016        sprintf(xattr_fname, XATTR_LIST_FNAME_RAW_SZ,
2017                MNT_CDROM "/archives", current_tarball_number);
2018        sprintf(acl_fname, ACL_LIST_FNAME_RAW_SZ, MNT_CDROM "/archives",
2019                current_tarball_number);
2020        if (strstr(tarball_fname, ".bz2")) {
2021            strcpy(executable, "bzip2");
2022        } else if (strstr(tarball_fname, ".lzo")) {
2023            strcpy(executable, "lzop");
2024        } else {
2025            executable[0] = '\0';
2026        }
2027        if (executable[0]) {
2028            sprintf(tmp, "which %s > /dev/null 2> /dev/null", executable);
2029            if (run_program_and_log_output(tmp, FALSE)) {
2030                log_to_screen
2031                    ("(compare_a_tarball) Compression program not found - oh no!");
2032                paranoid_MR_finish(1);
2033            }
2034            strcpy(tmp, executable);
2035            sprintf(executable, "-P %s -Z", tmp);
2036        }
2037#ifdef __FreeBSD__
2038#define BUFSIZE 512
2039#else
2040#define BUFSIZE (1024L*1024L)/TAPE_BLOCK_SIZE
2041#endif
2042
2043//      if (strstr(tarball_fname, ".star."))
2044        if (use_star) {
2045            sprintf(command,
2046                    "star -x -force-remove -U " STAR_ACL_SZ
2047                    " errctl= file=%s", tarball_fname);
2048            if (strstr(tarball_fname, ".bz2")) {
2049                strcat(command, " -bz");
2050            }
2051        } else {
2052            if (filelist_subset_fname[0] != '\0') {
2053                sprintf(command,
2054                        "afio -i -M 8m -b %ld -c %ld %s -w %s %s",
2055                        TAPE_BLOCK_SIZE,
2056                        BUFSIZE, executable, filelist_subset_fname,
2057//             files_to_restore_this_time_fname,
2058                        tarball_fname);
2059            } else {
2060                sprintf(command,
2061                        "afio -i -b %ld -c %ld -M 8m %s %s",
2062                        TAPE_BLOCK_SIZE,
2063                        BUFSIZE, executable, tarball_fname);
2064            }
2065        }
2066#undef BUFSIZE
2067        sprintf(command + strlen(command), " 2>> %s >> %s", temp_log,
2068                temp_log);
2069        log_msg(1, "command = '%s'", command);
2070        unlink(temp_log);
2071        res = system(command);
2072        if (res) {
2073            p = strstr(command, "-acl ");
2074            if (p) {
2075                p[0] = p[1] = p[2] = p[3] = ' ';
2076                log_msg(1, "new command = '%s'", command);
2077                res = system(command);
2078            }
2079        }
2080        if (res && length_of_file(temp_log) < 5) {
2081            res = 0;
2082        }
2083
2084        log_msg(1, "Setting fattr list %s", xattr_fname);
2085        if (length_of_file(xattr_fname) > 0) {
2086            res = set_fattr_list(filelist_subset_fname, xattr_fname);
2087            if (res) {
2088                log_to_screen
2089                    ("Errors occurred while setting extended attributes");
2090            } else {
2091                log_msg(1, "I set xattr OK");
2092            }
2093            retval += res;
2094        }
2095        if (length_of_file(acl_fname) > 0) {
2096            log_msg(1, "Setting acl list %s", acl_fname);
2097            res = set_acl_list(filelist_subset_fname, acl_fname);
2098            if (res) {
2099                log_to_screen
2100                    ("Errors occurred while setting access control lists");
2101            } else {
2102                log_msg(1, "I set ACL OK");
2103            }
2104            retval += res;
2105        }
2106        if (retval) {
2107            sprintf(command, "cat %s >> %s", temp_log, MONDO_LOGFILE);
2108            system(command);
2109            log_msg(2, "Errors occurred while processing fileset #%d",
2110                    current_tarball_number);
2111        } else {
2112            log_msg(2, "Fileset #%d processed OK", current_tarball_number);
2113        }
2114    }
2115    if (does_file_exist("/PAUSE")) {
2116        popup_and_OK
2117            ("Press ENTER to go on. Delete /PAUSE to stop these pauses.");
2118    }
2119    unlink(filelist_subset_fname);
2120    unlink(xattr_fname);
2121    unlink(acl_fname);
2122    unlink(temp_log);
2123
2124  leave_sub:
2125    paranoid_free(command);
2126    paranoid_free(tmp);
2127    paranoid_free(filelist_name);
2128    paranoid_free(filelist_subset_fname);
2129    paranoid_free(executable);
2130    paranoid_free(temp_log);
2131    paranoid_free(xattr_fname);
2132    paranoid_free(acl_fname);
2133    log_msg(5, "Leaving");
2134    return (retval);
2135}
2136
2137/**************************************************************************
2138 *END_RESTORE_A_TARBALL_FROM_CD                                           *
2139 **************************************************************************/
2140
2141
2142/**
2143 * Restore a tarball from the currently opened stream.
2144 * @param bkpinfo The backup information structure. Fields used:
2145 * - @c bkpinfo->backup_media_type
2146 * - @c bkpinfo->media_device
2147 * - @c bkpinfo->zip_exe
2148 * @param tarball_fname The filename of the afioball to restore.
2149 * @param current_tarball_number The number (starting from 0) of the fileset
2150 * we're restoring now.
2151 * @param filelist The node structure containing the list of files to be
2152 * restored. If no file in the afioball is in this list, afio will still be
2153 * called, but nothing will be written.
2154 * @param size The size (in @b bytes) of the afioball.
2155 * @return 0 for success, nonzero for failure.
2156 */
2157int
2158restore_a_tarball_from_stream(struct s_bkpinfo *bkpinfo,
2159                              char *tarball_fname,
2160                              long current_tarball_number,
2161                              struct s_node *filelist,
2162                              long long size, char *xattr_fname,
2163                              char *acl_fname)
2164{
2165    int retval = 0;
2166    int res = 0;
2167
2168  /** malloc add ***/
2169    char *tmp;
2170    char *command;
2171    char *afio_fname;
2172    char *filelist_fname;
2173    char *filelist_subset_fname;
2174    char *executable;
2175    long matches = 0;
2176    bool restore_this_fileset = FALSE;
2177    bool use_star;
2178
2179    assert(bkpinfo != NULL);
2180    assert_string_is_neither_NULL_nor_zerolength(tarball_fname);
2181    malloc_string(filelist_subset_fname);
2182    malloc_string(filelist_fname);
2183    malloc_string(afio_fname);
2184    malloc_string(executable);
2185    malloc_string(command);
2186    malloc_string(tmp);
2187    filelist_subset_fname[0] = '\0';
2188    /* to do it with a file... */
2189    use_star = (strstr(tarball_fname, ".star")) ? TRUE : FALSE;
2190    sprintf(tmp,
2191            "Restoring from fileset #%ld (%ld KB) on %s #%d",
2192            current_tarball_number, (long) size >> 10,
2193            media_descriptor_string(bkpinfo->backup_media_type),
2194            g_current_media_number);
2195    log_msg(2, tmp);
2196    run_program_and_log_output("mkdir -p " MNT_RESTORING "/tmp", FALSE);
2197
2198  /****************************************************************************
2199   * Use RAMDISK's /tmp; saves time; oh wait, it's too small                  *
2200   * Well, pipe from tape to afio, then; oh wait, can't do that either: bug   *
2201   * in afio or someting; oh darn.. OK, use tmpfs :-)                         *
2202   ****************************************************************************/
2203    filelist_fname[0] = filelist_subset_fname[0] = '\0';
2204    sprintf(afio_fname, "/tmp/tmpfs/archive.tmp.%ld",
2205            current_tarball_number);
2206    sprintf(filelist_fname, "%s/filelist.%ld", bkpinfo->tmpdir,
2207            current_tarball_number);
2208    sprintf(filelist_subset_fname, "%s/filelist-subset-%ld.tmp",
2209            bkpinfo->tmpdir, current_tarball_number);
2210//  sprintf(filelist_fname, "/tmp/tmpfs/temp-filelist.%ld", current_tarball_number);
2211    res = read_file_from_stream_to_file(bkpinfo, afio_fname, size);
2212    if (strstr(tarball_fname, ".star")) {
2213        bkpinfo->use_star = TRUE;
2214    }
2215    if (res) {
2216        log_msg(1, "Warning - error reading afioball from tape");
2217    }
2218    if (bkpinfo->compression_level == 0) {
2219        executable[0] = '\0';
2220    } else {
2221        if (bkpinfo->use_star) {
2222            strcpy(executable, " -bz");
2223        } else {
2224            sprintf(executable, "-P %s -Z", bkpinfo->zip_exe);
2225        }
2226    }
2227
2228    if (!filelist)              // if unconditional restore then restore entire fileset
2229    {
2230        restore_this_fileset = TRUE;
2231    } else                      // If restoring selectively then get TOC from tarball
2232    {
2233        if (strstr(tarball_fname, ".star.")) {
2234            use_star = TRUE;
2235            sprintf(command, "star -t file=%s %s", afio_fname, executable);
2236        } else {
2237            use_star = FALSE;
2238            sprintf(command, "afio -t -M 8m -b %ld %s %s", TAPE_BLOCK_SIZE,
2239                    executable, afio_fname);
2240        }
2241        sprintf(command + strlen(command), " > %s 2>> %s", filelist_fname,
2242                MONDO_LOGFILE);
2243        log_msg(1, "command = %s", command);
2244        if (system(command)) {
2245            log_msg(4, "Warning - error occurred while retrieving TOC");
2246        }
2247        if ((matches =
2248             save_filelist_entries_in_common(filelist_fname, filelist,
2249                                             filelist_subset_fname,
2250                                             use_star))
2251            <= 0 || length_of_file(filelist_subset_fname) < 2) {
2252            if (length_of_file(filelist_subset_fname) < 2) {
2253                log_msg(1, "No matches found in fileset %ld",
2254                        current_tarball_number);
2255            }
2256            sprintf(tmp, "Skipping fileset %ld", current_tarball_number);
2257            log_msg(2, tmp);
2258            restore_this_fileset = FALSE;
2259        } else {
2260            log_msg(5, "%ld matches. Saved fileset %ld's subset to %s",
2261                    matches, current_tarball_number,
2262                    filelist_subset_fname);
2263            restore_this_fileset = TRUE;
2264        }
2265    }
2266
2267// Concoct the call to star/afio to restore files
2268    if (strstr(tarball_fname, ".star."))    // star
2269    {
2270        sprintf(command, "star -x file=%s %s", afio_fname, executable);
2271        if (filelist) {
2272            sprintf(command + strlen(command), " list=%s",
2273                    filelist_subset_fname);
2274        }
2275    } else                      // afio
2276    {
2277        sprintf(command, "afio -i -M 8m -b %ld %s", TAPE_BLOCK_SIZE,
2278                executable);
2279        if (filelist) {
2280            sprintf(command + strlen(command), " -w %s",
2281                    filelist_subset_fname);
2282        }
2283        sprintf(command + strlen(command), " %s", afio_fname);
2284    }
2285    sprintf(command + strlen(command), " 2>> %s", MONDO_LOGFILE);
2286
2287// Call if IF there are files to restore (selectively/unconditionally)
2288    if (restore_this_fileset) {
2289        log_msg(1, "Calling command='%s'", command);
2290        paranoid_system(command);
2291
2292        iamhere("Restoring xattr, acl stuff");
2293        res = set_fattr_list(filelist_subset_fname, xattr_fname);
2294        if (res) {
2295            log_msg(1, "Errors occurred while setting xattr");
2296        } else {
2297            log_msg(1, "I set xattr OK");
2298        }
2299        retval += res;
2300
2301        res = set_acl_list(filelist_subset_fname, acl_fname);
2302        if (res) {
2303            log_msg(1, "Errors occurred while setting ACL");
2304        } else {
2305            log_msg(1, "I set ACL OK");
2306        }
2307        retval += res;
2308
2309    } else {
2310        log_msg(1, "NOT CALLING '%s'", command);
2311    }
2312
2313    if (does_file_exist("/PAUSE") && current_tarball_number >= 50) {
2314        log_to_screen("Paused after set %ld", current_tarball_number);
2315        popup_and_OK("Pausing. Press ENTER to continue.");
2316    }
2317
2318    unlink(filelist_subset_fname);
2319    unlink(filelist_fname);
2320    unlink(afio_fname);
2321
2322    paranoid_free(filelist_subset_fname);
2323    paranoid_free(filelist_fname);
2324    paranoid_free(afio_fname);
2325    paranoid_free(command);
2326    paranoid_free(tmp);
2327    return (retval);
2328}
2329
2330/**************************************************************************
2331 *END_RESTORE_A_TARBALL_FROM_STREAM                                       *
2332 **************************************************************************/
2333
2334
2335
2336
2337/**
2338 * Restore all biggiefiles from all media in this CD backup.
2339 * The CD with the last afioball should be currently mounted.
2340 * @param bkpinfo The backup information structure. @c backup_media_type is the
2341 * only field used in this function.
2342 * @param filelist The node structure containing the list of files to be
2343 * restored. If a prospective biggiefile is not in this list, it will be ignored.
2344 * @return 0 for success, nonzero for failure.
2345 */
2346int
2347restore_all_biggiefiles_from_CD(struct s_bkpinfo *bkpinfo,
2348                                struct s_node *filelist)
2349{
2350    int retval = 0;
2351    int res;
2352    long noof_biggiefiles, bigfileno = 0, total_slices;
2353  /** malloc **/
2354    char *tmp;
2355    bool just_changed_cds = FALSE, finished;
2356    char *xattr_fname;
2357    char *acl_fname;
2358    char *biggies_whose_EXATs_we_should_set;    // EXtended ATtributes
2359    char *pathname_of_last_biggie_restored;
2360    FILE *fbw = NULL;
2361
2362    malloc_string(xattr_fname);
2363    malloc_string(acl_fname);
2364    malloc_string(tmp);
2365    malloc_string(biggies_whose_EXATs_we_should_set);
2366    malloc_string(pathname_of_last_biggie_restored);
2367    assert(bkpinfo != NULL);
2368
2369    sprintf(biggies_whose_EXATs_we_should_set,
2370            "%s/biggies-whose-EXATs-we-should-set", bkpinfo->tmpdir);
2371    if (!(fbw = fopen(biggies_whose_EXATs_we_should_set, "w"))) {
2372        log_msg(1, "Warning - cannot openout %s",
2373                biggies_whose_EXATs_we_should_set);
2374    }
2375
2376    read_cfg_var(g_mondo_cfg_file, "total-slices", tmp);
2377    total_slices = atol(tmp);
2378    sprintf(tmp, "Reassembling large files      ");
2379    mvaddstr_and_log_it(g_currentY, 0, tmp);
2380    if (length_of_file(BIGGIELIST) < 6) {
2381        log_msg(1, "OK, no biggielist; not restoring biggiefiles");
2382        return (0);
2383    }
2384    noof_biggiefiles = count_lines_in_file(BIGGIELIST);
2385    if (noof_biggiefiles <= 0) {
2386        log_msg(2,
2387                "OK, no biggiefiles in biggielist; not restoring biggiefiles");
2388        return (0);
2389    }
2390    sprintf(tmp, "OK, there are %ld biggiefiles in the archives",
2391            noof_biggiefiles);
2392    log_msg(2, tmp);
2393
2394    open_progress_form("Reassembling large files",
2395                       "I am now reassembling all the large files.",
2396                       "Please wait. This may take some time.",
2397                       "", total_slices);
2398    for (bigfileno = 0, finished = FALSE; !finished;) {
2399        log_msg(2, "Thinking about restoring bigfile %ld", bigfileno + 1);
2400        if (!does_file_exist(slice_fname(bigfileno, 0, ARCHIVES_PATH, ""))) {
2401            log_msg(3,
2402                    "...but its first slice isn't on this CD. Perhaps this was a selective restore?");
2403            log_msg(3, "Cannot find bigfile #%ld 's first slice on %s #%d",
2404                    bigfileno + 1,
2405                    media_descriptor_string(bkpinfo->backup_media_type),
2406                    g_current_media_number);
2407            log_msg(3, "Slicename would have been %s",
2408                    slice_fname(bigfileno + 1, 0, ARCHIVES_PATH, ""));
2409            // I'm not positive 'just_changed_cds' is even necessary...
2410            if (just_changed_cds) {
2411                just_changed_cds = FALSE;
2412                log_msg(3,
2413                        "I'll continue to scan this CD for bigfiles to be restored.");
2414            } else if (does_file_exist(MNT_CDROM "/archives/NOT-THE-LAST")) {
2415                insist_on_this_cd_number(bkpinfo,
2416                                         ++g_current_media_number);
2417                sprintf(tmp, "Restoring from %s #%d",
2418                        media_descriptor_string(bkpinfo->
2419                                                backup_media_type),
2420                        g_current_media_number);
2421                log_to_screen(tmp);
2422                just_changed_cds = TRUE;
2423            } else {
2424                log_msg(2, "There was no bigfile #%ld. That's OK.",
2425                        bigfileno + 1);
2426                log_msg(2, "I'm going to stop restoring bigfiles now.");
2427                finished = TRUE;
2428            }
2429        } else {
2430            just_changed_cds = FALSE;
2431            sprintf(tmp, "Restoring big file %ld", bigfileno + 1);
2432            update_progress_form(tmp);
2433            res =
2434                restore_a_biggiefile_from_CD(bkpinfo, bigfileno, filelist,
2435                                             pathname_of_last_biggie_restored);
2436            iamhere(pathname_of_last_biggie_restored);
2437            if (fbw && pathname_of_last_biggie_restored[0]) {
2438                fprintf(fbw, "%s\n", pathname_of_last_biggie_restored);
2439            }
2440            retval += res;
2441            bigfileno++;
2442
2443        }
2444    }
2445
2446    if (fbw) {
2447        fclose(fbw);
2448        sprintf(acl_fname, ACL_BIGGLST_FNAME_RAW_SZ, ARCHIVES_PATH);
2449        sprintf(xattr_fname, XATTR_BIGGLST_FNAME_RAW_SZ, ARCHIVES_PATH);
2450        if (length_of_file(acl_fname) > 0 && find_home_of_exe("setfacl")) {
2451            set_acl_list(biggies_whose_EXATs_we_should_set, acl_fname);
2452        }
2453        if (length_of_file(xattr_fname) > 0
2454            && find_home_of_exe("setfattr")) {
2455            set_fattr_list(biggies_whose_EXATs_we_should_set, xattr_fname);
2456        }
2457    }
2458    if (does_file_exist("/PAUSE")) {
2459        popup_and_OK
2460            ("Press ENTER to go on. Delete /PAUSE to stop these pauses.");
2461    }
2462    close_progress_form();
2463    if (retval) {
2464        mvaddstr_and_log_it(g_currentY++, 74, "Errors.");
2465    } else {
2466        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
2467    }
2468    paranoid_free(xattr_fname);
2469    paranoid_free(acl_fname);
2470    paranoid_free(tmp);
2471    paranoid_free(biggies_whose_EXATs_we_should_set);
2472    paranoid_free(pathname_of_last_biggie_restored);
2473    return (retval);
2474}
2475
2476/**************************************************************************
2477 *END_RESTORE_ALL_BIGGIFILES_FROM_CD                                      *
2478 **************************************************************************/
2479
2480
2481
2482/**
2483 * Restore all afioballs from all CDs in the backup.
2484 * The first CD should be inserted (if not, it will be asked for).
2485 * @param bkpinfo The backup information structure. @c backup_media_type is the
2486 * only field used in @e this function.
2487 * @param filelist The node structure containing the list of files to be
2488 * restored. If no file in some particular afioball is in this list, afio will
2489 * still be called for that fileset, but nothing will be written.
2490 * @return 0 for success, or the number of filesets that failed.
2491 */
2492int
2493restore_all_tarballs_from_CD(struct s_bkpinfo *bkpinfo,
2494                             struct s_node *filelist)
2495{
2496    int retval = 0;
2497    int res;
2498    int attempts;
2499    long current_tarball_number = 0;
2500    long max_val;
2501  /**malloc ***/
2502    char *tmp;
2503    char *tarball_fname;
2504    char *progress_str;
2505    char *comment;
2506
2507    malloc_string(tmp);
2508    malloc_string(tarball_fname);
2509    malloc_string(progress_str);
2510    malloc_string(comment);
2511
2512    assert(bkpinfo != NULL);
2513
2514    mvaddstr_and_log_it(g_currentY, 0, "Restoring from archives");
2515    log_msg(2,
2516            "Insisting on 1st CD, so that I can have a look at LAST-FILELIST-NUMBER");
2517    if (g_current_media_number != 1) {
2518        log_msg(3, "OK, that's jacked up.");
2519        g_current_media_number = 1;
2520    }
2521    insist_on_this_cd_number(bkpinfo, g_current_media_number);
2522    read_cfg_var(g_mondo_cfg_file, "last-filelist-number", tmp);
2523    max_val = atol(tmp) + 1;
2524    sprintf(progress_str, "Restoring from %s #%d",
2525            media_descriptor_string(bkpinfo->backup_media_type),
2526            g_current_media_number);
2527    log_to_screen(progress_str);
2528    open_progress_form("Restoring from archives",
2529                       "Restoring data from the archives.",
2530                       "Please wait. This may take some time.",
2531                       progress_str, max_val);
2532    for (;;) {
2533        insist_on_this_cd_number(bkpinfo, g_current_media_number);
2534        update_progress_form(progress_str);
2535        sprintf(tarball_fname, MNT_CDROM "/archives/%ld.afio.bz2",
2536                current_tarball_number);
2537        if (!does_file_exist(tarball_fname)) {
2538            sprintf(tarball_fname, MNT_CDROM "/archives/%ld.afio.lzo",
2539                    current_tarball_number);
2540        }
2541        if (!does_file_exist(tarball_fname)) {
2542            sprintf(tarball_fname, MNT_CDROM "/archives/%ld.afio.",
2543                    current_tarball_number);
2544        }
2545        if (!does_file_exist(tarball_fname)) {
2546            sprintf(tarball_fname, MNT_CDROM "/archives/%ld.star.bz2",
2547                    current_tarball_number);
2548        }
2549        if (!does_file_exist(tarball_fname)) {
2550            sprintf(tarball_fname, MNT_CDROM "/archives/%ld.star.",
2551                    current_tarball_number);
2552        }
2553        if (!does_file_exist(tarball_fname)) {
2554            if (current_tarball_number == 0) {
2555                log_to_screen
2556                    ("No tarballs. Strange. Maybe you only backed up freakin' big files?");
2557                return (0);
2558            }
2559            if (!does_file_exist(MNT_CDROM "/archives/NOT-THE-LAST")
2560                || system("find " MNT_CDROM
2561                          "/archives/slice* > /dev/null 2> /dev/null") ==
2562                0) {
2563                break;
2564            }
2565            g_current_media_number++;
2566            sprintf(progress_str, "Restoring from %s #%d",
2567                    media_descriptor_string(bkpinfo->backup_media_type),
2568                    g_current_media_number);
2569            log_to_screen(progress_str);
2570        } else {
2571            sprintf(progress_str, "Restoring from fileset #%ld on %s #%d",
2572                    current_tarball_number,
2573                    media_descriptor_string(bkpinfo->backup_media_type),
2574                    g_current_media_number);
2575//    log_msg(3, "progress_str = %s", progress_str);
2576            for (res = 999, attempts = 0; attempts < 3 && res != 0;
2577                 attempts++) {
2578                res =
2579                    restore_a_tarball_from_CD(tarball_fname,
2580                                              current_tarball_number,
2581                                              filelist);
2582            }
2583            sprintf(tmp, "%s #%d, fileset #%ld - restore ",
2584                    media_descriptor_string(bkpinfo->backup_media_type),
2585                    g_current_media_number, current_tarball_number);
2586            if (res) {
2587                strcat(tmp, "reported errors");
2588            } else if (attempts > 1) {
2589                strcat(tmp, "succeeded");
2590            } else {
2591                strcat(tmp, "succeeded");
2592            }
2593            if (attempts > 1) {
2594                sprintf(tmp + strlen(tmp), " (%d attempts) - review logs",
2595                        attempts);
2596            }
2597            strcpy(comment, tmp);
2598            if (attempts > 1) {
2599                log_to_screen(comment);
2600            }
2601
2602            retval += res;
2603            current_tarball_number++;
2604            g_current_progress++;
2605        }
2606    }
2607    close_progress_form();
2608    if (retval) {
2609        mvaddstr_and_log_it(g_currentY++, 74, "Errors.");
2610    } else {
2611        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
2612    }
2613    paranoid_free(tmp);
2614    paranoid_free(tarball_fname);
2615    paranoid_free(progress_str);
2616    paranoid_free(comment);
2617
2618    return (retval);
2619}
2620
2621/**************************************************************************
2622 *END_RESTORE_ALL_TARBALLS_FROM_CD                                        *
2623 **************************************************************************/
2624
2625
2626
2627/**
2628 * Restore all biggiefiles from the currently opened stream.
2629 * @param bkpinfo The backup information structure. Passed to other functions.
2630 * @param filelist The node structure containing the list of files to be
2631 * restored. If a prospective biggiefile is not in the list, it will be ignored.
2632 * @return 0 for success, or the number of biggiefiles that failed.
2633 */
2634int
2635restore_all_biggiefiles_from_stream(struct s_bkpinfo *bkpinfo,
2636                                    struct s_node *filelist)
2637{
2638    long noof_biggiefiles;
2639    long current_bigfile_number = 0;
2640    long total_slices;
2641
2642    int retval = 0;
2643    int res = 0;
2644    int ctrl_chr;
2645
2646  /** malloc add ****/
2647    char *tmp;
2648    char *biggie_fname;
2649    char *biggie_cksum;
2650    char *xattr_fname;
2651    char *acl_fname;
2652    char *p;
2653    char *pathname_of_last_biggie_restored;
2654    char *biggies_whose_EXATs_we_should_set;    // EXtended ATtributes
2655    long long biggie_size;
2656    FILE *fbw = NULL;
2657
2658    malloc_string(tmp);
2659    malloc_string(biggie_fname);
2660    malloc_string(biggie_cksum);
2661    malloc_string(xattr_fname);
2662    malloc_string(acl_fname);
2663    malloc_string(biggies_whose_EXATs_we_should_set);
2664    malloc_string(pathname_of_last_biggie_restored);
2665    assert(bkpinfo != NULL);
2666
2667    read_cfg_var(g_mondo_cfg_file, "total-slices", tmp);
2668
2669    total_slices = atol(tmp);
2670    sprintf(tmp, "Reassembling large files      ");
2671    sprintf(xattr_fname, XATTR_BIGGLST_FNAME_RAW_SZ, bkpinfo->tmpdir);
2672    sprintf(acl_fname, ACL_BIGGLST_FNAME_RAW_SZ, bkpinfo->tmpdir);
2673    mvaddstr_and_log_it(g_currentY, 0, tmp);
2674    sprintf(biggies_whose_EXATs_we_should_set,
2675            "%s/biggies-whose-EXATs-we-should-set", bkpinfo->tmpdir);
2676    if (!(fbw = fopen(biggies_whose_EXATs_we_should_set, "w"))) {
2677        log_msg(1, "Warning - cannot openout %s",
2678                biggies_whose_EXATs_we_should_set);
2679    }
2680// get xattr and acl files if they're there
2681    res =
2682        read_header_block_from_stream(&biggie_size, biggie_fname,
2683                                      &ctrl_chr);
2684    if (ctrl_chr == BLK_START_EXTENDED_ATTRIBUTES) {
2685        res =
2686            read_EXAT_files_from_tape(bkpinfo, &biggie_size, biggie_fname,
2687                                      &ctrl_chr, xattr_fname, acl_fname);
2688    }
2689
2690    noof_biggiefiles = atol(biggie_fname);
2691    sprintf(tmp, "OK, there are %ld biggiefiles in the archives",
2692            noof_biggiefiles);
2693    log_msg(2, tmp);
2694    open_progress_form("Reassembling large files",
2695                       "I am now reassembling all the large files.",
2696                       "Please wait. This may take some time.",
2697                       "", total_slices);
2698
2699    for (res =
2700         read_header_block_from_stream(&biggie_size, biggie_fname,
2701                                       &ctrl_chr);
2702         ctrl_chr != BLK_STOP_BIGGIEFILES;
2703         res =
2704         read_header_block_from_stream(&biggie_size, biggie_fname,
2705                                       &ctrl_chr)) {
2706        if (ctrl_chr != BLK_START_A_NORMBIGGIE
2707            && ctrl_chr != BLK_START_A_PIHBIGGIE) {
2708            wrong_marker(BLK_START_A_NORMBIGGIE, ctrl_chr);
2709        }
2710        p = strrchr(biggie_fname, '/');
2711        if (!p) {
2712            p = biggie_fname;
2713        } else {
2714            p++;
2715        }
2716        sprintf(tmp, "Restoring big file %ld (%lld K)",
2717                current_bigfile_number + 1, biggie_size / 1024);
2718        update_progress_form(tmp);
2719        res = restore_a_biggiefile_from_stream(bkpinfo, biggie_fname,
2720                                               current_bigfile_number,
2721                                               biggie_cksum,
2722                                               biggie_size,
2723                                               filelist, ctrl_chr,
2724                                               pathname_of_last_biggie_restored);
2725        log_msg(1, "I believe I have restored %s",
2726                pathname_of_last_biggie_restored);
2727        if (fbw && pathname_of_last_biggie_restored[0]) {
2728            fprintf(fbw, "%s\n", pathname_of_last_biggie_restored);
2729        }
2730        retval += res;
2731        current_bigfile_number++;
2732
2733    }
2734    if (current_bigfile_number != noof_biggiefiles
2735        && noof_biggiefiles != 0) {
2736        sprintf(tmp, "Warning - bigfileno=%ld but noof_biggiefiles=%ld\n",
2737                current_bigfile_number, noof_biggiefiles);
2738    } else {
2739        sprintf(tmp,
2740                "%ld biggiefiles in biggielist.txt; %ld biggiefiles processed today.",
2741                noof_biggiefiles, current_bigfile_number);
2742    }
2743    log_msg(1, tmp);
2744
2745    if (fbw) {
2746        fclose(fbw);
2747        if (length_of_file(biggies_whose_EXATs_we_should_set) > 2) {
2748            iamhere("Setting biggie-EXATs");
2749            if (length_of_file(acl_fname) > 0) {
2750                log_msg(1, "set_acl_list(%s,%s)",
2751                        biggies_whose_EXATs_we_should_set, acl_fname);
2752                set_acl_list(biggies_whose_EXATs_we_should_set, acl_fname);
2753            }
2754            if (length_of_file(xattr_fname) > 0) {
2755                log_msg(1, "set_fattr_List(%s,%s)",
2756                        biggies_whose_EXATs_we_should_set, xattr_fname);
2757                set_fattr_list(biggies_whose_EXATs_we_should_set,
2758                               xattr_fname);
2759            }
2760        } else {
2761            iamhere
2762                ("No biggiefiles selected. So, no biggie-EXATs to set.");
2763        }
2764    }
2765    if (does_file_exist("/PAUSE")) {
2766        popup_and_OK
2767            ("Press ENTER to go on. Delete /PAUSE to stop these pauses.");
2768    }
2769
2770    close_progress_form();
2771    if (retval) {
2772        mvaddstr_and_log_it(g_currentY++, 74, "Errors.");
2773    } else {
2774        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
2775    }
2776    paranoid_free(biggies_whose_EXATs_we_should_set);
2777    paranoid_free(pathname_of_last_biggie_restored);
2778    paranoid_free(biggie_fname);
2779    paranoid_free(biggie_cksum);
2780    paranoid_free(xattr_fname);
2781    paranoid_free(acl_fname);
2782    paranoid_free(tmp);
2783    return (retval);
2784}
2785
2786/**************************************************************************
2787 *END_RESTORE_ALL_BIGGIEFILES_FROM_STREAM                                 *
2788 **************************************************************************/
2789
2790
2791
2792
2793
2794
2795/**
2796 * Restore all afioballs from the currently opened tape stream.
2797 * @param bkpinfo The backup information structure. Fields used:
2798 * - @c bkpinfo->backup_media_type
2799 * - @c bkpinfo->restore_path
2800 * @param filelist The node structure containing the list of files to be
2801 * restored. If no file in an afioball is in this list, afio will still be
2802 * called for that fileset, but nothing will be written.
2803 * @return 0 for success, or the number of filesets that failed.
2804 */
2805int
2806restore_all_tarballs_from_stream(struct s_bkpinfo *bkpinfo,
2807                                 struct s_node *filelist)
2808{
2809    int retval = 0;
2810    int res;
2811    long current_afioball_number = 0;
2812    int ctrl_chr;
2813    long max_val /*, total_noof_files */ ;
2814
2815  /** malloc **/
2816    char *tmp;
2817    char *progress_str;
2818    char *tmp_fname;
2819    char *xattr_fname;
2820    char *acl_fname;
2821
2822    long long tmp_size;
2823
2824    malloc_string(tmp);
2825    malloc_string(progress_str);
2826    malloc_string(tmp_fname);
2827    assert(bkpinfo != NULL);
2828    malloc_string(xattr_fname);
2829    malloc_string(acl_fname);
2830    mvaddstr_and_log_it(g_currentY, 0, "Restoring from archives");
2831    read_cfg_var(g_mondo_cfg_file, "last-filelist-number", tmp);
2832    max_val = atol(tmp) + 1;
2833
2834    chdir(bkpinfo->restore_path);   /* I don't know why this is needed _here_ but it seems to be. -HR, 02/04/2002 */
2835
2836    run_program_and_log_output("pwd", 5);
2837
2838    sprintf(progress_str, "Restoring from media #%d",
2839            g_current_media_number);
2840    log_to_screen(progress_str);
2841    open_progress_form("Restoring from archives",
2842                       "Restoring data from the archives.",
2843                       "Please wait. This may take some time.",
2844                       progress_str, max_val);
2845
2846    log_msg(3, "hey");
2847
2848    res = read_header_block_from_stream(&tmp_size, tmp_fname, &ctrl_chr);
2849    if (res) {
2850        log_msg(2, "Warning - error reading afioball from tape");
2851    }
2852    retval += res;
2853    if (ctrl_chr != BLK_START_AFIOBALLS) {
2854        wrong_marker(BLK_START_AFIOBALLS, ctrl_chr);
2855    }
2856    log_msg(2, "ho");
2857    res = read_header_block_from_stream(&tmp_size, tmp_fname, &ctrl_chr);
2858    while (ctrl_chr != BLK_STOP_AFIOBALLS) {
2859        update_progress_form(progress_str);
2860        sprintf(xattr_fname, "%s/xattr-subset-%ld.tmp", bkpinfo->tmpdir,
2861                current_afioball_number);
2862        sprintf(acl_fname, "%s/acl-subset-%ld.tmp", bkpinfo->tmpdir,
2863                current_afioball_number);
2864        unlink(xattr_fname);
2865        unlink(acl_fname);
2866        if (ctrl_chr == BLK_START_EXTENDED_ATTRIBUTES) {
2867            iamhere("Reading EXAT files from tape");
2868            res =
2869                read_EXAT_files_from_tape(bkpinfo, &tmp_size, tmp_fname,
2870                                          &ctrl_chr, xattr_fname,
2871                                          acl_fname);
2872        }
2873        if (ctrl_chr != BLK_START_AN_AFIO_OR_SLICE) {
2874            wrong_marker(BLK_START_AN_AFIO_OR_SLICE, ctrl_chr);
2875        }
2876        sprintf(tmp,
2877                "Restoring from fileset #%ld (name=%s, size=%ld K)",
2878                current_afioball_number, tmp_fname, (long) tmp_size >> 10);
2879        res =
2880            restore_a_tarball_from_stream(bkpinfo, tmp_fname,
2881                                          current_afioball_number,
2882                                          filelist, tmp_size, xattr_fname,
2883                                          acl_fname);
2884        retval += res;
2885        if (res) {
2886            sprintf(tmp, "Fileset %ld - errors occurred",
2887                    current_afioball_number);
2888            log_to_screen(tmp);
2889        }
2890        res =
2891            read_header_block_from_stream(&tmp_size, tmp_fname, &ctrl_chr);
2892        if (ctrl_chr != BLK_STOP_AN_AFIO_OR_SLICE) {
2893            wrong_marker(BLK_STOP_AN_AFIO_OR_SLICE, ctrl_chr);
2894        }
2895
2896        current_afioball_number++;
2897        g_current_progress++;
2898        sprintf(progress_str, "Restoring from fileset #%ld on %s #%d",
2899                current_afioball_number,
2900                media_descriptor_string(bkpinfo->backup_media_type),
2901                g_current_media_number);
2902        res =
2903            read_header_block_from_stream(&tmp_size, tmp_fname, &ctrl_chr);
2904        unlink(xattr_fname);
2905        unlink(acl_fname);
2906    }                           // next
2907    log_msg(1, "All done with afioballs");
2908    close_progress_form();
2909    if (retval) {
2910        mvaddstr_and_log_it(g_currentY++, 74, "Errors.");
2911    } else {
2912        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
2913    }
2914    paranoid_free(tmp);
2915    paranoid_free(progress_str);
2916    paranoid_free(tmp_fname);
2917    paranoid_free(xattr_fname);
2918    paranoid_free(acl_fname);
2919    return (retval);
2920}
2921
2922/**************************************************************************
2923 *END_ RESTORE_ALL_TARBALLS_FROM_STREAM                                   *
2924 **************************************************************************/
2925
2926/* @} - end of LLrestoreGroup */
2927
2928
2929/**
2930 * Restore all files in @p filelist.
2931 * @param bkpinfo The backup information structure. Most fields are used.
2932 * @param filelist The node structure containing the list of files to be
2933 * restored.
2934 * @return 0 for success, or the number of afioballs and biggiefiles that failed.
2935 * @ingroup restoreGroup
2936 */
2937int restore_everything(struct s_bkpinfo *bkpinfo, struct s_node *filelist)
2938{
2939    int resA;
2940    int resB;
2941
2942  /** mallco ***/
2943    char *cwd;
2944    char *newpath;
2945    char *tmp;
2946    assert(bkpinfo != NULL);
2947
2948    malloc_string(cwd);
2949    malloc_string(newpath);
2950    malloc_string(tmp);
2951    log_msg(2, "restore_everything() --- starting");
2952    g_current_media_number = 1;
2953    getcwd(cwd, MAX_STR_LEN - 1);
2954    sprintf(tmp, "mkdir -p %s", bkpinfo->restore_path);
2955    run_program_and_log_output(tmp, FALSE);
2956    log_msg(1, "Changing dir to %s", bkpinfo->restore_path);
2957    chdir(bkpinfo->restore_path);
2958    getcwd(newpath, MAX_STR_LEN - 1);
2959    log_msg(1, "path is now %s", newpath);
2960    log_msg(1, "restoring everything");
2961    if (!find_home_of_exe("petris") && !g_text_mode) {
2962        newtDrawRootText(0, g_noof_rows - 2,
2963                         "Press ALT-<left cursor> twice to play Petris :-) ");
2964        newtRefresh();
2965    }
2966    mvaddstr_and_log_it(g_currentY, 0, "Preparing to read your archives");
2967    if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
2968        mount_cdrom(bkpinfo);
2969        mvaddstr_and_log_it(g_currentY++, 0,
2970                            "Restoring OS and data from streaming media");
2971        if (bkpinfo->backup_media_type == cdstream) {
2972            openin_cdstream(bkpinfo);
2973        } else {
2974            assert_string_is_neither_NULL_nor_zerolength(bkpinfo->
2975                                                         media_device);
2976            openin_tape(bkpinfo);
2977        }
2978        resA = restore_all_tarballs_from_stream(bkpinfo, filelist);
2979        resB = restore_all_biggiefiles_from_stream(bkpinfo, filelist);
2980        if (bkpinfo->backup_media_type == cdstream) {
2981            closein_cdstream(bkpinfo);
2982        } else {
2983            closein_tape(bkpinfo);
2984        }
2985    } else {
2986        mvaddstr_and_log_it(g_currentY++, 0,
2987                            "Restoring OS and data from CD       ");
2988        mount_cdrom(bkpinfo);
2989        resA = restore_all_tarballs_from_CD(bkpinfo, filelist);
2990        resB = restore_all_biggiefiles_from_CD(bkpinfo, filelist);
2991    }
2992    chdir(cwd);
2993    if (resA + resB) {
2994        log_to_screen("Errors occurred while data was being restored.");
2995    }
2996    if (length_of_file("/etc/raidtab") > 0) {
2997        log_msg(2, "Copying local raidtab to restored filesystem");
2998        run_program_and_log_output("cp -f /etc/raidtab " MNT_RESTORING
2999                                   "/etc/raidtab", FALSE);
3000    }
3001    kill_petris();
3002    log_msg(2, "restore_everything() --- leaving");
3003    paranoid_free(cwd);
3004    paranoid_free(newpath);
3005    paranoid_free(tmp);
3006    return (resA + resB);
3007}
3008
3009/**************************************************************************
3010 *END_RESTORE_EVERYTHING                                                  *
3011 **************************************************************************/
3012
3013
3014
3015/**
3016 * @brief Haha. You wish! (This function is not implemented :-)
3017 */
3018int
3019restore_live_from_monitas_server(struct s_bkpinfo *bkpinfo,
3020                                 char *monitas_device,
3021                                 char *restore_this_directory,
3022                                 char *restore_here)
3023     /* NB: bkpinfo hasn't been populated yet, except for ->tmp which is "/tmp" */
3024{
3025    FILE *fout;
3026    int retval = 0;
3027    int i;
3028    int j;
3029    struct mountlist_itself the_mountlist;
3030    static struct raidlist_itself the_raidlist;
3031  /** malloc **/
3032    char tmp[MAX_STR_LEN + 1];
3033    char command[MAX_STR_LEN + 1];
3034    char datablock[256 * 1024];
3035    char datadisks_fname[MAX_STR_LEN + 1];
3036    long k;
3037    long length;
3038    long long llt;
3039    struct s_node *filelist = NULL;
3040    assert(bkpinfo != NULL);
3041    assert_string_is_neither_NULL_nor_zerolength(monitas_device);
3042    assert(restore_this_directory != NULL);
3043    assert(restore_here != NULL);
3044
3045    sprintf(tmp, "restore_here = '%s'", restore_here);
3046
3047    log_msg(2, tmp);
3048
3049    log_msg(2, "restore_live_from_monitas_server() - starting");
3050    unlink("/tmp/mountlist.txt");
3051    unlink("/tmp/filelist.full");
3052    unlink("/tmp/biggielist.txt");
3053    if (restore_here[0] == '\0') {
3054        strcpy(bkpinfo->restore_path, MNT_RESTORING);
3055    } else {
3056        strcpy(bkpinfo->restore_path, restore_here);
3057    }
3058    log_msg(3, "FYI FYI FYI FYI FYI FYI FYI FYI FYI FYI FYI");
3059    sprintf(tmp, "FYI - data will be restored to %s",
3060            bkpinfo->restore_path);
3061    log_msg(3, tmp);
3062    log_msg(3, "FYI FYI FYI FYI FYI FYI FYI FYI FYI FYI FYI");
3063    sprintf(datadisks_fname, "/tmp/mondorestore.datadisks.%d",
3064            (int) (random() % 32768));
3065    chdir(bkpinfo->tmpdir);
3066
3067    sprintf(command, "cat %s", monitas_device);
3068    g_tape_stream = popen(command, "r");    // for compatibility with openin_tape()
3069    if (!(fout = fopen(datadisks_fname, "w"))) {
3070        log_OS_error(datadisks_fname);
3071        return (1);
3072    }
3073    for (i = 0; i < 32; i++) {
3074        for (j = 0; j < 4; j++) {
3075            for (length = k = 0; length < 256 * 1024; length += k) {
3076                k = fread(datablock + length, 1, 256 * 1024 - length,
3077                          g_tape_stream);
3078            }
3079            fwrite(datablock, 1, length, fout);
3080            g_tape_posK += length;
3081        }
3082    }
3083    paranoid_fclose(fout);
3084    sprintf(command,
3085            "tar -zxvf %s tmp/mondo-restore.cfg tmp/mountlist.txt tmp/filelist.full tmp/biggielist.txt",
3086            datadisks_fname);
3087    run_program_and_log_output(command, 4);
3088    read_header_block_from_stream(&llt, tmp, &i);
3089    read_header_block_from_stream(&llt, tmp, &i);
3090
3091    unlink(datadisks_fname);
3092    read_cfg_file_into_bkpinfo(g_mondo_cfg_file, bkpinfo);
3093    retval = load_mountlist(&the_mountlist, g_mountlist_fname); // in case read_cfg_file_into_bkpinfo   strcpy(bkpinfo->media_device, monitas_device);
3094
3095
3096    load_raidtab_into_raidlist(&the_raidlist, RAIDTAB_FNAME);
3097    iamhere("FIXME");
3098    fatal_error("This will fail");
3099    sprintf(command,
3100            "grep -x \"%s.*\" %s > %s",
3101            restore_this_directory, g_filelist_full, g_filelist_full);
3102    if (system(command)) {
3103        retval++;
3104        log_to_screen
3105            ("Error(s) occurred while processing filelist and wildcard");
3106    }
3107    iamhere("FIXME");
3108    fatal_error("This will fail");
3109    sprintf(command,
3110            "grep -x \"%s.*\" %s > %s",
3111            restore_this_directory, g_biggielist_txt, g_biggielist_txt);
3112    if (system(command)) {
3113        log_msg(1,
3114                "Error(s) occurred while processing biggielist and wildcard");
3115    }
3116    sprintf(command, "touch %s", g_biggielist_txt);
3117    run_program_and_log_output(command, FALSE);
3118//  filelist = load_filelist(g_filelist_restthese);  // FIXME --- this probably doesn't work because it doesn't include the biggiefiles
3119    retval += restore_everything(bkpinfo, filelist);
3120    free_filelist(filelist);
3121    log_msg(2, "--------End of restore_live_from_monitas_server--------");
3122    return (retval);
3123}
3124
3125/**************************************************************************
3126 *END_RESTORE_LIVE_FROM_MONITAS_SERVER                                    *
3127 **************************************************************************/
3128
3129
3130
3131
3132extern void wait_until_software_raids_are_prepped(char *, int);
3133
3134
3135char which_restore_mode(void);
3136
3137
3138/**
3139 * Log a "don't panic" message to the logfile.
3140 */
3141void welcome_to_mondorestore()
3142{
3143    log_msg(0, "-------------- Mondo Restore v%s -------------", PACKAGE_VERSION);
3144    log_msg(0,
3145            "DON'T PANIC! Mondorestore logs almost everything, so please ");
3146    log_msg(0,
3147            "don't break out in a cold sweat just because you see a few  ");
3148    log_msg(0,
3149            "error messages in the log. Read them; analyze them; see if  ");
3150    log_msg(0,
3151            "they are significant; above all, verify your backups! Please");
3152    log_msg(0,
3153            "attach a compressed copy of this log to any e-mail you send ");
3154    log_msg(0,
3155            "to the Mondo mailing list when you are seeking technical    ");
3156    log_msg(0,
3157            "support. Without it, we can't help you.               - Hugo");
3158    log_msg(0,
3159            "------------------------------------------------------------");
3160    log_msg(0,
3161            "BTW, despite (or perhaps because of) the wealth of messages,");
3162    log_msg(0,
3163            "some users are inclined to stop reading this log.  If Mondo ");
3164    log_msg(0,
3165            "stopped for some reason, chances are it's detailed here.    ");
3166    log_msg(0,
3167            "More than likely there's a message at the very end of this  ");
3168    log_msg(0,
3169            "log that will tell you what is wrong.  Please read it!      ");
3170    log_msg(0,
3171            "------------------------------------------------------------");
3172}
3173
3174
3175
3176/**
3177 * Restore the user's data.
3178 * What did you think it did, anyway? :-)
3179 */
3180int main(int argc, char *argv[])
3181{
3182    FILE *fin;
3183    FILE *fout;
3184    int retval = 0;
3185    int res;
3186//  int c;
3187    char *tmp;
3188
3189    struct mountlist_itself *mountlist;
3190    struct raidlist_itself *raidlist;
3191    struct s_bkpinfo *bkpinfo;
3192    struct s_node *filelist;
3193    char *a, *b;
3194
3195  /**************************************************************************
3196   * hugo-                                                                  *
3197   * busy stuff here - it needs some comments -stan                           *
3198   *                                                                        *
3199   **************************************************************************/
3200    if (getuid() != 0) {
3201        fprintf(stderr, "Please run as root.\r\n");
3202        exit(127);
3203    }
3204
3205    g_loglevel = DEFAULT_MR_LOGLEVEL;
3206    malloc_string(tmp);
3207
3208/* Configure global variables */
3209#ifdef __FreeBSD__
3210    if (strstr
3211        (call_program_and_get_last_line_of_output("cat /tmp/cmdline"),
3212         "textonly"))
3213#else
3214    if (strstr
3215        (call_program_and_get_last_line_of_output("cat /proc/cmdline"),
3216         "textonly"))
3217#endif
3218    {
3219        g_text_mode = TRUE;
3220        log_msg(1, "TEXTONLY MODE");
3221    } else {
3222        g_text_mode = FALSE;
3223    }                           // newt :-)
3224    if (!
3225        (bkpinfo = g_bkpinfo_DONTUSETHIS =
3226         malloc(sizeof(struct s_bkpinfo)))) {
3227        fatal_error("Cannot malloc bkpinfo");
3228    }
3229    if (!(mountlist = malloc(sizeof(struct mountlist_itself)))) {
3230        fatal_error("Cannot malloc mountlist");
3231    }
3232    if (!(raidlist = malloc(sizeof(struct raidlist_itself)))) {
3233        fatal_error("Cannot malloc raidlist");
3234    }
3235
3236    malloc_libmondo_global_strings();
3237
3238    strcpy(g_mondo_home,
3239           call_program_and_get_last_line_of_output("which mondorestore"));
3240    sprintf(g_tmpfs_mountpt, "/tmp/tmpfs");
3241    make_hole_for_dir(g_tmpfs_mountpt);
3242    g_current_media_number = 1; // precaution
3243
3244    run_program_and_log_output("mkdir -p " MNT_CDROM, FALSE);
3245    run_program_and_log_output("mkdir -p /mnt/floppy", FALSE);
3246
3247    malloc_string(tmp);
3248    malloc_string(a);
3249    malloc_string(b);
3250    setup_MR_global_filenames(bkpinfo); // malloc() and set globals, using bkpinfo->tmpdir etc.
3251    reset_bkpinfo(bkpinfo);
3252    bkpinfo->backup_media_type = none;  // in case boot disk was made for one backup type but user wants to restore from another backup type
3253    bkpinfo->restore_data = TRUE;   // Well, yeah :-)
3254    if (am_I_in_disaster_recovery_mode()) {
3255        run_program_and_log_output("mount / -o remount,rw", 2);
3256    }                           // for b0rken distros
3257    g_main_pid = getpid();
3258    srandom((int) (time(NULL)));
3259    register_pid(getpid(), "mondo");
3260    set_signals(TRUE);
3261    g_kernel_version = get_kernel_version();
3262
3263    log_msg(1, "FYI - g_mountlist_fname = %s", g_mountlist_fname);
3264    if (strlen(g_mountlist_fname) < 3) {
3265        fatal_error
3266            ("Serious error in malloc()'ing. Could be a bug in your glibc.");
3267    }
3268    mkdir(MNT_CDROM, 0x770);
3269
3270/* Backup original mountlist.txt */
3271    sprintf(tmp, "%s.orig", g_mountlist_fname);
3272    if (!does_file_exist(g_mountlist_fname)) {
3273        log_msg(2,
3274                "%ld: Warning - g_mountlist_fname (%s) does not exist yet",
3275                __LINE__, g_mountlist_fname);
3276    } else if (!does_file_exist(tmp)) {
3277        sprintf(tmp, "cp -f %s %s.orig", g_mountlist_fname,
3278                g_mountlist_fname);
3279        run_program_and_log_output(tmp, FALSE);
3280    }
3281
3282/* Init directories */
3283    make_hole_for_dir(bkpinfo->tmpdir);
3284    sprintf(tmp, "mkdir -p %s", bkpinfo->tmpdir);
3285    run_program_and_log_output(tmp, FALSE);
3286    make_hole_for_dir("/var/log");
3287    make_hole_for_dir("/tmp/tmpfs");    /* just in case... */
3288    run_program_and_log_output("umount " MNT_CDROM, FALSE);
3289    run_program_and_log_output
3290        ("ln -sf /var/log/mondo-archive.log /tmp/mondo-restore.log",
3291         FALSE);
3292
3293    run_program_and_log_output("rm -Rf /tmp/tmpfs/mondo.tmp.*", FALSE);
3294
3295/* Init GUI */
3296    malloc_libmondo_global_strings();
3297    setup_newt_stuff();         /* call newtInit and setup screen log */
3298    welcome_to_mondorestore();
3299    if (bkpinfo->disaster_recovery) {
3300        log_msg(1, "I am in disaster recovery mode");
3301    } else {
3302        log_msg(1, "I am in normal, live mode");
3303    }
3304
3305    iamhere("what time is it");
3306
3307/* Process command-line parameters */
3308    if (argc == 2 && strcmp(argv[1], "--edit-mountlist") == 0) {
3309#ifdef __FreeBSD__
3310        system("mv -f /tmp/raidconf.txt /etc/raidtab");
3311        if (!does_file_exist("/etc/raidtab"))
3312            system("vinum printconfig > /etc/raidtab");
3313#endif
3314        load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME);
3315        if (!does_file_exist(g_mountlist_fname)) {
3316            strcpy(g_mountlist_fname, "/tmp/mountlist.txt");
3317        }
3318        res = let_user_edit_the_mountlist(bkpinfo, mountlist, raidlist);
3319#ifdef __FreeBSD__
3320        system("mv -f /etc/raidtab /tmp/raidconf.txt");
3321#endif
3322        paranoid_MR_finish(res);
3323    }
3324
3325    g_loglevel = DEFAULT_MR_LOGLEVEL;
3326    if (argc == 3 && strcmp(argv[1], "--echo-to-screen") == 0) {
3327        fout = fopen("/tmp/out.txt", "w");
3328        fput_string_one_char_at_a_time(stderr, argv[2]);
3329        finish(0);
3330    }
3331
3332    if (argc == 3 && strcmp(argv[1], "--gendf") == 0) {
3333        make_grub_install_scriptlet(argv[2]);
3334        finish(0);
3335    }
3336
3337    if (argc >= 2 && strcmp(argv[1], "--pih") == 0) {
3338        if (system("mount | grep cdrom 2> /dev/null > /dev/null")) {
3339            system("mount " MNT_CDROM);
3340        }
3341        bkpinfo->compression_level = 1;
3342        g_current_media_number = 2;
3343        strcpy(bkpinfo->restore_path, "/tmp/TESTING");
3344        bkpinfo->backup_media_type = dvd;
3345        open_progress_form("Reassembling /dev/hda1",
3346                           "Shark is a bit of a silly person.",
3347                           "Please wait. This may take some time.",
3348                           "", 1999);
3349        system("rm -Rf /tmp/*pih*");
3350
3351        restore_a_biggiefile_from_CD(bkpinfo, 42, NULL, tmp);
3352    }
3353
3354    if (argc == 5 && strcmp(argv[1], "--common") == 0) {
3355        g_loglevel = 6;
3356        filelist = load_filelist(argv[2]);
3357        if (!filelist) {
3358            fatal_error("Failed to load filelist");
3359        }
3360        toggle_node_selection(filelist, FALSE);
3361        toggle_all_root_dirs_on(filelist);
3362        // BERLIOS: /usr/lib ???
3363        toggle_path_selection(filelist, "/usr/share", TRUE);
3364//      show_filelist(filelist);
3365        save_filelist(filelist, "/tmp/out.txt");
3366//      finish(0);
3367//      toggle_path_selection (filelist, "/root/stuff", TRUE);
3368        strcpy(a, argv[3]);
3369        strcpy(b, argv[4]);
3370
3371        res = save_filelist_entries_in_common(a, filelist, b, FALSE);
3372        free_filelist(filelist);
3373        printf("res = %d", res);
3374        finish(0);
3375    }
3376
3377    if (argc == 3 && strcmp(argv[1], "--popuplist") == 0) {
3378        popup_changelist_from_file(argv[2]);
3379        paranoid_MR_finish(0);
3380    }
3381
3382    if (argc == 5 && strcmp(argv[1], "--copy") == 0) {
3383        log_msg(1, "SCORE");
3384        g_loglevel = 10;
3385        if (strstr(argv[2], "save")) {
3386            log_msg(1, "Saving from %s to %s", argv[3], argv[4]);
3387            fin = fopen(argv[3], "r");
3388            fout = fopen(argv[4], "w");
3389            copy_from_src_to_dest(fin, fout, 'w');
3390            fclose(fin);
3391            fin = fopen(argv[3], "r");
3392            copy_from_src_to_dest(fin, fout, 'w');
3393            fclose(fout);
3394            fclose(fin);
3395        } else if (strstr(argv[2], "restore")) {
3396            fout = fopen(argv[3], "w");
3397            fin = fopen(argv[4], "r");
3398            copy_from_src_to_dest(fout, fin, 'r');
3399            fclose(fin);
3400            fin = fopen(argv[4], "r");
3401            copy_from_src_to_dest(fout, fin, 'r');
3402            fclose(fout);
3403            fclose(fin);
3404        } else {
3405            fatal_error("Unknown additional param");
3406        }
3407        finish(0);
3408    }
3409
3410    if (argc == 3 && strcmp(argv[1], "--mdstat") == 0) {
3411        wait_until_software_raids_are_prepped(argv[2], 100);
3412        finish(0);
3413    }
3414
3415    if (argc == 4 && strcmp(argv[1], "--mdconv") == 0) {
3416        finish(create_raidtab_from_mdstat(argv[2], argv[3]));
3417    }
3418
3419
3420    if (argc == 2 && strcmp(argv[1], "--live-grub") == 0) {
3421        retval = run_grub(FALSE, "/dev/hda");
3422        if (retval) {
3423            log_to_screen("Failed to write Master Boot Record");
3424        }
3425        paranoid_MR_finish(0);
3426    }
3427    if (argc == 3 && strcmp(argv[1], "--paa") == 0) {
3428        g_current_media_number = atoi(argv[2]);
3429        pause_and_ask_for_cdr(5, NULL);
3430        paranoid_MR_finish(0);
3431    } else if (!bkpinfo->disaster_recovery) {   // live!
3432        if (argc != 1) {
3433            popup_and_OK
3434                ("Live mode doesn't support command-line parameters yet.");
3435            paranoid_MR_finish(1);
3436//    return(1);
3437        }
3438        log_msg(1, "I am in normal, live mode.");
3439        log_msg(2, "FYI, MOUNTLIST_FNAME = %s", g_mountlist_fname);
3440        mount_boot_if_necessary();  /* for Gentoo users */
3441        log_msg(2, "Still here.");
3442        if (argc > 1 && strcmp(argv[argc - 1], "--live-from-cd") == 0) {
3443            g_restoring_live_from_cd = TRUE;
3444        }
3445        if (argc == 5 && strcmp(argv[1], "--monitas-live") == 0) {
3446            retval =
3447                restore_live_from_monitas_server(bkpinfo,
3448                                                 argv[2],
3449                                                 argv[3], argv[4]);
3450        } else {
3451            log_msg(2, "Calling restore_to_live_filesystem()");
3452            retval = restore_to_live_filesystem(bkpinfo);
3453        }
3454        log_msg(2, "Still here. Yay.");
3455        if (strlen(bkpinfo->tmpdir) > 0) {
3456            sprintf(tmp, "rm -Rf %s/*", bkpinfo->tmpdir);
3457            run_program_and_log_output(tmp, FALSE);
3458        }
3459        unmount_boot_if_necessary();    /* for Gentoo users */
3460        paranoid_MR_finish(retval);
3461    } else {
3462/* Disaster recovery mode (must be) */
3463        log_msg(1, "I must be in disaster recovery mode.");
3464        log_msg(2, "FYI, MOUNTLIST_FNAME = %s ", g_mountlist_fname);
3465        if (argc == 3 && strcmp(argv[1], "--monitas-memorex") == 0) {
3466            log_to_screen("Uh, that hasn't been implemented yet.");
3467            paranoid_MR_finish(1);
3468        }
3469
3470        iamhere("About to call load_mountlist and load_raidtab");
3471        strcpy(bkpinfo->restore_path, MNT_RESTORING);
3472        read_cfg_file_into_bkpinfo(g_mondo_cfg_file, bkpinfo);
3473        retval = load_mountlist(mountlist, g_mountlist_fname);
3474        retval += load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME);
3475        iamhere
3476            ("Returned from calling load_mountlist and load_raidtab successfully");
3477
3478        if (argc > 1
3479            && (strcmp(argv[1], "--compare") == 0
3480                || strcmp(argv[1], "--nuke") == 0)) {
3481            if (bkpinfo->backup_media_type == nfs
3482                && !is_this_device_mounted(bkpinfo->nfs_mount)) {
3483                log_msg(1, "Mounting nfs dir");
3484                sprintf(bkpinfo->isodir, "/tmp/isodir");
3485                run_program_and_log_output("mkdir -p /tmp/isodir", 5);
3486                sprintf(tmp, "mount %s -t nfs -o nolock /tmp/isodir",
3487                        bkpinfo->nfs_mount);
3488                run_program_and_log_output(tmp, 1);
3489            }
3490        }
3491
3492
3493        if (retval) {
3494            log_to_screen
3495                ("Warning - load_raidtab_into_raidlist returned an error");
3496        }
3497
3498
3499        log_msg(1, "Send in the clowns.");
3500
3501        if (argc == 2 && strcmp(argv[1], "--partition-only") == 0) {
3502            log_msg(0, "Partitioning only.");
3503            load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME);
3504            strcpy(g_mountlist_fname, "/tmp/mountlist.txt");
3505            load_mountlist(mountlist, g_mountlist_fname);
3506            res = partition_everything(mountlist);
3507            finish(res);
3508        }
3509
3510        if (argc == 2 && strcmp(argv[1], "--format-only") == 0) {
3511            log_msg(0, "Formatting only.");
3512            load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME);
3513            strcpy(g_mountlist_fname, "/tmp/mountlist.txt");
3514            load_mountlist(mountlist, g_mountlist_fname);
3515            res = format_everything(mountlist, FALSE);
3516            finish(res);
3517        }
3518
3519        if (argc == 2 && strcmp(argv[1], "--stop-lvm-and-raid") == 0) {
3520            log_msg(0, "Stopping LVM and RAID");
3521            load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME);
3522            strcpy(g_mountlist_fname, "/tmp/mountlist.txt");
3523            load_mountlist(mountlist, g_mountlist_fname);
3524            res = do_my_funky_lvm_stuff(TRUE, FALSE);
3525            res += stop_all_raid_devices(mountlist);
3526            finish(res);
3527        }
3528
3529        if (argc == 2 && strcmp(argv[1], "--nuke") == 0) {
3530            iamhere("nuking");
3531            retval += nuke_mode(bkpinfo, mountlist, raidlist);
3532        } else if (argc == 2 && strcmp(argv[1], "--interactive") == 0) {
3533            iamhere("catchall");
3534            retval += catchall_mode(bkpinfo, mountlist, raidlist);
3535        } else if (argc == 2 && strcmp(argv[1], "--compare") == 0) {
3536            iamhere("compare");
3537            retval += compare_mode(bkpinfo, mountlist, raidlist);
3538        } else if (argc == 2 && strcmp(argv[1], "--iso") == 0) {
3539            iamhere("iso");
3540            retval = iso_mode(bkpinfo, mountlist, raidlist, FALSE);
3541        } else if (argc == 2 && strcmp(argv[1], "--mbr") == 0) {
3542            iamhere("mbr");
3543            retval = mount_all_devices(mountlist, TRUE);
3544            if (!retval) {
3545                retval += run_boot_loader(FALSE);
3546                retval += unmount_all_devices(mountlist);
3547            }
3548            if (retval) {
3549                log_to_screen("Failed to write Master Boot Record");
3550            }
3551        } else if (argc == 2 && strcmp(argv[1], "--isonuke") == 0) {
3552            iamhere("isonuke");
3553            retval = iso_mode(bkpinfo, mountlist, raidlist, TRUE);
3554        } else if (argc != 1) {
3555            log_to_screen("Invalid paremeters");
3556            paranoid_MR_finish(1);
3557        } else {
3558            iamhere("catchall (no mode specified in command-line call");
3559            retval += catchall_mode(bkpinfo, mountlist, raidlist);
3560        }
3561    }
3562
3563    /* clean up at the end */
3564    if (retval) {
3565        if (does_file_exist("/tmp/changed.files")) {
3566            log_to_screen
3567                ("See /tmp/changed.files for list of files that have changed.");
3568        }
3569        mvaddstr_and_log_it(g_currentY++,
3570                            0,
3571                            "Run complete. Errors were reported. Please review the logfile.");
3572    } else {
3573        if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) {
3574            mvaddstr_and_log_it(g_currentY++,
3575                                0,
3576                                "Run complete. Please remove floppy/CD/media and reboot.");
3577        } else {
3578            run_program_and_log_output("sync", FALSE);
3579            if (is_this_device_mounted(MNT_CDROM)) {
3580                res =
3581                    run_program_and_log_output("umount " MNT_CDROM, FALSE);
3582            } else {
3583                res = 0;
3584            }
3585
3586            if (!bkpinfo->please_dont_eject) {
3587                res = eject_device("/dev/cdrom");
3588/*
3589              if (res)
3590        {
3591          log_to_screen( "WARNING - failed to eject CD-ROM disk" );
3592        }
3593*/
3594            }
3595            mvaddstr_and_log_it(g_currentY++,
3596                                0,
3597                                "Run complete. Please remove media and reboot.");
3598        }
3599    }
3600
3601// g_I_have_just_nuked is set true by nuke_mode() just before it returns
3602    if (g_I_have_just_nuked || does_file_exist("/POST-NUKE-ANYWAY")) {
3603        if (!system("which post-nuke > /dev/null 2> /dev/null")) {
3604            log_msg(1, "post-nuke found; running...");
3605            if (mount_all_devices(mountlist, TRUE)) {
3606                log_to_screen
3607                    ("Unable to re-mount partitions for post-nuke stuff");
3608            } else {
3609                log_msg(1, "Re-mounted partitions for post-nuke stuff");
3610                sprintf(tmp, "post-nuke %s %d", bkpinfo->restore_path,
3611                        retval);
3612                if (!g_text_mode) {
3613                    newtSuspend();
3614                }
3615                log_msg(2, "Calling '%s'", tmp);
3616                if ((res = system(tmp))) {
3617                    log_OS_error(tmp);
3618                }
3619                if (!g_text_mode) {
3620                    newtResume();
3621                }
3622//              newtCls();
3623                log_msg(1, "post-nuke returned w/ res=%d", res);
3624            }
3625            unmount_all_devices(mountlist);
3626            log_msg(1, "I've finished post-nuking.");
3627        }
3628    }
3629/* 
3630  log_to_screen("If you are REALLY in a hurry, hit Ctrl-Alt-Del now.");
3631  log_to_screen("Otherwise, please wait until the RAID disks are done.");
3632  wait_until_software_raids_are_prepped("/proc/mdstat", 100);
3633  log_to_screen("Thank you.");
3634*/
3635    unlink("/tmp/mondo-run-prog.tmp");
3636    set_signals(FALSE);
3637    sprintf(tmp, "rm -Rf %s", bkpinfo->tmpdir);
3638    run_program_and_log_output(tmp, FALSE);
3639    log_to_screen
3640        ("Restore log copied to /tmp/mondo-restore.log on your hard disk");
3641    sprintf(tmp,
3642            "Mondo-restore is exiting (retval=%d)                                      ",
3643            retval);
3644    log_to_screen(tmp);
3645    sprintf(tmp, "umount %s", bkpinfo->isodir);
3646    run_program_and_log_output(tmp, 5);
3647    paranoid_free(mountlist);
3648    paranoid_free(raidlist);
3649    if (am_I_in_disaster_recovery_mode()) {
3650        run_program_and_log_output("mount / -o remount,rw", 2);
3651    }                           // for b0rken distros
3652    paranoid_MR_finish(retval); // frees global stuff plus bkpinfo
3653    free_libmondo_global_strings(); // it's fine to have this here :) really :)
3654    paranoid_free(a);
3655    paranoid_free(b);
3656    paranoid_free(tmp);
3657
3658    unlink("/tmp/filelist.full");
3659    unlink("/tmp/filelist.full.gz");
3660
3661    exit(retval);
3662}
3663
3664/**************************************************************************
3665 *END_MAIN                                                                *
3666 **************************************************************************/
3667
3668
3669
3670
3671
3672/**************************************************************************
3673 *END_MONDO-RESTORE.C                                                     *
3674 **************************************************************************/
Note: See TracBrowser for help on using the repository browser.