/*************************************************************************** mondo-restore.c - restores mondoarchive data ------------------- begin : Fri May 19 2000 copyright : (C) 2000 by Hugo Rabson email : Hugo Rabson cvsid : $Id: mondo-restore.c 94 2005-10-28 23:08:39Z bcornec $ ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ /*************************************************************************** * Change Log * *************************************************************************** . 07/26 - workaround - if not /dev then don't call partimagehack 07/21 - if switch to Interactive Mode during nuke then don't nuke twice :) just once 07/18 - better support of users who boot from LVM CD and nuke-restore non-LVM backups 07/10 - when using 'star', exclude all 'not removed.' messages from log (misleading, they are) - try w/ ACLs, then w/o ACLs if star fails w/ ACLs - added ACL, xattr support for afio 06/26 - remove make_relevant_partition_bootable(); roll into mondo-prep.c - changed various newtDrawRootText() calls to use g_noof_rows instead of integers 06/19 - don't try to access biggiestruct before it is populated w/ data relating to biggiefile; instead, use control char to find out if biggiefile is to be restored w/ partimagehack or not - added AUX_VER 06/15 - read_cfg_file_into_bkpinfo() --- use user-supplied 'isodir' instead of archive's 'isodir' if different Conor Daly 06/17 - restore with partimagehack if NTFS device; dd, if non-NTFS device 06/14 - unmount all; remount, run grub-mr; unmount again 04/09 - fixed subset restoration bug introduced when I added 'star' support 04/04 - cleaned up restore_a_tarball_from_stream() 04/03 - added star support - pause for 3s between partitioning and formatting (if in Nuke Mode) 03/28 - check that g_mountlist_fname was correcfly set; abort if it wasn't 03/25 - removed dev_null code 03/22/04 - added mode_of_file() - added code to check for unauthorised modification of /dev/null by afio (for instance) 12/27/03 - check /tmp/cmdline instead of /proc/cmdline if on FreeBSD 11/15 - added g_fprep - "Switch to interactive mode?" msg is now more informative 11/05 - after 'Are you sure?' when user specifies / as restore path, set restore_path[] to "" so that files are restored to [blank]/file/name :) 10/29 - moved "No restoring or comparing will take place today" block up to before iso_fiddly_bits (if iso) is called - fix bug if you're in ISO Mode and you say "exit to shell" 10/22 - swapped calls to chmod() and chown() after restoration of big files (Jens) 10/21 - changed "/mnt/cdrom" to MNT_CDROM 10/19 - restore biggiefiles selectively from CD properly - use find_my_editor() to find pico/vi/whatever - better use of call_program_and_get_last_line_of_output() to scan /proc/cmdline 10/18 - don't report 'missing compressor' if no compressor used at all 10/14 - log afio's error messages to /var/log/mondo-archive.log when restoring :) - call vi if pico is not available 10/09 - better logging if fatal error (cannot openout bigfile) - better GUI feedback when restoring big files - restore_everything() now uses s_node* instead of char* - ditto restore_all_*_from_*() 10/02 - succinct msg instead of pop-ups, if used -H 09/27 - tweaked restore-time gui 09/26 - proper reporting of DVD/CDR/etc. type in displayed dialogs 09/23 - malloc/free global strings in new subroutines - malloc_libmondo_global_strings() and free_libmondo_global_strings() - which are in libmondo-tools.c 09/21 - trying to fix "mondorestore " segfault 09/18 - better remounting of / - cleaned up run_grub() - sensible command-line handling in Live Mode 09/17 - cleaned up GRUB installer script a bit 09/15 - remount / as r/w if in disaster recovery mode; helps for b0rken distros 09/13 - major NTFS hackage 09/12 - changed some in-sub var decl'ns to malloc()'s 09/05 - don't let me run unless I'm root - finished NTFS/partimagehack support (CD only); working on tape now 09/01 - fixed cosmetic bug in biggiefile restore window 06/01 - 08/31 - added code to main() to make sure NFS (backup) share is mounted in Nuke and Compare Modes - added code to run_grub() to mount /boot before running grub-install - fixed some stray assert()'s in restore_a_biggiefile_from_stream() - fixed bugs in extract_config_file_from_ramdisk() and get_cfg_file_from_archive() which stopped fape+floppy users from being able to boot from floppy and leave floppy in drive :) - added hooks to partimage for doing imagedevs - fixed calls to popup_and_get_string() 05/01 - 05/31 - fixed biggiefile atime/utime dates restoration bug, I think - added misc clean-up (Steve Hindle) - fixed support for subdir-within-NFS-mount - if nuke mode fails & user reverts to interactive mode _and succeeds_, don't claim nuke mode aborted :) - unmount_all_devices() uses mountlist->el[lino].mountpt instead of mountlist->el[lino].device where possible - added Joshua Oreman's FreeBSD patches - copied missing paragraph from 1.6x's read_cfg_file_into_bkpinfo() to 1.7x's; affected tape streamer users (badly!) - cleaned up some paranoid assert()'s - use which("post-nuke") instead of find_home_of_exe("post-nuke") - fixed "Don't eject while restoring" mode - get_cfg_file_from_archive() --- also recovers mountlist.txt now :) - don't eject if 'donteject' is in kernel's command line - added 'don't panic' msg to start of log 04/01 - 04/30 - added text mode (cat /proc/cmdline; if textonly then text mode is on) - delete /var/lock/subsys/ * when nuking - don't resize mountlist if "noresize" present in /proc/cmdline - changed from chmod -R 1777 tmp to chmod 1777 tmp - replace newtFinished() and newtInit() with newtSuspend() and newtResume() - get_cfg_file_from_archive() returns 1 instead of aborting now - read_cfg_file_into_bkpinfo) --- if autorun CD but its config file indicates a non-CD backup media then ask, just in case - sped up restore_a_tarball_from_CD() a bit - line 4469 --- if post-nuke not found then don't run it :) - replaced "/mnt/RESTORING" with MNT_RESTORING (#define'd) - moved compare_*() into mondorestore/mondo-rstr-compare.c - moved some RAID subroutines into common/libmondo-raid.c - fixed some iso live compare problems - replaced FILELIST_FULL with g_filelist_full and FILELIST_FULL_STUB; g_filelist_full being the full path of the filelist.full text file and FILELIST_FULL_STUB being "tmp/filelist.full" (relative path); - ditto BIGGIELIST_TXT, MONDO_CFG_FILE - added lots of assert()'s and log_OS_error()'s - in Nuke Mode, check mountlist's sanity before doing anything else; if it fails sanity test, offer to revert to Interactive Mode (or abort) - copy log to /mnt/RESTORING/root at end - read_cfg_file_into_bkpinfo() --- read iso-dev and isodir if bkptype==iso - line 1701 --- delete ramdisk file after extracting config info - moved call to make_relevant_partitions_bootable() from within run_boot_loader() to within interactive_mode() and nuke_mode(), after unmounting disks - if editing fstab or *.conf, try to use pico if available - better calling of make-me-bootable - don't sort mountlist anywhere anymore except _locally_ in mount_all_devices() and unmount_all_devices() - edit fstab, grub.conf _after_ stabgrub-me if it fails - run_boot_loader() --- backup all crucial files to /etc/ *.pristine first - added iso_fiddly_bits() - fixed ISO mode support - mount_cdrom() only searches for device if NOT in disaster recovery mode - changed lost of system()'s into run_program_and_log_output()'s - don't eject if bkpinfo->please_dont_eject_when_restoring - cleaned up post-nuke handling code - always eject CD at end, unless bkpinfo->please_dont_... - misc clean-up (Tom Mortell) - afio uses -c (1024L*1024L)/TAPE_BLOCK_SIZE now instead of -c 1024 01/01 - 03/31/2003 - commented out sort_... line (BB) - clean-up (Stan Benoit) - added code for LVM and SW Raid (Brian Borgeson) - line 814 - added -p to 'mkdir -p tmp' - mount_cdrom() - calls find_cdrom_device() if bkpinfo->media_device is blank (to fill it) 11/01 - 12/31/2002 - mount_cdrom() better at handling multiple CD drives - minor clean-up in restore_a_tarball_from_CD() - if --live-from-cd then assume restoring live from CD - tweaked it to run better w/ ArkLinux - create /mnt/RESTORING/mnt/.boot.d for Gentoo users after restoring - cleaned up iso_mode(); no longer asks for NFS info 3 times - mount_cdrom() was trying to mount /mnt/isodir/%s/%d.iso; is now just %s/%d.iso - mount/unmount /boot if necessary (Gentoo) - added RAW MBR support; added run_raw_mbr() for the purpose - unmount & remount supermounts at start/end of live restore - copy /tmp/mountlist.txt to /tmp/mountlist.txt.orig at start - cleaned up string-handling in get_cfg_info_from_archives() - fixed run_grub() to call new stabgrub-me script - popup list of changed files after Compare Mode - permit mondorestore --edit-mountlist even if live mode - create a repaired copy of grub-install which is RAID-friendly; use it when initializing boot sector with run_grub() - use grub-MR instead of grub-install - fixed read_cfg_file_into_bkpinfo() to ignore cfg file's backup_media_type if user has already specified a backup media type interactively 10/01 - 10/31 - run_grub() will let you specify the boot device as well as edit the system files, if grub-install fails - fixed bug in fwrite() call in restore_biggiefile_from_CD() - fixed bug affecting restoration of bigfiles from CD's w/0 compression - run_grub() will run 'grub-install {boot device}' instead of 'grub-install (hd0)' 09/01 - 09/30 - use /tmp/tmpfs/mondo.tmp instead of /tmp/mondo.tmp - initialize MOUNTLIST_FNAME at start of main() - differential-related cleanup - better handling of CD-ROM drives which aren't /dev/cdrom :) - run_program_and_log_output() now takes boolean operator to specify whether it will log its activities in the event of _success_ - always load config file from archive before operating on it - moved some subroutines around; now closer to alphabetical order - changed mount.mindi to mount.bootisk - mount disks readonly if in Compare Mode - set /dev/null's permissions to 777, just in case it somehow gets mangled ...which apparently happen with some devfs-based Linux distributions - remove /var/run/ *.pid after restoring - move spurious lockfiles from /home/ * to /home/ * /.disabled - if Interactive Mode then ask user which media, etc. (i.e. catchall mode is now same as Interactive Mode) 08/01 - 08/30 - use data structure to store the fname, checksum, mods & perms of each bigfile ... biggiestruct :) - if a filelist is tiny (2 bytes or less) then ignore it - insist_on_this_cd_number() --- now takes 2 params, not 1 - re-enabled 'g_current_media_number = 1' in restore_everything() - added same to compare_mode() - replaced lots of global char[]'s with malloc()'s - if differential backup then don't permit formatting or fdisking, whether Interactive or Nuke mode - added call to register_pid() at start of main() - if Nuke Mode & it succeeds then ask user if they have contributed yet - changed tape-size to media-size (config file) - changed using_* to backup_media_type - changed *_from_tape to *_from_stream 07/01 - 07/31 - added find_and_mount_actual_cdrom() - temp dir is always random - skip tarballs if they don't contain files we're looking for (used to read the whole thing & _then_ skip) - use media_size[1] instead of media_size[0] - fixed serious bug in line 1546 - should have been !=, not ==; stopped mondorestore from correctly restoring big files - bigfile piping enhancements (Philippe de Muyter) - unmount CD-ROM after restoring from live filesystem - TAPE_BLOCK_SIZE treated as %ld, not %d 06/01 - 06/30 - added signal-trapping - disabled 'nr-failed-disks' flag - fixed problem w/selective restore - don't change /tmp's permissions unless it doesn't exist & must be created - fixed bug in --mbr - is_file_in_list() enhanced to exclude /mnt/RESTORING or whatever - added support for uncompressed archives - --monitas-live now accepts path-to-restore_to_, not just path to restore - added some debugging/tracking code to the NFS section - various monitas-related enhancements - added --isonuke and --mbr switches - better logging in run_grub() - improved --monitas-live - mkdir -p /mnt/RESTORING/var/run/console just in case user excludes it - afio now uses 16MB buffer instead of 8MB - always use bkpinfo->media_size[0], now that -s has been expanded - popup and ask where to restore data, if restoring selectively 05/01 - 05/31 - add '--monitas' flag - don't run chmod -R 1777 /mnt/RESTORING/tmp before unmounting unless restoring at the time... 04/01 - 04/30 - delete old /tmp/filelist.full,biggielist.txt if found when restoring to live filesystem - replace MONDO_VERSION #define with VERSION from ../config.h - write fname of bigfile to screen when having trouble reading/comparing it - if restoring to live filesystem then wipe /tmp/tmpfs/ * afterwards - removed spurious finish(0) from main() 03/01 - 03/31/2002 - if /tmp/m*ndo-restore.cfg not found then assume live restore; restore to / instead of /mnt/RESTORING - clean up is_file_in_list() to deal with the /mnt/RESTORING/ prefix - exclude leading '/' from filelist.restore-these - if /tmp/fstab.new exists then use _it_ instead of /tmp/fstab to label ext2 or ext3 partitions - improved logging [...] 07/10/2001 --- first incarnation */ /** * @file * The main file for mondorestore. */ /************************************************************************** * #include statements * **************************************************************************/ #include "../common/my-stuff.h" #include "../common/mondostructures.h" #include "../common/libmondo.h" #include "mr-externs.h" #include "mondo-restore.h" #include "mondo-rstr-compare-EXT.h" #include "mondo-rstr-tools-EXT.h" #include extern void success_message(void); extern void twenty_seconds_til_yikes(void); /* For use in other programs (ex. XMondo) */ #ifdef MONDORESTORE_MODULE #define main __mondorestore_main #define g_ISO_restore_mode __mondorestore_g_ISO_restore_mode #endif #ifndef VERSION #define VERSION AUX_VER #endif char g_version[] = VERSION; //static char cvsid[] = "$Id: mondo-restore.c 94 2005-10-28 23:08:39Z bcornec $"; /************************************************************************** * Globals * **************************************************************************/ extern char *g_tmpfs_mountpt; // declared in libmondo-tools.c extern struct s_bkpinfo *g_bkpinfo_DONTUSETHIS; // used by finish() to free // up global bkpinfo struct extern bool g_text_mode; extern FILE *g_fprep; extern double g_kernel_version; extern int g_partition_table_locked_up; extern int g_noof_rows; extern int partition_everything(struct mountlist_itself *mountlist); /** * @name Restore-Time Globals * @ingroup globalGroup * @{ */ /** * If TRUE, then SIGPIPE was just caught. * Set by the signal handler; cleared after it's handled. */ bool g_sigpipe_caught = FALSE; /** * If TRUE, then we're restoring from ISOs or an NFS server. * If FALSE, then we're restoring from some kind of real media (tape, CD, etc.) */ bool g_ISO_restore_mode = FALSE; /* are we in Iso Mode? */ /** * If TRUE, then we have had a successful "nuke" restore. */ bool g_I_have_just_nuked = FALSE; /** * The device to mount to get at the ISO images. Ignored unless @p g_ISO_restore_mode. */ char *g_isodir_device; /** * The format of @p g_isodir_device. Ignored unless @p g_ISO_restore_mode. */ char *g_isodir_format; /** * The location of 'biggielist.txt', containing the biggiefiles on the current archive set. */ char *g_biggielist_txt; /** * The location of 'filelist.full', containing all files (including biggiefiles) on * the current archive set. */ char *g_filelist_full; /** * The location of a file containing a list of the devices that were archived * as images, not as individual files. */ char *g_filelist_imagedevs; /** * The location of a file containing a list of imagedevs to actually restore. * @see g_filelist_imagedevs */ char *g_imagedevs_restthese; /** * The location of 'mondo-restore.cfg', containing the metadata * information for this backup. */ char *g_mondo_cfg_file; /** * The location of 'mountlist.txt', containing the information on the * user's partitions and hard drives. */ char *g_mountlist_fname; /** * Mondo's home directory during backup. Unused in mondo-restore; included * to avoid link errors. */ char *g_mondo_home; /* @} - end of "Restore-Time Globals" in globalGroup */ extern int copy_from_src_to_dest(FILE * f_orig, FILE * f_archived, char direction); /************************************************************************** * COMPAQ PROLIANT Stuff: needs some special help * **************************************************************************/ /** * The message to display if we detect that the user is using a Compaq Proliant. */ #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." /** * Allow the user to modify the mountlist before we partition & format their drives. * @param bkpinfo The backup information structure. @c disaster_recovery is the only field used. * @param mountlist The mountlist to let the user modify. * @param raidlist The raidlist that goes with @p mountlist. * @return 0 for success, nonzero for failure. * @ingroup restoreGuiGroup */ int let_user_edit_the_mountlist(struct s_bkpinfo *bkpinfo, struct mountlist_itself *mountlist, struct raidlist_itself *raidlist) { int retval = 0, res = 0; log_msg(2, "let_user_edit_the_mountlist() --- starting"); assert(bkpinfo != NULL); assert(mountlist != NULL); assert(raidlist != NULL); if (!bkpinfo->disaster_recovery) { strcpy(g_mountlist_fname, "/tmp/mountlist.txt"); log_msg(2, "I guess you're testing edit_mountlist()"); } if (!does_file_exist(g_mountlist_fname)) { log_to_screen(g_mountlist_fname); log_to_screen("does not exist"); return (1); } retval = load_mountlist(mountlist, g_mountlist_fname); load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME); if (retval) { log_to_screen ("Warning - load_raidtab_into_raidlist returned an error"); } res = edit_mountlist(g_mountlist_fname, mountlist, raidlist); if (res) { return (1); } save_mountlist_to_disk(mountlist, g_mountlist_fname); save_raidlist_to_raidtab(raidlist, RAIDTAB_FNAME); log_to_screen("I have finished editing the mountlist for you."); return (retval); } /** * Determine whether @p mountlist contains a Compaq diagnostic partition. * @param mountlist The mountlist to examine. * @return TRUE if there's a Compaq diagnostic partition; FALSE if not. * @ingroup restoreUtilityGroup */ bool partition_table_contains_Compaq_diagnostic_partition(struct mountlist_itself * mountlist) { int i; assert(mountlist != NULL); for (i = 0; i < mountlist->entries; i++) { if (strstr(mountlist->el[i].format, "ompaq")) { log_msg(2, "mountlist[%d] (%s) is %s (Compaq alert!)", i, mountlist->el[i].device, mountlist->el[i].format); return (TRUE); } } return (FALSE); } /************************************************************************** *END_PARTITION_TABLE_CONTAINS_COMPAQ_DIAGNOSTIC_PARTITION * **************************************************************************/ /** * Allow the user to abort the backup if we find that there is a Compaq diagnostic partition. * @note This function does not actually check for the presence of a Compaq partition. * @ingroup restoreUtilityGroup */ void offer_to_abort_because_Compaq_Proliants_suck(void) { popup_and_OK(COMPAQ_PROLIANTS_SUCK); if (ask_me_yes_or_no ("Would you like to reboot and use your Compaq CD to prep your hard drive?")) { fatal_error ("Aborting. Please reboot and prep your hard drive with your Compaq CD."); } } /************************************************************************** *END_OFFER_TO_ABORT_BECAUSE_COMPAQ_PROLIANTS_SUCK * **************************************************************************/ /** * Call interactive_mode(), nuke_mode(), or compare_mode() depending on the user's choice. * @param bkpinfo The backup information structure. Most fields are used. * @param mountlist The mountlist containing information about the user's partitions. * @param raidlist The raidlist to go with @p mountlist. * @return The return code from the mode function called. * @ingroup restoreGroup */ int catchall_mode(struct s_bkpinfo *bkpinfo, struct mountlist_itself *mountlist, struct raidlist_itself *raidlist) { char c, *tmp; int retval = 0; iamhere("inside catchall"); assert(bkpinfo != NULL); assert(mountlist != NULL); assert(raidlist != NULL); malloc_string(tmp); iamhere("pre wrm"); c = which_restore_mode(); iamhere("post wrm"); if (c == 'I' || c == 'N' || c == 'C') { interactively_obtain_media_parameters_from_user(bkpinfo, FALSE); } else { popup_and_OK("No restoring or comparing will take place today."); if (is_this_device_mounted("/mnt/cdrom")) { run_program_and_log_output("umount /mnt/cdrom", FALSE); } if (g_ISO_restore_mode) { sprintf(tmp, "umount %s", bkpinfo->isodir); run_program_and_log_output(tmp, FALSE); } paranoid_MR_finish(0); } iamhere("post int"); if (bkpinfo->backup_media_type == iso) { if (iso_fiddly_bits(bkpinfo, (c == 'N') ? TRUE : FALSE)) { log_msg(2, "catchall_mode --- iso_fiddly_bits returned w/ error"); return (1); } else { log_msg(2, "catchall_mode --- iso_fiddly_bits ok"); } } if (c == 'I') { log_msg(2, "IM selected"); retval += interactive_mode(bkpinfo, mountlist, raidlist); } else if (c == 'N') { log_msg(2, "NM selected"); retval += nuke_mode(bkpinfo, mountlist, raidlist); } else if (c == 'C') { log_msg(2, "CM selected"); retval += compare_mode(bkpinfo, mountlist, raidlist); } paranoid_free(tmp); return (retval); } /************************************************************************** *END_CATCHALL_MODE * **************************************************************************/ /************************************************************************** *END_ EXTRACT_CONFIG_FILE_FROM_RAMDISK * **************************************************************************/ /** * Locate an executable in the directory structure rooted at @p restg. * @param out_path Where to put the executable. * @param fname The basename of the executable. * @param restg The directory structure to look in. * @note If it could not be found in @p restg then @p fname is put in @p out_path. * @ingroup restoreUtilityGroup */ void find_pathname_of_executable_preferably_in_RESTORING(char *out_path, char *fname, char *restg) { assert(out_path != NULL); assert_string_is_neither_NULL_nor_zerolength(fname); sprintf(out_path, "%s/sbin/%s", restg, fname); if (does_file_exist(out_path)) { sprintf(out_path, "%s/usr/sbin/%s", restg, fname); if (does_file_exist(out_path)) { sprintf(out_path, "%s/bin/%s", restg, fname); if (does_file_exist(out_path)) { sprintf(out_path, "%s/usr/bin/%s", restg, fname); if (does_file_exist(out_path)) { strcpy(out_path, fname); } } } } } /************************************************************************** *END_FIND_PATHNAME_OF_EXECUTABLE_PREFERABLY_IN_RESTORING * **************************************************************************/ /** * @addtogroup restoreGroup * @{ */ /** * Restore the user's data, in a disaster recovery situation, prompting the * user about whether or not to do every step. * The user can edit the mountlist, choose files to restore, etc. * @param bkpinfo The backup information structure. Most fields are used. * @param mountlist The mountlist containing information about the user's partitions. * @param raidlist The raidlist to go with @p mountlist. * @return 0 for success, or the number of errors encountered. */ int interactive_mode(struct s_bkpinfo *bkpinfo, struct mountlist_itself *mountlist, struct raidlist_itself *raidlist) { int retval = 0; int res; int ptn_errs = 0; int fmt_errs = 0; bool done; bool restore_all; /** needs malloc **********/ char *tmp; char *fstab_fname; char *old_restpath; struct s_node *filelist; /* try to partition and format */ log_msg(2, "interactive_mode --- starting (great, assertions OK)"); malloc_string(tmp); malloc_string(fstab_fname); malloc_string(old_restpath); assert(bkpinfo != NULL); assert(mountlist != NULL); assert(raidlist != NULL); log_msg(2, "interactive_mode --- assertions OK"); if (g_text_mode) { if (!ask_me_yes_or_no ("Interactive Mode + textonly = experimental! Proceed anyway?")) { fatal_error("Wise move."); } } iamhere("About to load config file"); get_cfg_file_from_archive_or_bust(bkpinfo); read_cfg_file_into_bkpinfo(g_mondo_cfg_file, bkpinfo); iamhere("Done loading config file; resizing ML"); #ifdef __FreeBSD__ if (strstr (call_program_and_get_last_line_of_output("cat /tmp/cmdline"), "noresize")) #else if (strstr (call_program_and_get_last_line_of_output("cat /proc/cmdline"), "noresize")) #endif { log_msg(1, "Not resizing mountlist."); } else { resize_mountlist_proportionately_to_suit_new_drives(mountlist); } for (done = FALSE; !done;) { iamhere("About to edit mountlist"); if (g_text_mode) { save_mountlist_to_disk(mountlist, g_mountlist_fname); sprintf(tmp, "%s %s", find_my_editor(), g_mountlist_fname); res = system(tmp); load_mountlist(mountlist, g_mountlist_fname); } else { res = edit_mountlist(g_mountlist_fname, mountlist, raidlist); } iamhere("Finished editing mountlist"); if (res) { paranoid_MR_finish(1); } log_msg(2, "Proceeding..."); save_mountlist_to_disk(mountlist, g_mountlist_fname); save_raidlist_to_raidtab(raidlist, RAIDTAB_FNAME); mvaddstr_and_log_it(1, 30, "Restoring Interactively"); if (bkpinfo->differential) { log_to_screen("Because this is a differential backup, disk"); log_to_screen (" partitioning and formatting will not take place."); done = TRUE; } else { if (ask_me_yes_or_no ("Do you want to erase and partition your hard drives?")) { if (partition_table_contains_Compaq_diagnostic_partition (mountlist)) { offer_to_abort_because_Compaq_Proliants_suck(); done = TRUE; } else { twenty_seconds_til_yikes(); g_fprep = fopen("/tmp/prep.sh", "w"); ptn_errs = partition_everything(mountlist); if (ptn_errs) { log_to_screen ("Warning. Errors occurred during disk partitioning."); } fmt_errs = format_everything(mountlist, FALSE); if (!fmt_errs) { log_to_screen ("Errors during disk partitioning were handled OK."); log_to_screen ("Partitions were formatted OK despite those errors."); ptn_errs = 0; } if (!ptn_errs && !fmt_errs) { done = TRUE; } } paranoid_fclose(g_fprep); } else { mvaddstr_and_log_it(g_currentY++, 0, "User opted not to partition the devices"); if (ask_me_yes_or_no ("Do you want to format your hard drives?")) { fmt_errs = format_everything(mountlist, TRUE); if (!fmt_errs) { done = TRUE; } } else { ptn_errs = fmt_errs = 0; done = TRUE; } } if (fmt_errs) { mvaddstr_and_log_it(g_currentY++, 0, "Errors occurred. Please repartition and format drives manually."); done = FALSE; } if (ptn_errs & !fmt_errs) { mvaddstr_and_log_it(g_currentY++, 0, "Errors occurred during partitioning. Formatting, however, went OK."); done = TRUE; } if (!done) { if (!ask_me_yes_or_no("Re-edit the mountlist?")) { retval++; goto end_of_func; } } } } /* mount */ if (mount_all_devices(mountlist, TRUE)) { unmount_all_devices(mountlist); retval++; goto end_of_func; } /* restore */ if ((restore_all = ask_me_yes_or_no("Do you want me to restore all of your data?"))) { log_msg(1, "Restoring all data"); retval += restore_everything(bkpinfo, NULL); } else if ((restore_all = ask_me_yes_or_no ("Do you want me to restore _some_ of your data?"))) { strcpy(old_restpath, bkpinfo->restore_path); for (done = FALSE; !done;) { unlink("/tmp/filelist.full"); filelist = process_filelist_and_biggielist(bkpinfo); /* Now you have /tmp/tmpfs/filelist.restore-these and /tmp/tmpfs/biggielist.restore-these; the former is a list of regular files; the latter, biggiefiles and imagedevs. */ if (filelist) { gotos_suck: strcpy(tmp, old_restpath); // (NB: %s is where your filesystem is mounted now, by default)", MNT_RESTORING); if (popup_and_get_string ("Restore path", "Restore files to where?", tmp, MAX_STR_LEN / 4)) { if (!strcmp(tmp, "/")) { if (!ask_me_yes_or_no("Are you sure?")) { goto gotos_suck; } tmp[0] = '\0'; // so we restore to [blank]/file/name :) } strcpy(bkpinfo->restore_path, tmp); log_msg(1, "Restoring subset"); retval += restore_everything(bkpinfo, filelist); free_filelist(filelist); } else { strcpy(bkpinfo->restore_path, old_restpath); free_filelist(filelist); } if (!ask_me_yes_or_no ("Restore another subset of your backup?")) { done = TRUE; } } else { done = TRUE; } } strcpy(old_restpath, bkpinfo->restore_path); } else { mvaddstr_and_log_it(g_currentY++, 0, "User opted not to restore any data. "); } if (retval) { mvaddstr_and_log_it(g_currentY++, 0, "Errors occurred during the restore phase. "); } if (ask_me_yes_or_no("Initialize the boot loader?")) { run_boot_loader(TRUE); } else { mvaddstr_and_log_it(g_currentY++, 0, "User opted not to initialize the boot loader."); } // run_program_and_log_output("cp -af /etc/lvm " MNT_RESTORING "/etc/", 1); protect_against_braindead_sysadmins(); // modify_rclocal_one_time( MNT_RESTORING "/etc" ); retval += unmount_all_devices(mountlist); /* if (restore_some || restore_all || */ if (ask_me_yes_or_no ("Label your ext2 and ext3 partitions if necessary?")) { mvaddstr_and_log_it(g_currentY, 0, "Using e2label to label your ext2,3 partitions"); if (does_file_exist("/tmp/fstab.new")) { strcpy(fstab_fname, "/tmp/fstab.new"); } else { strcpy(fstab_fname, "/tmp/fstab"); } sprintf(tmp, "label-partitions-as-necessary %s < %s >> %s 2>> %s", g_mountlist_fname, fstab_fname, MONDO_LOGFILE, MONDO_LOGFILE); res = system(tmp); if (res) { log_to_screen ("label-partitions-as-necessary returned an error"); mvaddstr_and_log_it(g_currentY++, 74, "Failed."); } else { mvaddstr_and_log_it(g_currentY++, 74, "Done."); } retval += res; } iamhere("About to leave interactive_mode()"); if (retval) { mvaddstr_and_log_it(g_currentY++, 0, "Warning - errors occurred during the restore phase."); } end_of_func: paranoid_free(tmp); paranoid_free(fstab_fname); paranoid_free(old_restpath); iamhere("Leaving interactive_mode()"); return (retval); } /************************************************************************** *END_INTERACTIVE_MODE * **************************************************************************/ /** * Run an arbitrary restore mode (prompt the user), but from ISO images * instead of real media. * @param bkpinfo The backup information structure. Most fields are used. * @param mountlist The mountlist containing information about the user's partitions. * @param raidlist The raidlist that goes with @p mountlist. * @param nuke_me_please If TRUE, we plan to run Nuke Mode. * @return 0 for success, or the number of errors encountered. */ int iso_mode(struct s_bkpinfo *bkpinfo, struct mountlist_itself *mountlist, struct raidlist_itself *raidlist, bool nuke_me_please) { char c; int retval = 0; assert(bkpinfo != NULL); assert(mountlist != NULL); assert(raidlist != NULL); if (iso_fiddly_bits(bkpinfo, nuke_me_please)) { log_msg(1, "iso_mode --- returning w/ error"); return (1); } else { c = which_restore_mode(); if (c == 'I' || c == 'N' || c == 'C') { interactively_obtain_media_parameters_from_user(bkpinfo, FALSE); } if (c == 'I') { retval += interactive_mode(bkpinfo, mountlist, raidlist); } else if (c == 'N') { retval += nuke_mode(bkpinfo, mountlist, raidlist); } else if (c == 'C') { retval += compare_mode(bkpinfo, mountlist, raidlist); } else { log_to_screen("OK, I shan't restore/compare any files."); } } if (is_this_device_mounted(MNT_CDROM)) { paranoid_system("umount " MNT_CDROM); } // if (! already_mounted) // { if (system("umount /tmp/isodir 2> /dev/null")) { log_to_screen ("WARNING - unable to unmount device where the ISO files are stored."); } // } return (retval); } /************************************************************************** *END_ISO_MODE * **************************************************************************/ /* MONDO - saving your a$$ since Feb 18th, 2000 */ /** * Restore the user's data automatically (no prompts), after a twenty-second * warning period. * @param bkpinfo The backup information structure. Most fields are used. * @param mountlist The mountlist containing information about the user's partitions. * @param raidlist The raidlist that goes with @p mountlist. * @return 0 for success, or the number of errors encountered. * @warning THIS WILL ERASE ALL EXISTING DATA! */ int nuke_mode(struct s_bkpinfo *bkpinfo, struct mountlist_itself *mountlist, struct raidlist_itself *raidlist) { int retval = 0; int res = 0; bool boot_loader_installed = FALSE; /** malloc **/ char tmp[MAX_STR_LEN], tmpA[MAX_STR_LEN], tmpB[MAX_STR_LEN], tmpC[MAX_STR_LEN]; assert(bkpinfo != NULL); assert(mountlist != NULL); assert(raidlist != NULL); log_msg(2, "nuke_mode --- starting"); get_cfg_file_from_archive_or_bust(bkpinfo); load_mountlist(mountlist, g_mountlist_fname); // in case read_cfg_file_into_bkpinfo updated the mountlist #ifdef __FreeBSD__ if (strstr (call_program_and_get_last_line_of_output("cat /tmp/cmdline"), "noresize")) #else if (strstr (call_program_and_get_last_line_of_output("cat /proc/cmdline"), "noresize")) #endif { log_msg(2, "Not resizing mountlist."); } else { resize_mountlist_proportionately_to_suit_new_drives(mountlist); } if (!evaluate_mountlist(mountlist, tmpA, tmpB, tmpC)) { sprintf(tmp, "Mountlist analyzed. Result: \"%s %s %s\" Switch to Interactive Mode?", tmpA, tmpB, tmpC); if (ask_me_yes_or_no(tmp)) { retval = interactive_mode(bkpinfo, mountlist, raidlist); finish(retval); } else { fatal_error("Nuke Mode aborted. "); } } save_mountlist_to_disk(mountlist, g_mountlist_fname); mvaddstr_and_log_it(1, 30, "Restoring Automatically"); if (bkpinfo->differential) { log_to_screen("Because this is a differential backup, disk"); log_to_screen("partitioning and formatting will not take place."); res = 0; } else { if (partition_table_contains_Compaq_diagnostic_partition (mountlist)) { offer_to_abort_because_Compaq_Proliants_suck(); } else { twenty_seconds_til_yikes(); g_fprep = fopen("/tmp/prep.sh", "w"); #ifdef __FreeBSD__ if (strstr (call_program_and_get_last_line_of_output ("cat /tmp/cmdline"), "nopart")) #else if (strstr (call_program_and_get_last_line_of_output ("cat /proc/cmdline"), "nopart")) #endif { log_msg(2, "Not partitioning drives due to 'nopart' option."); res = 0; } else { res = partition_everything(mountlist); if (res) { log_to_screen ("Warning. Errors occurred during partitioning."); res = 0; } } retval += res; if (!res) { log_to_screen("Preparing to format your disk(s)"); sleep(1); system("sync"); log_to_screen("Please wait. This may take a few minutes."); res += format_everything(mountlist, FALSE); } paranoid_fclose(g_fprep); } } retval += res; if (res) { mvaddstr_and_log_it(g_currentY++, 0, "Failed to partition and/or format your hard drives."); if (ask_me_yes_or_no("Try in interactive mode instead?")) { retval = interactive_mode(bkpinfo, mountlist, raidlist); goto after_the_nuke; } else if (!ask_me_yes_or_no ("Would you like to try to proceed anyway?")) { return (retval); } } retval = mount_all_devices(mountlist, TRUE); if (retval) { unmount_all_devices(mountlist); log_to_screen ("Unable to mount all partitions. Sorry, I cannot proceed."); return (retval); } iamhere("Restoring everything"); retval += restore_everything(bkpinfo, NULL); if (!run_boot_loader(FALSE)) { log_msg(1, "Great! Boot loader was installed. No need for msg at end."); boot_loader_installed = TRUE; } protect_against_braindead_sysadmins(); // run_program_and_log_output("cp -af /etc/lvm " MNT_RESTORING "/etc/", 1); // modify_rclocal_one_time( MNT_RESTORING "/etc" ); retval += unmount_all_devices(mountlist); mvaddstr_and_log_it(g_currentY, 0, "Using e2label to label your ext2,3 partitions"); sprintf(tmp, "label-partitions-as-necessary %s < /tmp/fstab", g_mountlist_fname); res = run_program_and_log_output(tmp, TRUE); if (res) { log_to_screen("label-partitions-as-necessary returned an error"); mvaddstr_and_log_it(g_currentY++, 74, "Failed."); } else { mvaddstr_and_log_it(g_currentY++, 74, "Done."); } retval += res; after_the_nuke: if (retval) { log_to_screen("Errors occurred during the nuke phase."); } else if (strstr(call_program_and_get_last_line_of_output("cat /proc/cmdline"), "RESTORE")) // Bruno's thing { log_to_screen ("PC was restored successfully. Thank you for using Mondo Rescue."); log_to_screen ("Please visit http://www.mondorescue.org and thank the dev team."); } else { #ifdef FREELOADER success_message(); #else log_to_screen("PC was restored successfully!"); #endif } g_I_have_just_nuked = TRUE; /* if (!boot_loader_installed && !does_file_exist(DO_MBR_PLEASE)) { log_to_screen("PLEASE RUN 'mondorestore --mbr' NOW TO INITIALIZE YOUR BOOT SECTOR"); write_one_liner_data_file(DO_MBR_PLEASE, "mondorestore --mbr"); } */ return (retval); } /************************************************************************** *END_NUKE_MODE * **************************************************************************/ /** * Restore the user's data (or a subset of it) to the live filesystem. * This should not be called if we're booted from CD! * @param bkpinfo The backup information structure. Most fields are used. * @return 0 for success, or the number of errors encountered. */ int restore_to_live_filesystem(struct s_bkpinfo *bkpinfo) { int retval = 0; /** malloc **/ char *old_restpath; struct mountlist_itself *mountlist; // static struct raidlist_itself *raidlist; struct s_node *filelist; log_msg(1, "restore_to_live_filesystem() - starting"); assert(bkpinfo != NULL); malloc_string(old_restpath); mountlist = malloc(sizeof(struct mountlist_itself)); raidlist = malloc(sizeof(struct raidlist_itself)); if (!mountlist || !raidlist) { fatal_error("Cannot malloc() mountlist and/or raidlist"); } strcpy(bkpinfo->restore_path, "/"); if (!g_restoring_live_from_cd) { popup_and_OK ("Please insert tape/CD/boot floppy, then hit 'OK' to continue."); sleep(1); } interactively_obtain_media_parameters_from_user(bkpinfo, FALSE); log_msg(2, "bkpinfo->media_device = %s", bkpinfo->media_device); if (!bkpinfo->media_device[0]) { log_msg(2, "Warning - failed to find media dev"); } log_msg(2, "bkpinfo->isodir = %s", bkpinfo->isodir); open_evalcall_form("Thinking..."); get_cfg_file_from_archive_or_bust(bkpinfo); read_cfg_file_into_bkpinfo(g_mondo_cfg_file, bkpinfo); load_mountlist(mountlist, g_mountlist_fname); // in case read_cfg_file_into_bkpinfo close_evalcall_form(); retval = load_mountlist(mountlist, g_mountlist_fname); load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME); filelist = process_filelist_and_biggielist(bkpinfo); if (filelist) { save_filelist(filelist, "/tmp/selected-files.txt"); strcpy(old_restpath, bkpinfo->restore_path); if (popup_and_get_string("Restore path", "Restore files to where? )", bkpinfo->restore_path, MAX_STR_LEN / 4)) { iamhere("Restoring everything"); retval += restore_everything(bkpinfo, filelist); free_filelist(filelist); strcpy(bkpinfo->restore_path, old_restpath); } else { free_filelist(filelist); } strcpy(bkpinfo->restore_path, old_restpath); } if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) { log_msg(2, "I probably don't need to unmount or eject the CD-ROM but I'm doing it anyway."); } run_program_and_log_output("umount " MNT_CDROM, FALSE); if (!bkpinfo->please_dont_eject) { eject_device(bkpinfo->media_device); } paranoid_free(old_restpath); free(mountlist); free(raidlist); return (retval); } /************************************************************************** *END_RESTORE_TO_LIVE_FILESYSTEM * **************************************************************************/ /* @} - end of restoreGroup */ #include /** * @addtogroup LLrestoreGroup * @{ */ /** * Restore biggiefile @p bigfileno from the currently mounted CD. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->backup_media_type * - @c bkpinfo->restore_path * @param bigfileno The biggiefile number (starting from 0) to restore. * @param filelist The node structure containing the list of files to restore. * If the biggiefile is not in this list, it will be skipped (return value will * still indicate success). * @return 0 for success (or skip), nonzero for failure. */ int restore_a_biggiefile_from_CD(struct s_bkpinfo *bkpinfo, long bigfileno, struct s_node *filelist, char *pathname_of_last_file_restored) { FILE *fin; FILE *fout; FILE *fbzip2; /** malloc ***/ char *checksum, *outfile_fname, *tmp, *bzip2_command, *partimagehack_command, *suffix, *sz_devfile; char *bigblk; char *p; int retval = 0; int finished = FALSE; long sliceno; long siz; char partimagehack_fifo[MAX_STR_LEN]; char *file_to_openout = NULL; struct s_filename_and_lstat_info biggiestruct; struct utimbuf the_utime_buf, *ubuf; bool use_partimage_hack = FALSE; pid_t pid; int res = 0; int old_loglevel; char sz_msg[MAX_STR_LEN]; struct s_node *node; old_loglevel = g_loglevel; ubuf = &the_utime_buf; assert(bkpinfo != NULL); malloc_string(checksum); malloc_string(outfile_fname); malloc_string(tmp); malloc_string(bzip2_command); malloc_string(partimagehack_command); malloc_string(suffix); malloc_string(sz_devfile); pathname_of_last_file_restored[0] = '\0'; if (!(bigblk = malloc(TAPE_BLOCK_SIZE))) { fatal_error("Cannot malloc bigblk"); } if (!(fin = fopen(slice_fname(bigfileno, 0, ARCHIVES_PATH, ""), "r"))) { log_to_screen("Cannot even open bigfile's info file"); return (1); } memset((void *) &biggiestruct, 0, sizeof(biggiestruct)); if (fread((void *) &biggiestruct, 1, sizeof(biggiestruct), fin) < sizeof(biggiestruct)) { log_msg(2, "Warning - unable to get biggiestruct of bigfile #%d", bigfileno + 1); } paranoid_fclose(fin); strcpy(checksum, biggiestruct.checksum); if (!checksum[0]) { sprintf(tmp, "Warning - bigfile %ld does not have a checksum", bigfileno + 1); log_msg(3, tmp); p = checksum; } if (!strncmp(biggiestruct.filename, "/dev/", 5)) // Whether NTFS or not :) { strcpy(outfile_fname, biggiestruct.filename); } else { sprintf(outfile_fname, "%s/%s", bkpinfo->restore_path, biggiestruct.filename); } /* skip file if we have a selective restore subset & it doesn't match */ if (filelist != NULL) { node = find_string_at_node(filelist, biggiestruct.filename); if (!node) { log_msg(0, "Skipping %s (name isn't in filelist)", biggiestruct.filename); pathname_of_last_file_restored[0] = '\0'; return (0); } else if (!(node->selected)) { log_msg(1, "Skipping %s (name isn't in biggielist subset)", biggiestruct.filename); pathname_of_last_file_restored[0] = '\0'; return (0); } } /* otherwise, continue */ log_msg(1, "DEFINITELY restoring %s", biggiestruct.filename); if (biggiestruct.use_partimagehack) { if (strncmp(biggiestruct.filename, "/dev/", 5)) { log_msg(1, "I was in error when I set biggiestruct.use_partimagehack to TRUE."); log_msg(1, "%s isn't even in /dev", biggiestruct.filename); biggiestruct.use_partimagehack = FALSE; } } if (biggiestruct.use_partimagehack) // if it's an NTFS device // if (!strncmp ( biggiestruct.filename, "/dev/", 5)) { g_loglevel = 4; use_partimage_hack = TRUE; log_msg(2, "Calling partimagehack in background because %s is an NTFS /dev entry", outfile_fname); sprintf(sz_devfile, "/tmp/%d.%d.000", (int) (random() % 32768), (int) (random() % 32768)); mkfifo(sz_devfile, 0x770); strcpy(partimagehack_fifo, sz_devfile); file_to_openout = partimagehack_fifo; switch (pid = fork()) { case -1: fatal_error("Fork failure"); case 0: log_msg(3, "CHILD - fip - calling feed_outfrom_partimage(%s, %s)", biggiestruct.filename, partimagehack_fifo); res = feed_outfrom_partimage(biggiestruct.filename, partimagehack_fifo); // log_msg(3, "CHILD - fip - exiting"); exit(res); break; default: log_msg(3, "feed_into_partimage() called in background --- pid=%ld", (long int) (pid)); } } else { use_partimage_hack = FALSE; partimagehack_fifo[0] = '\0'; file_to_openout = outfile_fname; if (!does_file_exist(outfile_fname)) // yes, it looks weird with the '!' but it's correct that way { make_hole_for_file(outfile_fname); } } sprintf(tmp, "Reassembling big file %ld (%s)", bigfileno + 1, outfile_fname); log_msg(2, tmp); /* last slice is zero-length and uncompressed; when we find it, we stop. We DON'T wait until there are no more slices; if we did that, We might stop at end of CD, not at last slice (which is 0-len and uncompd) */ strncpy(pathname_of_last_file_restored, biggiestruct.filename, MAX_STR_LEN - 1); pathname_of_last_file_restored[MAX_STR_LEN - 1] = '\0'; log_msg(3, "file_to_openout = %s", file_to_openout); if (!(fout = fopen(file_to_openout, "w"))) { log_to_screen("Cannot openout outfile_fname - hard disk full?"); return (1); } log_msg(3, "Opened out to %s", outfile_fname); // CD/DVD --> mondorestore --> partimagehack --> hard disk itself for (sliceno = 1, finished = FALSE; !finished;) { if (!does_file_exist (slice_fname(bigfileno, sliceno, ARCHIVES_PATH, "")) && !does_file_exist(slice_fname (bigfileno, sliceno, ARCHIVES_PATH, "lzo")) && !does_file_exist(slice_fname (bigfileno, sliceno, ARCHIVES_PATH, "bz2"))) { log_msg(3, "Cannot find a data slice or terminator slice on CD %d", g_current_media_number); g_current_media_number++; sprintf(tmp, "Asking for %s #%d so that I may read slice #%ld\n", media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number, sliceno); log_msg(2, tmp); sprintf(tmp, "Restoring from %s #%d", media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number); log_to_screen(tmp); insist_on_this_cd_number(bkpinfo, g_current_media_number); log_to_screen("Continuing to restore."); } else { strcpy(tmp, slice_fname(bigfileno, sliceno, ARCHIVES_PATH, "")); if (does_file_exist(tmp) && length_of_file(tmp) == 0) { log_msg(2, "End of bigfile # %ld (slice %ld is the terminator)", bigfileno + 1, sliceno); finished = TRUE; continue; } else { if (does_file_exist (slice_fname (bigfileno, sliceno, ARCHIVES_PATH, "lzo"))) { strcpy(bzip2_command, "lzop"); strcpy(suffix, "lzo"); } else if (does_file_exist (slice_fname (bigfileno, sliceno, ARCHIVES_PATH, "bz2"))) { strcpy(bzip2_command, "bzip2"); strcpy(suffix, "bz2"); } else if (does_file_exist (slice_fname (bigfileno, sliceno, ARCHIVES_PATH, ""))) { strcpy(bzip2_command, ""); strcpy(suffix, ""); } else { log_to_screen("OK, that's pretty fsck0red..."); return (1); } } if (bzip2_command[0] != '\0') { sprintf(bzip2_command + strlen(bzip2_command), " -dc %s 2>> %s", slice_fname(bigfileno, sliceno, ARCHIVES_PATH, suffix), MONDO_LOGFILE); } else { sprintf(bzip2_command, "cat %s 2>> %s", slice_fname(bigfileno, sliceno, ARCHIVES_PATH, suffix), MONDO_LOGFILE); } sprintf(tmp, "Working on %s #%d, file #%ld, slice #%ld ", media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number, bigfileno + 1, sliceno); log_msg(2, tmp); if (!g_text_mode) { newtDrawRootText(0, g_noof_rows - 2, tmp); newtRefresh(); strip_spaces(tmp); update_progress_form(tmp); } if (!(fbzip2 = popen(bzip2_command, "r"))) { fatal_error("Can't run popen command"); } while (!feof(fbzip2)) { siz = fread(bigblk, 1, TAPE_BLOCK_SIZE, fbzip2); if (siz > 0) { sprintf(sz_msg, "Read %ld from fbzip2", siz); siz = fwrite(bigblk, 1, siz, fout); sprintf(sz_msg + strlen(sz_msg), "; written %ld to fout", siz); // log_msg(2. sz_msg); } } paranoid_pclose(fbzip2); sliceno++; g_current_progress++; } } /* memset(bigblk, TAPE_BLOCK_SIZE, 1); // This all looks very fishy... fwrite( bigblk, 1, TAPE_BLOCK_SIZE, fout); fwrite( bigblk, 1, TAPE_BLOCK_SIZE, fout); fwrite( bigblk, 1, TAPE_BLOCK_SIZE, fout); fwrite( bigblk, 1, TAPE_BLOCK_SIZE, fout); */ paranoid_fclose(fout); g_loglevel = old_loglevel; if (use_partimage_hack) { log_msg(3, "Waiting for partimage to finish"); sprintf(tmp, " ps ax | grep \" partimagehack \" | grep -v grep > /dev/null 2> /dev/null"); while (system(tmp) == 0) { sleep(1); } log_it("OK, partimage has really finished"); } if (strcmp(outfile_fname, "/dev/null")) { chown(outfile_fname, biggiestruct.properties.st_uid, biggiestruct.properties.st_gid); chmod(outfile_fname, biggiestruct.properties.st_mode); ubuf->actime = biggiestruct.properties.st_atime; ubuf->modtime = biggiestruct.properties.st_mtime; utime(outfile_fname, ubuf); } paranoid_free(bigblk); paranoid_free(checksum); paranoid_free(outfile_fname); paranoid_free(tmp); paranoid_free(bzip2_command); paranoid_free(partimagehack_command); paranoid_free(suffix); paranoid_free(sz_devfile); return (retval); } /************************************************************************** *END_ RESTORE_A_BIGGIEFILE_FROM_CD * **************************************************************************/ /** * Restore a biggiefile from the currently opened stream. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->restore_path * - @c bkpinfo->zip_exe * @param orig_bf_fname The original filename of the biggiefile. * @param biggiefile_number The number of the biggiefile (starting from 0). * @param orig_checksum Unused. * @param biggiefile_size Unused. * @param filelist The node structure containing the list of files to be restored. * If @p orig_bf_fname is not in the list, it will be ignored. * @return 0 for success (or skip), nonzero for failure. * @bug orig_checksum and biggiefile_size are unused (except to check that they are non-NULL). */ int restore_a_biggiefile_from_stream(struct s_bkpinfo *bkpinfo, char *orig_bf_fname, long biggiefile_number, char *orig_checksum, //UNUSED long long biggiefile_size, //UNUSED struct s_node *filelist, int use_partimagehack, char *pathname_of_last_file_restored) { FILE *pout; FILE *fin; /** mallocs ********/ char *tmp; char *command; char *outfile_fname; char *partimagehack_command; char *sz_devfile; char *partimagehack_fifo; char *file_to_openout = NULL; struct s_node *node; int old_loglevel; long current_slice_number = 0; int retval = 0; int res = 0; int ctrl_chr = '\0'; long long slice_siz; bool dummy_restore = FALSE; bool use_partimage_hack = FALSE; pid_t pid; struct s_filename_and_lstat_info biggiestruct; struct utimbuf the_utime_buf, *ubuf; ubuf = &the_utime_buf; malloc_string(tmp); malloc_string(partimagehack_fifo); malloc_string(outfile_fname); malloc_string(command); malloc_string(sz_devfile); malloc_string(partimagehack_command); old_loglevel = g_loglevel; assert(bkpinfo != NULL); assert(orig_bf_fname != NULL); assert(orig_checksum != NULL); pathname_of_last_file_restored[0] = '\0'; if (use_partimagehack == BLK_START_A_PIHBIGGIE) { use_partimagehack = 1; log_msg(1, "%s --- pih=YES", orig_bf_fname); } else if (use_partimagehack == BLK_START_A_NORMBIGGIE) { use_partimagehack = 0; log_msg(1, "%s --- pih=NO", orig_bf_fname); } else { use_partimagehack = 0; log_msg(1, "%s --- pih=NO (weird marker though)", orig_bf_fname); } strncpy(pathname_of_last_file_restored, orig_bf_fname, MAX_STR_LEN - 1); pathname_of_last_file_restored[MAX_STR_LEN - 1] = '\0'; /* open out to biggiefile to be restored (or /dev/null if biggiefile is not to be restored) */ if (filelist != NULL) { node = find_string_at_node(filelist, orig_bf_fname); if (!node) { dummy_restore = TRUE; log_msg(1, "Skipping big file %ld (%s) - not in biggielist subset", biggiefile_number + 1, orig_bf_fname); pathname_of_last_file_restored[0] = '\0'; } else if (!(node->selected)) { dummy_restore = TRUE; log_msg(1, "Skipping %s (name isn't in biggielist subset)", orig_bf_fname); pathname_of_last_file_restored[0] = '\0'; } } if (use_partimagehack) { if (strncmp(orig_bf_fname, "/dev/", 5)) { log_msg(1, "I was in error when I set use_partimagehack to TRUE."); log_msg(1, "%s isn't even in /dev", orig_bf_fname); use_partimagehack = FALSE; } } if (use_partimagehack) { g_loglevel = 4; strcpy(outfile_fname, orig_bf_fname); use_partimage_hack = TRUE; log_msg(2, "Calling partimagehack in background because %s is a /dev entry", outfile_fname); sprintf(sz_devfile, "/tmp/%d.%d.000", (int) (random() % 32768), (int) (random() % 32768)); mkfifo(sz_devfile, 0x770); strcpy(partimagehack_fifo, sz_devfile); file_to_openout = partimagehack_fifo; switch (pid = fork()) { case -1: fatal_error("Fork failure"); case 0: log_msg(3, "CHILD - fip - calling feed_outfrom_partimage(%s, %s)", outfile_fname, partimagehack_fifo); res = feed_outfrom_partimage(outfile_fname, partimagehack_fifo); // log_msg(3, "CHILD - fip - exiting"); exit(res); break; default: log_msg(3, "feed_into_partimage() called in background --- pid=%ld", (long int) (pid)); } } else { if (!strncmp(orig_bf_fname, "/dev/", 5)) // non-NTFS partition { strcpy(outfile_fname, orig_bf_fname); } else // biggiefile { sprintf(outfile_fname, "%s/%s", bkpinfo->restore_path, orig_bf_fname); } use_partimage_hack = FALSE; partimagehack_fifo[0] = '\0'; file_to_openout = outfile_fname; if (!does_file_exist(outfile_fname)) // yes, it looks weird with the '!' but it's correct that way { make_hole_for_file(outfile_fname); } sprintf(tmp, "Reassembling big file %ld (%s)", biggiefile_number + 1, orig_bf_fname); log_msg(2, tmp); } if (dummy_restore) { sprintf(outfile_fname, "/dev/null"); } if (!bkpinfo->zip_exe[0]) { sprintf(command, "cat > \"%s\"", file_to_openout); } else { sprintf(command, "%s -dc > \"%s\" 2>> %s", bkpinfo->zip_exe, file_to_openout, MONDO_LOGFILE); } sprintf(tmp, "Pipe command = '%s'", command); log_msg(3, tmp); /* restore biggiefile, one slice at a time */ if (!(pout = popen(command, "w"))) { fatal_error("Cannot pipe out"); } for (res = read_header_block_from_stream(&slice_siz, tmp, &ctrl_chr); ctrl_chr != BLK_STOP_A_BIGGIE; res = read_header_block_from_stream(&slice_siz, tmp, &ctrl_chr)) { if (ctrl_chr != BLK_START_AN_AFIO_OR_SLICE) { wrong_marker(BLK_START_AN_AFIO_OR_SLICE, ctrl_chr); } sprintf(tmp, "Working on file #%ld, slice #%ld ", biggiefile_number + 1, current_slice_number); log_msg(2, tmp); if (!g_text_mode) { newtDrawRootText(0, g_noof_rows - 2, tmp); newtRefresh(); } strip_spaces(tmp); update_progress_form(tmp); if (current_slice_number == 0) { res = read_file_from_stream_to_file(bkpinfo, "/tmp/biggie-blah.txt", slice_siz); if (!(fin = fopen("/tmp/biggie-blah.txt", "r"))) { log_OS_error("blah blah"); } else { if (fread ((void *) &biggiestruct, 1, sizeof(biggiestruct), fin) < sizeof(biggiestruct)) { log_msg(2, "Warning - unable to get biggiestruct of bigfile #%d", biggiefile_number + 1); } paranoid_fclose(fin); } } else { res = read_file_from_stream_to_stream(bkpinfo, pout, slice_siz); } retval += res; res = read_header_block_from_stream(&slice_siz, tmp, &ctrl_chr); if (ctrl_chr != BLK_STOP_AN_AFIO_OR_SLICE) { wrong_marker(BLK_STOP_AN_AFIO_OR_SLICE, ctrl_chr); } current_slice_number++; g_current_progress++; } paranoid_pclose(pout); log_msg(1, "pathname_of_last_file_restored is now %s", pathname_of_last_file_restored); if (use_partimage_hack) { log_msg(3, "Waiting for partimage to finish"); sprintf(tmp, " ps ax | grep \" partimagehack \" | grep -v grep > /dev/null 2> /dev/null"); while (system(tmp) == 0) { sleep(1); } log_msg(3, "OK, partimage has really finished"); } log_msg(3, "biggiestruct.filename = %s", biggiestruct.filename); log_msg(3, "biggiestruct.checksum = %s", biggiestruct.checksum); if (strcmp(outfile_fname, "/dev/null")) { chmod(outfile_fname, biggiestruct.properties.st_mode); chown(outfile_fname, biggiestruct.properties.st_uid, biggiestruct.properties.st_gid); ubuf->actime = biggiestruct.properties.st_atime; ubuf->modtime = biggiestruct.properties.st_mtime; utime(outfile_fname, ubuf); } paranoid_free(tmp); paranoid_free(outfile_fname); paranoid_free(command); paranoid_free(partimagehack_command); paranoid_free(sz_devfile); paranoid_free(partimagehack_fifo); g_loglevel = old_loglevel; return (retval); } /************************************************************************** *END_RESTORE_A_BIGGIEFILE_FROM_STREAM * **************************************************************************/ /** * Restore @p tarball_fname from CD. * @param tarball_fname The filename of the tarball to restore (in /mnt/cdrom). * This will be used unmodified. * @param current_tarball_number The number (starting from 0) of the fileset * we're restoring now. * @param filelist The node structure containing the list of files to be * restored. If no file in the afioball is in this list, afio will still be * called, but nothing will be written. * @return 0 for success, nonzero for failure. */ int restore_a_tarball_from_CD(char *tarball_fname, long current_tarball_number, struct s_node *filelist) { int retval = 0; int res; char *p; /** malloc **/ char *command; char *tmp; char *filelist_name; char *filelist_subset_fname; char *executable; char *temp_log; char screen_message[100]; long matches = 0; bool use_star; char *xattr_fname; char *acl_fname; // char files_to_restore_this_time_fname[MAX_STR_LEN]; assert_string_is_neither_NULL_nor_zerolength(tarball_fname); malloc_string(command); malloc_string(tmp); malloc_string(filelist_name); malloc_string(filelist_subset_fname); malloc_string(executable); malloc_string(temp_log); malloc_string(xattr_fname); malloc_string(acl_fname); log_msg(5, "Entering"); filelist_subset_fname[0] = '\0'; use_star = (strstr(tarball_fname, ".star")) ? TRUE : FALSE; // sprintf(files_to_restore_this_time_fname, "/tmp/ftrttf.%d.%d", (int)getpid(), (int)random()); sprintf(command, "mkdir -p %s/tmp", MNT_RESTORING); run_program_and_log_output(command, 9); sprintf(temp_log, "/tmp/%d.%d", (int) (random() % 32768), (int) (random() % 32768)); sprintf(filelist_name, MNT_CDROM "/archives/filelist.%ld", current_tarball_number); if (length_of_file(filelist_name) <= 2) { log_msg(2, "There are _zero_ files in filelist '%s'", filelist_name); log_msg(2, "This is a bit silly (ask Hugo to fix mondo-makefilelist, please)"); log_msg(2, "but it's non-critical. It's cosmetic. Don't worry about it."); retval = 0; goto leave_sub; } if (count_lines_in_file(filelist_name) <= 0 || length_of_file(tarball_fname) <= 0) { log_msg(3, "length_of_file(%s) = %ld", tarball_fname, length_of_file(tarball_fname)); sprintf(tmp, "Unable to restore fileset #%ld (CD I/O error)", current_tarball_number); log_to_screen(tmp); retval = 1; goto leave_sub; } if (filelist) { sprintf(filelist_subset_fname, "/tmp/filelist-subset-%ld.tmp", current_tarball_number); if ((matches = save_filelist_entries_in_common(filelist_name, filelist, filelist_subset_fname, use_star)) <= 0) { sprintf(tmp, "Skipping fileset %ld", current_tarball_number); log_msg(1, tmp); } else { log_msg(3, "Saved fileset %ld's subset to %s", current_tarball_number, filelist_subset_fname); } sprintf(screen_message, "Tarball #%ld --- %ld matches", current_tarball_number, matches); log_to_screen(screen_message); } else { filelist_subset_fname[0] = '\0'; } if (filelist == NULL || matches > 0) { sprintf(xattr_fname, XATTR_LIST_FNAME_RAW_SZ, MNT_CDROM "/archives", current_tarball_number); sprintf(acl_fname, ACL_LIST_FNAME_RAW_SZ, MNT_CDROM "/archives", current_tarball_number); if (strstr(tarball_fname, ".bz2")) { strcpy(executable, "bzip2"); } else if (strstr(tarball_fname, ".lzo")) { strcpy(executable, "lzop"); } else { executable[0] = '\0'; } if (executable[0]) { sprintf(tmp, "which %s > /dev/null 2> /dev/null", executable); if (run_program_and_log_output(tmp, FALSE)) { log_to_screen ("(compare_a_tarball) Compression program not found - oh no!"); paranoid_MR_finish(1); } strcpy(tmp, executable); sprintf(executable, "-P %s -Z", tmp); } #ifdef __FreeBSD__ #define BUFSIZE 512 #else #define BUFSIZE (1024L*1024L)/TAPE_BLOCK_SIZE #endif // if (strstr(tarball_fname, ".star.")) if (use_star) { sprintf(command, "star -x -force-remove -U " STAR_ACL_SZ " errctl= file=%s", tarball_fname); if (strstr(tarball_fname, ".bz2")) { strcat(command, " -bz"); } } else { if (filelist_subset_fname[0] != '\0') { sprintf(command, "afio -i -M 8m -b %ld -c %ld %s -w %s %s", TAPE_BLOCK_SIZE, BUFSIZE, executable, filelist_subset_fname, // files_to_restore_this_time_fname, tarball_fname); } else { sprintf(command, "afio -i -b %ld -c %ld -M 8m %s %s", TAPE_BLOCK_SIZE, BUFSIZE, executable, tarball_fname); } } #undef BUFSIZE sprintf(command + strlen(command), " 2>> %s >> %s", temp_log, temp_log); log_msg(1, "command = '%s'", command); unlink(temp_log); res = system(command); if (res) { p = strstr(command, "-acl "); if (p) { p[0] = p[1] = p[2] = p[3] = ' '; log_msg(1, "new command = '%s'", command); res = system(command); } } if (res && length_of_file(temp_log) < 5) { res = 0; } log_msg(1, "Setting fattr list %s", xattr_fname); if (length_of_file(xattr_fname) > 0) { res = set_fattr_list(filelist_subset_fname, xattr_fname); if (res) { log_to_screen ("Errors occurred while setting extended attributes"); } else { log_msg(1, "I set xattr OK"); } retval += res; } if (length_of_file(acl_fname) > 0) { log_msg(1, "Setting acl list %s", acl_fname); res = set_acl_list(filelist_subset_fname, acl_fname); if (res) { log_to_screen ("Errors occurred while setting access control lists"); } else { log_msg(1, "I set ACL OK"); } retval += res; } if (retval) { sprintf(command, "cat %s >> %s", temp_log, MONDO_LOGFILE); system(command); log_msg(2, "Errors occurred while processing fileset #%d", current_tarball_number); } else { log_msg(2, "Fileset #%d processed OK", current_tarball_number); } } if (does_file_exist("/PAUSE")) { popup_and_OK ("Press ENTER to go on. Delete /PAUSE to stop these pauses."); } unlink(filelist_subset_fname); unlink(xattr_fname); unlink(acl_fname); unlink(temp_log); leave_sub: paranoid_free(command); paranoid_free(tmp); paranoid_free(filelist_name); paranoid_free(filelist_subset_fname); paranoid_free(executable); paranoid_free(temp_log); paranoid_free(xattr_fname); paranoid_free(acl_fname); log_msg(5, "Leaving"); return (retval); } /************************************************************************** *END_RESTORE_A_TARBALL_FROM_CD * **************************************************************************/ /** * Restore a tarball from the currently opened stream. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->backup_media_type * - @c bkpinfo->media_device * - @c bkpinfo->zip_exe * @param tarball_fname The filename of the afioball to restore. * @param current_tarball_number The number (starting from 0) of the fileset * we're restoring now. * @param filelist The node structure containing the list of files to be * restored. If no file in the afioball is in this list, afio will still be * called, but nothing will be written. * @param size The size (in @b bytes) of the afioball. * @return 0 for success, nonzero for failure. */ int restore_a_tarball_from_stream(struct s_bkpinfo *bkpinfo, char *tarball_fname, long current_tarball_number, struct s_node *filelist, long long size, char *xattr_fname, char *acl_fname) { int retval = 0; int res = 0; /** malloc add ***/ char *tmp; char *command; char *afio_fname; char *filelist_fname; char *filelist_subset_fname; char *executable; long matches = 0; bool restore_this_fileset = FALSE; bool use_star; assert(bkpinfo != NULL); assert_string_is_neither_NULL_nor_zerolength(tarball_fname); malloc_string(filelist_subset_fname); malloc_string(filelist_fname); malloc_string(afio_fname); malloc_string(executable); malloc_string(command); malloc_string(tmp); filelist_subset_fname[0] = '\0'; /* to do it with a file... */ use_star = (strstr(tarball_fname, ".star")) ? TRUE : FALSE; sprintf(tmp, "Restoring from fileset #%ld (%ld KB) on %s #%d", current_tarball_number, (long) size >> 10, media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number); log_msg(2, tmp); run_program_and_log_output("mkdir -p " MNT_RESTORING "/tmp", FALSE); /**************************************************************************** * Use RAMDISK's /tmp; saves time; oh wait, it's too small * * Well, pipe from tape to afio, then; oh wait, can't do that either: bug * * in afio or someting; oh darn.. OK, use tmpfs :-) * ****************************************************************************/ filelist_fname[0] = filelist_subset_fname[0] = '\0'; sprintf(afio_fname, "/tmp/tmpfs/archive.tmp.%ld", current_tarball_number); sprintf(filelist_fname, "%s/filelist.%ld", bkpinfo->tmpdir, current_tarball_number); sprintf(filelist_subset_fname, "%s/filelist-subset-%ld.tmp", bkpinfo->tmpdir, current_tarball_number); // sprintf(filelist_fname, "/tmp/tmpfs/temp-filelist.%ld", current_tarball_number); res = read_file_from_stream_to_file(bkpinfo, afio_fname, size); if (strstr(tarball_fname, ".star")) { bkpinfo->use_star = TRUE; } if (res) { log_msg(1, "Warning - error reading afioball from tape"); } if (bkpinfo->compression_level == 0) { executable[0] = '\0'; } else { if (bkpinfo->use_star) { strcpy(executable, " -bz"); } else { sprintf(executable, "-P %s -Z", bkpinfo->zip_exe); } } if (!filelist) // if unconditional restore then restore entire fileset { restore_this_fileset = TRUE; } else // If restoring selectively then get TOC from tarball { if (strstr(tarball_fname, ".star.")) { use_star = TRUE; sprintf(command, "star -t file=%s %s", afio_fname, executable); } else { use_star = FALSE; sprintf(command, "afio -t -M 8m -b %ld %s %s", TAPE_BLOCK_SIZE, executable, afio_fname); } sprintf(command + strlen(command), " > %s 2>> %s", filelist_fname, MONDO_LOGFILE); log_msg(1, "command = %s", command); if (system(command)) { log_msg(4, "Warning - error occurred while retrieving TOC"); } if ((matches = save_filelist_entries_in_common(filelist_fname, filelist, filelist_subset_fname, use_star)) <= 0 || length_of_file(filelist_subset_fname) < 2) { if (length_of_file(filelist_subset_fname) < 2) { log_msg(1, "No matches found in fileset %ld", current_tarball_number); } sprintf(tmp, "Skipping fileset %ld", current_tarball_number); log_msg(2, tmp); restore_this_fileset = FALSE; } else { log_msg(5, "%ld matches. Saved fileset %ld's subset to %s", matches, current_tarball_number, filelist_subset_fname); restore_this_fileset = TRUE; } } // Concoct the call to star/afio to restore files if (strstr(tarball_fname, ".star.")) // star { sprintf(command, "star -x file=%s %s", afio_fname, executable); if (filelist) { sprintf(command + strlen(command), " list=%s", filelist_subset_fname); } } else // afio { sprintf(command, "afio -i -M 8m -b %ld %s", TAPE_BLOCK_SIZE, executable); if (filelist) { sprintf(command + strlen(command), " -w %s", filelist_subset_fname); } sprintf(command + strlen(command), " %s", afio_fname); } sprintf(command + strlen(command), " 2>> %s", MONDO_LOGFILE); // Call if IF there are files to restore (selectively/unconditionally) if (restore_this_fileset) { log_msg(1, "Calling command='%s'", command); paranoid_system(command); iamhere("Restoring xattr, acl stuff"); res = set_fattr_list(filelist_subset_fname, xattr_fname); if (res) { log_msg(1, "Errors occurred while setting xattr"); } else { log_msg(1, "I set xattr OK"); } retval += res; res = set_acl_list(filelist_subset_fname, acl_fname); if (res) { log_msg(1, "Errors occurred while setting ACL"); } else { log_msg(1, "I set ACL OK"); } retval += res; } else { log_msg(1, "NOT CALLING '%s'", command); } if (does_file_exist("/PAUSE") && current_tarball_number >= 50) { log_to_screen("Paused after set %ld", current_tarball_number); popup_and_OK("Pausing. Press ENTER to continue."); } unlink(filelist_subset_fname); unlink(filelist_fname); unlink(afio_fname); paranoid_free(filelist_subset_fname); paranoid_free(filelist_fname); paranoid_free(afio_fname); paranoid_free(command); paranoid_free(tmp); return (retval); } /************************************************************************** *END_RESTORE_A_TARBALL_FROM_STREAM * **************************************************************************/ /** * Restore all biggiefiles from all media in this CD backup. * The CD with the last afioball should be currently mounted. * @param bkpinfo The backup information structure. @c backup_media_type is the * only field used in this function. * @param filelist The node structure containing the list of files to be * restored. If a prospective biggiefile is not in this list, it will be ignored. * @return 0 for success, nonzero for failure. */ int restore_all_biggiefiles_from_CD(struct s_bkpinfo *bkpinfo, struct s_node *filelist) { int retval = 0; int res; long noof_biggiefiles, bigfileno = 0, total_slices; /** malloc **/ char *tmp; bool just_changed_cds = FALSE, finished; char *xattr_fname; char *acl_fname; char *biggies_whose_EXATs_we_should_set; // EXtended ATtributes char *pathname_of_last_biggie_restored; FILE *fbw = NULL; malloc_string(xattr_fname); malloc_string(acl_fname); malloc_string(tmp); malloc_string(biggies_whose_EXATs_we_should_set); malloc_string(pathname_of_last_biggie_restored); assert(bkpinfo != NULL); sprintf(biggies_whose_EXATs_we_should_set, "%s/biggies-whose-EXATs-we-should-set", bkpinfo->tmpdir); if (!(fbw = fopen(biggies_whose_EXATs_we_should_set, "w"))) { log_msg(1, "Warning - cannot openout %s", biggies_whose_EXATs_we_should_set); } read_cfg_var(g_mondo_cfg_file, "total-slices", tmp); total_slices = atol(tmp); sprintf(tmp, "Reassembling large files "); mvaddstr_and_log_it(g_currentY, 0, tmp); if (length_of_file(BIGGIELIST) < 6) { log_msg(1, "OK, no biggielist; not restoring biggiefiles"); return (0); } noof_biggiefiles = count_lines_in_file(BIGGIELIST); if (noof_biggiefiles <= 0) { log_msg(2, "OK, no biggiefiles in biggielist; not restoring biggiefiles"); return (0); } sprintf(tmp, "OK, there are %ld biggiefiles in the archives", noof_biggiefiles); log_msg(2, tmp); open_progress_form("Reassembling large files", "I am now reassembling all the large files.", "Please wait. This may take some time.", "", total_slices); for (bigfileno = 0, finished = FALSE; !finished;) { log_msg(2, "Thinking about restoring bigfile %ld", bigfileno + 1); if (!does_file_exist(slice_fname(bigfileno, 0, ARCHIVES_PATH, ""))) { log_msg(3, "...but its first slice isn't on this CD. Perhaps this was a selective restore?"); log_msg(3, "Cannot find bigfile #%ld 's first slice on %s #%d", bigfileno + 1, media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number); log_msg(3, "Slicename would have been %s", slice_fname(bigfileno + 1, 0, ARCHIVES_PATH, "")); // I'm not positive 'just_changed_cds' is even necessary... if (just_changed_cds) { just_changed_cds = FALSE; log_msg(3, "I'll continue to scan this CD for bigfiles to be restored."); } else if (does_file_exist(MNT_CDROM "/archives/NOT-THE-LAST")) { insist_on_this_cd_number(bkpinfo, ++g_current_media_number); sprintf(tmp, "Restoring from %s #%d", media_descriptor_string(bkpinfo-> backup_media_type), g_current_media_number); log_to_screen(tmp); just_changed_cds = TRUE; } else { log_msg(2, "There was no bigfile #%ld. That's OK.", bigfileno + 1); log_msg(2, "I'm going to stop restoring bigfiles now."); finished = TRUE; } } else { just_changed_cds = FALSE; sprintf(tmp, "Restoring big file %ld", bigfileno + 1); update_progress_form(tmp); res = restore_a_biggiefile_from_CD(bkpinfo, bigfileno, filelist, pathname_of_last_biggie_restored); iamhere(pathname_of_last_biggie_restored); if (fbw && pathname_of_last_biggie_restored[0]) { fprintf(fbw, "%s\n", pathname_of_last_biggie_restored); } retval += res; bigfileno++; } } if (fbw) { fclose(fbw); sprintf(acl_fname, ACL_BIGGLST_FNAME_RAW_SZ, ARCHIVES_PATH); sprintf(xattr_fname, XATTR_BIGGLST_FNAME_RAW_SZ, ARCHIVES_PATH); if (length_of_file(acl_fname) > 0 && find_home_of_exe("setfacl")) { set_acl_list(biggies_whose_EXATs_we_should_set, acl_fname); } if (length_of_file(xattr_fname) > 0 && find_home_of_exe("setfattr")) { set_fattr_list(biggies_whose_EXATs_we_should_set, xattr_fname); } } if (does_file_exist("/PAUSE")) { popup_and_OK ("Press ENTER to go on. Delete /PAUSE to stop these pauses."); } close_progress_form(); if (retval) { mvaddstr_and_log_it(g_currentY++, 74, "Errors."); } else { mvaddstr_and_log_it(g_currentY++, 74, "Done."); } paranoid_free(xattr_fname); paranoid_free(acl_fname); paranoid_free(tmp); paranoid_free(biggies_whose_EXATs_we_should_set); paranoid_free(pathname_of_last_biggie_restored); return (retval); } /************************************************************************** *END_RESTORE_ALL_BIGGIFILES_FROM_CD * **************************************************************************/ /** * Restore all afioballs from all CDs in the backup. * The first CD should be inserted (if not, it will be asked for). * @param bkpinfo The backup information structure. @c backup_media_type is the * only field used in @e this function. * @param filelist The node structure containing the list of files to be * restored. If no file in some particular afioball is in this list, afio will * still be called for that fileset, but nothing will be written. * @return 0 for success, or the number of filesets that failed. */ int restore_all_tarballs_from_CD(struct s_bkpinfo *bkpinfo, struct s_node *filelist) { int retval = 0; int res; int attempts; long current_tarball_number = 0; long max_val; /**malloc ***/ char *tmp; char *tarball_fname; char *progress_str; char *comment; malloc_string(tmp); malloc_string(tarball_fname); malloc_string(progress_str); malloc_string(comment); assert(bkpinfo != NULL); mvaddstr_and_log_it(g_currentY, 0, "Restoring from archives"); log_msg(2, "Insisting on 1st CD, so that I can have a look at LAST-FILELIST-NUMBER"); if (g_current_media_number != 1) { log_msg(3, "OK, that's jacked up."); g_current_media_number = 1; } insist_on_this_cd_number(bkpinfo, g_current_media_number); read_cfg_var(g_mondo_cfg_file, "last-filelist-number", tmp); max_val = atol(tmp) + 1; sprintf(progress_str, "Restoring from %s #%d", media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number); log_to_screen(progress_str); open_progress_form("Restoring from archives", "Restoring data from the archives.", "Please wait. This may take some time.", progress_str, max_val); for (;;) { insist_on_this_cd_number(bkpinfo, g_current_media_number); update_progress_form(progress_str); sprintf(tarball_fname, MNT_CDROM "/archives/%ld.afio.bz2", current_tarball_number); if (!does_file_exist(tarball_fname)) { sprintf(tarball_fname, MNT_CDROM "/archives/%ld.afio.lzo", current_tarball_number); } if (!does_file_exist(tarball_fname)) { sprintf(tarball_fname, MNT_CDROM "/archives/%ld.afio.", current_tarball_number); } if (!does_file_exist(tarball_fname)) { sprintf(tarball_fname, MNT_CDROM "/archives/%ld.star.bz2", current_tarball_number); } if (!does_file_exist(tarball_fname)) { sprintf(tarball_fname, MNT_CDROM "/archives/%ld.star.", current_tarball_number); } if (!does_file_exist(tarball_fname)) { if (current_tarball_number == 0) { log_to_screen ("No tarballs. Strange. Maybe you only backed up freakin' big files?"); return (0); } if (!does_file_exist(MNT_CDROM "/archives/NOT-THE-LAST") || system("find " MNT_CDROM "/archives/slice* > /dev/null 2> /dev/null") == 0) { break; } g_current_media_number++; sprintf(progress_str, "Restoring from %s #%d", media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number); log_to_screen(progress_str); } else { sprintf(progress_str, "Restoring from fileset #%ld on %s #%d", current_tarball_number, media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number); // log_msg(3, "progress_str = %s", progress_str); for (res = 999, attempts = 0; attempts < 3 && res != 0; attempts++) { res = restore_a_tarball_from_CD(tarball_fname, current_tarball_number, filelist); } sprintf(tmp, "%s #%d, fileset #%ld - restore ", media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number, current_tarball_number); if (res) { strcat(tmp, "reported errors"); } else if (attempts > 1) { strcat(tmp, "succeeded"); } else { strcat(tmp, "succeeded"); } if (attempts > 1) { sprintf(tmp + strlen(tmp), " (%d attempts) - review logs", attempts); } strcpy(comment, tmp); if (attempts > 1) { log_to_screen(comment); } retval += res; current_tarball_number++; g_current_progress++; } } close_progress_form(); if (retval) { mvaddstr_and_log_it(g_currentY++, 74, "Errors."); } else { mvaddstr_and_log_it(g_currentY++, 74, "Done."); } paranoid_free(tmp); paranoid_free(tarball_fname); paranoid_free(progress_str); paranoid_free(comment); return (retval); } /************************************************************************** *END_RESTORE_ALL_TARBALLS_FROM_CD * **************************************************************************/ /** * Restore all biggiefiles from the currently opened stream. * @param bkpinfo The backup information structure. Passed to other functions. * @param filelist The node structure containing the list of files to be * restored. If a prospective biggiefile is not in the list, it will be ignored. * @return 0 for success, or the number of biggiefiles that failed. */ int restore_all_biggiefiles_from_stream(struct s_bkpinfo *bkpinfo, struct s_node *filelist) { long noof_biggiefiles; long current_bigfile_number = 0; long total_slices; int retval = 0; int res = 0; int ctrl_chr; /** malloc add ****/ char *tmp; char *biggie_fname; char *biggie_cksum; char *xattr_fname; char *acl_fname; char *p; char *pathname_of_last_biggie_restored; char *biggies_whose_EXATs_we_should_set; // EXtended ATtributes long long biggie_size; FILE *fbw = NULL; malloc_string(tmp); malloc_string(biggie_fname); malloc_string(biggie_cksum); malloc_string(xattr_fname); malloc_string(acl_fname); malloc_string(biggies_whose_EXATs_we_should_set); malloc_string(pathname_of_last_biggie_restored); assert(bkpinfo != NULL); read_cfg_var(g_mondo_cfg_file, "total-slices", tmp); total_slices = atol(tmp); sprintf(tmp, "Reassembling large files "); sprintf(xattr_fname, XATTR_BIGGLST_FNAME_RAW_SZ, bkpinfo->tmpdir); sprintf(acl_fname, ACL_BIGGLST_FNAME_RAW_SZ, bkpinfo->tmpdir); mvaddstr_and_log_it(g_currentY, 0, tmp); sprintf(biggies_whose_EXATs_we_should_set, "%s/biggies-whose-EXATs-we-should-set", bkpinfo->tmpdir); if (!(fbw = fopen(biggies_whose_EXATs_we_should_set, "w"))) { log_msg(1, "Warning - cannot openout %s", biggies_whose_EXATs_we_should_set); } // get xattr and acl files if they're there res = read_header_block_from_stream(&biggie_size, biggie_fname, &ctrl_chr); if (ctrl_chr == BLK_START_EXTENDED_ATTRIBUTES) { res = read_EXAT_files_from_tape(bkpinfo, &biggie_size, biggie_fname, &ctrl_chr, xattr_fname, acl_fname); } noof_biggiefiles = atol(biggie_fname); sprintf(tmp, "OK, there are %ld biggiefiles in the archives", noof_biggiefiles); log_msg(2, tmp); open_progress_form("Reassembling large files", "I am now reassembling all the large files.", "Please wait. This may take some time.", "", total_slices); for (res = read_header_block_from_stream(&biggie_size, biggie_fname, &ctrl_chr); ctrl_chr != BLK_STOP_BIGGIEFILES; res = read_header_block_from_stream(&biggie_size, biggie_fname, &ctrl_chr)) { if (ctrl_chr != BLK_START_A_NORMBIGGIE && ctrl_chr != BLK_START_A_PIHBIGGIE) { wrong_marker(BLK_START_A_NORMBIGGIE, ctrl_chr); } p = strrchr(biggie_fname, '/'); if (!p) { p = biggie_fname; } else { p++; } sprintf(tmp, "Restoring big file %ld (%lld K)", current_bigfile_number + 1, biggie_size / 1024); update_progress_form(tmp); res = restore_a_biggiefile_from_stream(bkpinfo, biggie_fname, current_bigfile_number, biggie_cksum, biggie_size, filelist, ctrl_chr, pathname_of_last_biggie_restored); log_msg(1, "I believe I have restored %s", pathname_of_last_biggie_restored); if (fbw && pathname_of_last_biggie_restored[0]) { fprintf(fbw, "%s\n", pathname_of_last_biggie_restored); } retval += res; current_bigfile_number++; } if (current_bigfile_number != noof_biggiefiles && noof_biggiefiles != 0) { sprintf(tmp, "Warning - bigfileno=%ld but noof_biggiefiles=%ld\n", current_bigfile_number, noof_biggiefiles); } else { sprintf(tmp, "%ld biggiefiles in biggielist.txt; %ld biggiefiles processed today.", noof_biggiefiles, current_bigfile_number); } log_msg(1, tmp); if (fbw) { fclose(fbw); if (length_of_file(biggies_whose_EXATs_we_should_set) > 2) { iamhere("Setting biggie-EXATs"); if (length_of_file(acl_fname) > 0) { log_msg(1, "set_acl_list(%s,%s)", biggies_whose_EXATs_we_should_set, acl_fname); set_acl_list(biggies_whose_EXATs_we_should_set, acl_fname); } if (length_of_file(xattr_fname) > 0) { log_msg(1, "set_fattr_List(%s,%s)", biggies_whose_EXATs_we_should_set, xattr_fname); set_fattr_list(biggies_whose_EXATs_we_should_set, xattr_fname); } } else { iamhere ("No biggiefiles selected. So, no biggie-EXATs to set."); } } if (does_file_exist("/PAUSE")) { popup_and_OK ("Press ENTER to go on. Delete /PAUSE to stop these pauses."); } close_progress_form(); if (retval) { mvaddstr_and_log_it(g_currentY++, 74, "Errors."); } else { mvaddstr_and_log_it(g_currentY++, 74, "Done."); } paranoid_free(biggies_whose_EXATs_we_should_set); paranoid_free(pathname_of_last_biggie_restored); paranoid_free(biggie_fname); paranoid_free(biggie_cksum); paranoid_free(xattr_fname); paranoid_free(acl_fname); paranoid_free(tmp); return (retval); } /************************************************************************** *END_RESTORE_ALL_BIGGIEFILES_FROM_STREAM * **************************************************************************/ /** * Restore all afioballs from the currently opened tape stream. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->backup_media_type * - @c bkpinfo->restore_path * @param filelist The node structure containing the list of files to be * restored. If no file in an afioball is in this list, afio will still be * called for that fileset, but nothing will be written. * @return 0 for success, or the number of filesets that failed. */ int restore_all_tarballs_from_stream(struct s_bkpinfo *bkpinfo, struct s_node *filelist) { int retval = 0; int res; long current_afioball_number = 0; int ctrl_chr; long max_val /*, total_noof_files */ ; /** malloc **/ char *tmp; char *progress_str; char *tmp_fname; char *xattr_fname; char *acl_fname; long long tmp_size; malloc_string(tmp); malloc_string(progress_str); malloc_string(tmp_fname); assert(bkpinfo != NULL); malloc_string(xattr_fname); malloc_string(acl_fname); mvaddstr_and_log_it(g_currentY, 0, "Restoring from archives"); read_cfg_var(g_mondo_cfg_file, "last-filelist-number", tmp); max_val = atol(tmp) + 1; chdir(bkpinfo->restore_path); /* I don't know why this is needed _here_ but it seems to be. -HR, 02/04/2002 */ run_program_and_log_output("pwd", 5); sprintf(progress_str, "Restoring from media #%d", g_current_media_number); log_to_screen(progress_str); open_progress_form("Restoring from archives", "Restoring data from the archives.", "Please wait. This may take some time.", progress_str, max_val); log_msg(3, "hey"); res = read_header_block_from_stream(&tmp_size, tmp_fname, &ctrl_chr); if (res) { log_msg(2, "Warning - error reading afioball from tape"); } retval += res; if (ctrl_chr != BLK_START_AFIOBALLS) { wrong_marker(BLK_START_AFIOBALLS, ctrl_chr); } log_msg(2, "ho"); res = read_header_block_from_stream(&tmp_size, tmp_fname, &ctrl_chr); while (ctrl_chr != BLK_STOP_AFIOBALLS) { update_progress_form(progress_str); sprintf(xattr_fname, "%s/xattr-subset-%ld.tmp", bkpinfo->tmpdir, current_afioball_number); sprintf(acl_fname, "%s/acl-subset-%ld.tmp", bkpinfo->tmpdir, current_afioball_number); unlink(xattr_fname); unlink(acl_fname); if (ctrl_chr == BLK_START_EXTENDED_ATTRIBUTES) { iamhere("Reading EXAT files from tape"); res = read_EXAT_files_from_tape(bkpinfo, &tmp_size, tmp_fname, &ctrl_chr, xattr_fname, acl_fname); } if (ctrl_chr != BLK_START_AN_AFIO_OR_SLICE) { wrong_marker(BLK_START_AN_AFIO_OR_SLICE, ctrl_chr); } sprintf(tmp, "Restoring from fileset #%ld (name=%s, size=%ld K)", current_afioball_number, tmp_fname, (long) tmp_size >> 10); res = restore_a_tarball_from_stream(bkpinfo, tmp_fname, current_afioball_number, filelist, tmp_size, xattr_fname, acl_fname); retval += res; if (res) { sprintf(tmp, "Fileset %ld - errors occurred", current_afioball_number); log_to_screen(tmp); } res = read_header_block_from_stream(&tmp_size, tmp_fname, &ctrl_chr); if (ctrl_chr != BLK_STOP_AN_AFIO_OR_SLICE) { wrong_marker(BLK_STOP_AN_AFIO_OR_SLICE, ctrl_chr); } current_afioball_number++; g_current_progress++; sprintf(progress_str, "Restoring from fileset #%ld on %s #%d", current_afioball_number, media_descriptor_string(bkpinfo->backup_media_type), g_current_media_number); res = read_header_block_from_stream(&tmp_size, tmp_fname, &ctrl_chr); unlink(xattr_fname); unlink(acl_fname); } // next log_msg(1, "All done with afioballs"); close_progress_form(); if (retval) { mvaddstr_and_log_it(g_currentY++, 74, "Errors."); } else { mvaddstr_and_log_it(g_currentY++, 74, "Done."); } paranoid_free(tmp); paranoid_free(progress_str); paranoid_free(tmp_fname); paranoid_free(xattr_fname); paranoid_free(acl_fname); return (retval); } /************************************************************************** *END_ RESTORE_ALL_TARBALLS_FROM_STREAM * **************************************************************************/ /* @} - end of LLrestoreGroup */ /** * Restore all files in @p filelist. * @param bkpinfo The backup information structure. Most fields are used. * @param filelist The node structure containing the list of files to be * restored. * @return 0 for success, or the number of afioballs and biggiefiles that failed. * @ingroup restoreGroup */ int restore_everything(struct s_bkpinfo *bkpinfo, struct s_node *filelist) { int resA; int resB; /** mallco ***/ char *cwd; char *newpath; char *tmp; assert(bkpinfo != NULL); malloc_string(cwd); malloc_string(newpath); malloc_string(tmp); log_msg(2, "restore_everything() --- starting"); g_current_media_number = 1; getcwd(cwd, MAX_STR_LEN - 1); sprintf(tmp, "mkdir -p %s", bkpinfo->restore_path); run_program_and_log_output(tmp, FALSE); log_msg(1, "Changing dir to %s", bkpinfo->restore_path); chdir(bkpinfo->restore_path); getcwd(newpath, MAX_STR_LEN - 1); log_msg(1, "path is now %s", newpath); log_msg(1, "restoring everything"); if (!find_home_of_exe("petris") && !g_text_mode) { newtDrawRootText(0, g_noof_rows - 2, "Press ALT- twice to play Petris :-) "); newtRefresh(); } mvaddstr_and_log_it(g_currentY, 0, "Preparing to read your archives"); if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) { mount_cdrom(bkpinfo); mvaddstr_and_log_it(g_currentY++, 0, "Restoring OS and data from streaming media"); if (bkpinfo->backup_media_type == cdstream) { openin_cdstream(bkpinfo); } else { assert_string_is_neither_NULL_nor_zerolength(bkpinfo-> media_device); openin_tape(bkpinfo); } resA = restore_all_tarballs_from_stream(bkpinfo, filelist); resB = restore_all_biggiefiles_from_stream(bkpinfo, filelist); if (bkpinfo->backup_media_type == cdstream) { closein_cdstream(bkpinfo); } else { closein_tape(bkpinfo); } } else { mvaddstr_and_log_it(g_currentY++, 0, "Restoring OS and data from CD "); mount_cdrom(bkpinfo); resA = restore_all_tarballs_from_CD(bkpinfo, filelist); resB = restore_all_biggiefiles_from_CD(bkpinfo, filelist); } chdir(cwd); if (resA + resB) { log_to_screen("Errors occurred while data was being restored."); } if (length_of_file("/etc/raidtab") > 0) { log_msg(2, "Copying local raidtab to restored filesystem"); run_program_and_log_output("cp -f /etc/raidtab " MNT_RESTORING "/etc/raidtab", FALSE); } kill_petris(); log_msg(2, "restore_everything() --- leaving"); paranoid_free(cwd); paranoid_free(newpath); paranoid_free(tmp); return (resA + resB); } /************************************************************************** *END_RESTORE_EVERYTHING * **************************************************************************/ /** * @brief Haha. You wish! (This function is not implemented :-) */ int restore_live_from_monitas_server(struct s_bkpinfo *bkpinfo, char *monitas_device, char *restore_this_directory, char *restore_here) /* NB: bkpinfo hasn't been populated yet, except for ->tmp which is "/tmp" */ { FILE *fout; int retval = 0; int i; int j; struct mountlist_itself the_mountlist; static struct raidlist_itself the_raidlist; /** malloc **/ char tmp[MAX_STR_LEN + 1]; char command[MAX_STR_LEN + 1]; char datablock[256 * 1024]; char datadisks_fname[MAX_STR_LEN + 1]; long k; long length; long long llt; struct s_node *filelist = NULL; assert(bkpinfo != NULL); assert_string_is_neither_NULL_nor_zerolength(monitas_device); assert(restore_this_directory != NULL); assert(restore_here != NULL); sprintf(tmp, "restore_here = '%s'", restore_here); log_msg(2, tmp); log_msg(2, "restore_live_from_monitas_server() - starting"); unlink("/tmp/mountlist.txt"); unlink("/tmp/filelist.full"); unlink("/tmp/biggielist.txt"); if (restore_here[0] == '\0') { strcpy(bkpinfo->restore_path, MNT_RESTORING); } else { strcpy(bkpinfo->restore_path, restore_here); } log_msg(3, "FYI FYI FYI FYI FYI FYI FYI FYI FYI FYI FYI"); sprintf(tmp, "FYI - data will be restored to %s", bkpinfo->restore_path); log_msg(3, tmp); log_msg(3, "FYI FYI FYI FYI FYI FYI FYI FYI FYI FYI FYI"); sprintf(datadisks_fname, "/tmp/mondorestore.datadisks.%d", (int) (random() % 32768)); chdir(bkpinfo->tmpdir); sprintf(command, "cat %s", monitas_device); g_tape_stream = popen(command, "r"); // for compatibility with openin_tape() if (!(fout = fopen(datadisks_fname, "w"))) { log_OS_error(datadisks_fname); return (1); } for (i = 0; i < 32; i++) { for (j = 0; j < 4; j++) { for (length = k = 0; length < 256 * 1024; length += k) { k = fread(datablock + length, 1, 256 * 1024 - length, g_tape_stream); } fwrite(datablock, 1, length, fout); g_tape_posK += length; } } paranoid_fclose(fout); sprintf(command, "tar -zxvf %s tmp/mondo-restore.cfg tmp/mountlist.txt tmp/filelist.full tmp/biggielist.txt", datadisks_fname); run_program_and_log_output(command, 4); read_header_block_from_stream(&llt, tmp, &i); read_header_block_from_stream(&llt, tmp, &i); unlink(datadisks_fname); read_cfg_file_into_bkpinfo(g_mondo_cfg_file, bkpinfo); retval = load_mountlist(&the_mountlist, g_mountlist_fname); // in case read_cfg_file_into_bkpinfo strcpy(bkpinfo->media_device, monitas_device); load_raidtab_into_raidlist(&the_raidlist, RAIDTAB_FNAME); iamhere("FIXME"); fatal_error("This will fail"); sprintf(command, "cat %s | grep -x \"%s.*\" > %s", g_filelist_full, restore_this_directory, g_filelist_full); if (system(command)) { retval++; log_to_screen ("Error(s) occurred while processing filelist and wildcard"); } iamhere("FIXME"); fatal_error("This will fail"); sprintf(command, "cat %s | grep -x \"%s.*\" > %s", g_biggielist_txt, restore_this_directory, g_biggielist_txt); if (system(command)) { log_msg(1, "Error(s) occurred while processing biggielist and wildcard"); } sprintf(command, "touch %s", g_biggielist_txt); run_program_and_log_output(command, FALSE); // filelist = load_filelist(g_filelist_restthese); // FIXME --- this probably doesn't work because it doesn't include the biggiefiles retval += restore_everything(bkpinfo, filelist); free_filelist(filelist); log_msg(2, "--------End of restore_live_from_monitas_server--------"); return (retval); } /************************************************************************** *END_RESTORE_LIVE_FROM_MONITAS_SERVER * **************************************************************************/ extern void wait_until_software_raids_are_prepped(char *, int); char which_restore_mode(void); /** * Log a "don't panic" message to the logfile. */ void welcome_to_mondorestore() { log_msg(0, "-------------- Mondo Restore v%s -------------", VERSION); log_msg(0, "DON'T PANIC! Mondorestore logs almost everything, so please "); log_msg(0, "don't break out in a cold sweat just because you see a few "); log_msg(0, "error messages in the log. Read them; analyze them; see if "); log_msg(0, "they are significant; above all, verify your backups! Please"); log_msg(0, "attach a compressed copy of this log to any e-mail you send "); log_msg(0, "to the Mondo mailing list when you are seeking technical "); log_msg(0, "support. Without it, we can't help you. - Hugo"); log_msg(0, "------------------------------------------------------------"); log_msg(0, "BTW, despite (or perhaps because of) the wealth of messages,"); log_msg(0, "some users are inclined to stop reading this log. If Mondo "); log_msg(0, "stopped for some reason, chances are it's detailed here. "); log_msg(0, "More than likely there's a message at the very end of this "); log_msg(0, "log that will tell you what is wrong. Please read it! "); log_msg(0, "------------------------------------------------------------"); } /** * Restore the user's data. * What did you think it did, anyway? :-) */ int main(int argc, char *argv[]) { FILE *fin; FILE *fout; int retval = 0; int res; // int c; char *tmp; #ifndef VERSION #define VERSION "1.7x_cvs" #endif struct mountlist_itself *mountlist; struct raidlist_itself *raidlist; struct s_bkpinfo *bkpinfo; struct s_node *filelist; char *a, *b; /************************************************************************** * hugo- * * busy stuff here - it needs some comments -stan * * * **************************************************************************/ if (getuid() != 0) { fprintf(stderr, "Please run as root.\r\n"); exit(127); } g_loglevel = DEFAULT_MR_LOGLEVEL; malloc_string(tmp); /* Configure global variables */ #ifdef __FreeBSD__ if (strstr (call_program_and_get_last_line_of_output("cat /tmp/cmdline"), "textonly")) #else if (strstr (call_program_and_get_last_line_of_output("cat /proc/cmdline"), "textonly")) #endif { g_text_mode = TRUE; log_msg(1, "TEXTONLY MODE"); } else { g_text_mode = FALSE; } // newt :-) if (! (bkpinfo = g_bkpinfo_DONTUSETHIS = malloc(sizeof(struct s_bkpinfo)))) { fatal_error("Cannot malloc bkpinfo"); } if (!(mountlist = malloc(sizeof(struct mountlist_itself)))) { fatal_error("Cannot malloc mountlist"); } if (!(raidlist = malloc(sizeof(struct raidlist_itself)))) { fatal_error("Cannot malloc raidlist"); } malloc_libmondo_global_strings(); asprintf(&g_mondo_home, call_program_and_get_last_line_of_output("which mondorestore")); sprintf(g_tmpfs_mountpt, "/tmp/tmpfs"); make_hole_for_dir(g_tmpfs_mountpt); g_current_media_number = 1; // precaution run_program_and_log_output("mkdir -p " MNT_CDROM, FALSE); run_program_and_log_output("mkdir -p /mnt/floppy", FALSE); malloc_string(tmp); malloc_string(a); malloc_string(b); setup_MR_global_filenames(bkpinfo); // malloc() and set globals, using bkpinfo->tmpdir etc. reset_bkpinfo(bkpinfo); bkpinfo->backup_media_type = none; // in case boot disk was made for one backup type but user wants to restore from another backup type bkpinfo->restore_data = TRUE; // Well, yeah :-) if (am_I_in_disaster_recovery_mode()) { run_program_and_log_output("mount / -o remount,rw", 2); } // for b0rken distros g_main_pid = getpid(); srandom((int) (time(NULL))); register_pid(getpid(), "mondo"); set_signals(TRUE); g_kernel_version = get_kernel_version(); log_msg(1, "FYI - g_mountlist_fname = %s", g_mountlist_fname); if (strlen(g_mountlist_fname) < 3) { fatal_error ("Serious error in malloc()'ing. Could be a bug in your glibc."); } mkdir(MNT_CDROM, 0x770); /* Backup original mountlist.txt */ sprintf(tmp, "%s.orig", g_mountlist_fname); if (!does_file_exist(g_mountlist_fname)) { log_msg(2, "%ld: Warning - g_mountlist_fname (%s) does not exist yet", __LINE__, g_mountlist_fname); } else if (!does_file_exist(tmp)) { sprintf(tmp, "cp -f %s %s.orig", g_mountlist_fname, g_mountlist_fname); run_program_and_log_output(tmp, FALSE); } /* Init directories */ make_hole_for_dir(bkpinfo->tmpdir); sprintf(tmp, "mkdir -p %s", bkpinfo->tmpdir); run_program_and_log_output(tmp, FALSE); make_hole_for_dir("/var/log"); make_hole_for_dir("/tmp/tmpfs"); /* just in case... */ run_program_and_log_output("umount " MNT_CDROM, FALSE); run_program_and_log_output ("ln -sf /var/log/mondo-archive.log /tmp/mondo-restore.log", FALSE); run_program_and_log_output("rm -Rf /tmp/tmpfs/mondo.tmp.*", FALSE); /* Init GUI */ malloc_libmondo_global_strings(); setup_newt_stuff(); /* call newtInit and setup screen log */ welcome_to_mondorestore(); if (bkpinfo->disaster_recovery) { log_msg(1, "I am in disaster recovery mode"); } else { log_msg(1, "I am in normal, live mode"); } iamhere("what time is it"); /* Process command-line parameters */ if (argc == 2 && strcmp(argv[1], "--edit-mountlist") == 0) { #ifdef __FreeBSD__ system("mv -f /tmp/raidconf.txt /etc/raidtab"); if (!does_file_exist("/etc/raidtab")) system("vinum printconfig > /etc/raidtab"); #endif load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME); if (!does_file_exist(g_mountlist_fname)) { strcpy(g_mountlist_fname, "/tmp/mountlist.txt"); } res = let_user_edit_the_mountlist(bkpinfo, mountlist, raidlist); #ifdef __FreeBSD__ system("mv -f /etc/raidtab /tmp/raidconf.txt"); #endif paranoid_MR_finish(res); } g_loglevel = DEFAULT_MR_LOGLEVEL; if (argc == 3 && strcmp(argv[1], "--echo-to-screen") == 0) { fout = fopen("/tmp/out.txt", "w"); fput_string_one_char_at_a_time(stderr, argv[2]); finish(0); } if (argc == 3 && strcmp(argv[1], "--gendf") == 0) { make_grub_install_scriptlet(argv[2]); finish(0); } if (argc >= 2 && strcmp(argv[1], "--pih") == 0) { if (system("mount | grep cdrom 2> /dev/null > /dev/null")) { system("mount " MNT_CDROM); } bkpinfo->compression_level = 1; g_current_media_number = 2; strcpy(bkpinfo->restore_path, "/tmp/TESTING"); bkpinfo->backup_media_type = dvd; open_progress_form("Reassembling /dev/hda1", "Shark is a bit of a silly person.", "Please wait. This may take some time.", "", 1999); system("rm -Rf /tmp/*pih*"); restore_a_biggiefile_from_CD(bkpinfo, 42, NULL, tmp); } if (argc == 5 && strcmp(argv[1], "--common") == 0) { g_loglevel = 6; filelist = load_filelist(argv[2]); if (!filelist) { fatal_error("Failed to load filelist"); } toggle_node_selection(filelist, FALSE); toggle_all_root_dirs_on(filelist); toggle_path_selection(filelist, "/usr/share", TRUE); // show_filelist(filelist); save_filelist(filelist, "/tmp/out.txt"); // finish(0); // toggle_path_selection (filelist, "/root/stuff", TRUE); strcpy(a, argv[3]); strcpy(b, argv[4]); res = save_filelist_entries_in_common(a, filelist, b, FALSE); free_filelist(filelist); printf("res = %d", res); finish(0); } if (argc == 3 && strcmp(argv[1], "--popuplist") == 0) { popup_changelist_from_file(argv[2]); paranoid_MR_finish(0); } if (argc == 5 && strcmp(argv[1], "--copy") == 0) { log_msg(1, "SCORE"); g_loglevel = 10; if (strstr(argv[2], "save")) { log_msg(1, "Saving from %s to %s", argv[3], argv[4]); fin = fopen(argv[3], "r"); fout = fopen(argv[4], "w"); copy_from_src_to_dest(fin, fout, 'w'); fclose(fin); fin = fopen(argv[3], "r"); copy_from_src_to_dest(fin, fout, 'w'); fclose(fout); fclose(fin); } else if (strstr(argv[2], "restore")) { fout = fopen(argv[3], "w"); fin = fopen(argv[4], "r"); copy_from_src_to_dest(fout, fin, 'r'); fclose(fin); fin = fopen(argv[4], "r"); copy_from_src_to_dest(fout, fin, 'r'); fclose(fout); fclose(fin); } else { fatal_error("Unknown additional param"); } finish(0); } if (argc == 3 && strcmp(argv[1], "--mdstat") == 0) { wait_until_software_raids_are_prepped(argv[2], 100); finish(0); } if (argc == 4 && strcmp(argv[1], "--mdconv") == 0) { finish(create_raidtab_from_mdstat(argv[2], argv[3])); } if (argc == 2 && strcmp(argv[1], "--live-grub") == 0) { retval = run_grub(FALSE, "/dev/hda"); if (retval) { log_to_screen("Failed to write Master Boot Record"); } paranoid_MR_finish(0); } if (argc == 3 && strcmp(argv[1], "--paa") == 0) { g_current_media_number = atoi(argv[2]); pause_and_ask_for_cdr(5, NULL); paranoid_MR_finish(0); } else if (!bkpinfo->disaster_recovery) { // live! if (argc != 1) { popup_and_OK ("Live mode doesn't support command-line parameters yet."); paranoid_MR_finish(1); // return(1); } log_msg(1, "I am in normal, live mode."); log_msg(2, "FYI, MOUNTLIST_FNAME = %s", g_mountlist_fname); mount_boot_if_necessary(); /* for Gentoo users */ log_msg(2, "Still here."); if (argc > 1 && strcmp(argv[argc - 1], "--live-from-cd") == 0) { g_restoring_live_from_cd = TRUE; } if (argc == 5 && strcmp(argv[1], "--monitas-live") == 0) { retval = restore_live_from_monitas_server(bkpinfo, argv[2], argv[3], argv[4]); } else { log_msg(2, "Calling restore_to_live_filesystem()"); retval = restore_to_live_filesystem(bkpinfo); } log_msg(2, "Still here. Yay."); if (strlen(bkpinfo->tmpdir) > 0) { sprintf(tmp, "rm -Rf %s/*", bkpinfo->tmpdir); run_program_and_log_output(tmp, FALSE); } unmount_boot_if_necessary(); /* for Gentoo users */ paranoid_MR_finish(retval); } else { /* Disaster recovery mode (must be) */ log_msg(1, "I must be in disaster recovery mode."); log_msg(2, "FYI, MOUNTLIST_FNAME = %s ", g_mountlist_fname); if (argc == 3 && strcmp(argv[1], "--monitas-memorex") == 0) { log_to_screen("Uh, that hasn't been implemented yet."); paranoid_MR_finish(1); } iamhere("About to call load_mountlist and load_raidtab"); strcpy(bkpinfo->restore_path, MNT_RESTORING); read_cfg_file_into_bkpinfo(g_mondo_cfg_file, bkpinfo); retval = load_mountlist(mountlist, g_mountlist_fname); retval += load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME); iamhere ("Returned from calling load_mountlist and load_raidtab successfully"); if (argc > 1 && (strcmp(argv[1], "--compare") == 0 || strcmp(argv[1], "--nuke") == 0)) { if (bkpinfo->backup_media_type == nfs && !is_this_device_mounted(bkpinfo->nfs_mount)) { log_msg(1, "Mounting nfs dir"); sprintf(bkpinfo->isodir, "/tmp/isodir"); run_program_and_log_output("mkdir -p /tmp/isodir", 5); sprintf(tmp, "mount %s -t nfs -o nolock /tmp/isodir", bkpinfo->nfs_mount); run_program_and_log_output(tmp, 1); } } if (retval) { log_to_screen ("Warning - load_raidtab_into_raidlist returned an error"); } log_msg(1, "Send in the clowns."); if (argc == 2 && strcmp(argv[1], "--partition-only") == 0) { log_msg(0, "Partitioning only."); load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME); strcpy(g_mountlist_fname, "/tmp/mountlist.txt"); load_mountlist(mountlist, g_mountlist_fname); res = partition_everything(mountlist); finish(res); } if (argc == 2 && strcmp(argv[1], "--format-only") == 0) { log_msg(0, "Formatting only."); load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME); strcpy(g_mountlist_fname, "/tmp/mountlist.txt"); load_mountlist(mountlist, g_mountlist_fname); res = format_everything(mountlist, FALSE); finish(res); } if (argc == 2 && strcmp(argv[1], "--stop-lvm-and-raid") == 0) { log_msg(0, "Stopping LVM and RAID"); load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME); strcpy(g_mountlist_fname, "/tmp/mountlist.txt"); load_mountlist(mountlist, g_mountlist_fname); res = do_my_funky_lvm_stuff(TRUE, FALSE); res += stop_all_raid_devices(mountlist); finish(res); } if (argc == 2 && strcmp(argv[1], "--nuke") == 0) { iamhere("nuking"); retval += nuke_mode(bkpinfo, mountlist, raidlist); } else if (argc == 2 && strcmp(argv[1], "--interactive") == 0) { iamhere("catchall"); retval += catchall_mode(bkpinfo, mountlist, raidlist); } else if (argc == 2 && strcmp(argv[1], "--compare") == 0) { iamhere("compare"); retval += compare_mode(bkpinfo, mountlist, raidlist); } else if (argc == 2 && strcmp(argv[1], "--iso") == 0) { iamhere("iso"); retval = iso_mode(bkpinfo, mountlist, raidlist, FALSE); } else if (argc == 2 && strcmp(argv[1], "--mbr") == 0) { iamhere("mbr"); retval = mount_all_devices(mountlist, TRUE); if (!retval) { retval += run_boot_loader(FALSE); retval += unmount_all_devices(mountlist); } if (retval) { log_to_screen("Failed to write Master Boot Record"); } } else if (argc == 2 && strcmp(argv[1], "--isonuke") == 0) { iamhere("isonuke"); retval = iso_mode(bkpinfo, mountlist, raidlist, TRUE); } else if (argc != 1) { log_to_screen("Invalid paremeters"); paranoid_MR_finish(1); } else { iamhere("catchall (no mode specified in command-line call"); retval += catchall_mode(bkpinfo, mountlist, raidlist); } } /* clean up at the end */ if (retval) { if (does_file_exist("/tmp/changed.files")) { log_to_screen ("See /tmp/changed.files for list of files that have changed."); } mvaddstr_and_log_it(g_currentY++, 0, "Run complete. Errors were reported. Please review the logfile."); } else { if (IS_THIS_A_STREAMING_BACKUP(bkpinfo->backup_media_type)) { mvaddstr_and_log_it(g_currentY++, 0, "Run complete. Please remove floppy/CD/media and reboot."); } else { run_program_and_log_output("sync", FALSE); if (is_this_device_mounted(MNT_CDROM)) { res = run_program_and_log_output("umount " MNT_CDROM, FALSE); } else { res = 0; } if (!bkpinfo->please_dont_eject) { res = eject_device("/dev/cdrom"); /* if (res) { log_to_screen( "WARNING - failed to eject CD-ROM disk" ); } */ } mvaddstr_and_log_it(g_currentY++, 0, "Run complete. Please remove media and reboot."); } } // g_I_have_just_nuked is set true by nuke_mode() just before it returns if (g_I_have_just_nuked || does_file_exist("/POST-NUKE-ANYWAY")) { if (!system("which post-nuke > /dev/null 2> /dev/null")) { log_msg(1, "post-nuke found; running..."); if (mount_all_devices(mountlist, TRUE)) { log_to_screen ("Unable to re-mount partitions for post-nuke stuff"); } else { log_msg(1, "Re-mounted partitions for post-nuke stuff"); sprintf(tmp, "post-nuke %s %d", bkpinfo->restore_path, retval); if (!g_text_mode) { newtSuspend(); } log_msg(2, "Calling '%s'", tmp); if ((res = system(tmp))) { log_OS_error(tmp); } if (!g_text_mode) { newtResume(); } // newtCls(); log_msg(1, "post-nuke returned w/ res=%d", res); } unmount_all_devices(mountlist); log_msg(1, "I've finished post-nuking."); } } /* log_to_screen("If you are REALLY in a hurry, hit Ctrl-Alt-Del now."); log_to_screen("Otherwise, please wait until the RAID disks are done."); wait_until_software_raids_are_prepped("/proc/mdstat", 100); log_to_screen("Thank you."); */ unlink("/tmp/mondo-run-prog.tmp"); set_signals(FALSE); sprintf(tmp, "rm -Rf %s", bkpinfo->tmpdir); run_program_and_log_output(tmp, FALSE); log_to_screen ("Restore log copied to /tmp/mondo-restore.log on your hard disk"); sprintf(tmp, "Mondo-restore is exiting (retval=%d) ", retval); log_to_screen(tmp); sprintf(tmp, "umount %s", bkpinfo->isodir); run_program_and_log_output(tmp, 5); paranoid_free(mountlist); paranoid_free(raidlist); if (am_I_in_disaster_recovery_mode()) { run_program_and_log_output("mount / -o remount,rw", 2); } // for b0rken distros paranoid_MR_finish(retval); // frees global stuff plus bkpinfo free_libmondo_global_strings(); // it's fine to have this here :) really :) paranoid_free(a); paranoid_free(b); paranoid_free(tmp); unlink("/tmp/filelist.full"); unlink("/tmp/filelist.full.gz"); exit(retval); } /************************************************************************** *END_MAIN * **************************************************************************/ /************************************************************************** *END_MONDO-RESTORE.C * **************************************************************************/