ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/SystemFilesCopy.ycp

/**
 * File:	modules/SystemFilesCopy.ycp
 * Package:	Installation
 * Summary:	Functionality for copying files from another systems
 * Authors:	Lukas Ocilka <locilka@suse.cz>
 *
 * $Id: SystemFilesCopy.ycp 57028 2009-04-29 10:58:09Z lslezak $
 *
 * Functionality for copying files from any not-mounted systems
 * to inst-sys and from inst-sys to just-installed system.
 */

{

    textdomain "installation";
    module "SystemFilesCopy";

    import "Directory";
    import "FileUtils";
    import "String";
    import "Installation";
    import "ProductFeatures";
    import "Stage";
    import "InstData";


    // --> Variables
    
    /**
     * @struct [
     *     [ archive_name, copy_to ],
     *     [ "/tmp/archive_001.tgz", "/etc" ],
     * ]
     */
    list <list <string> > copy_files_to_installed_system = [];

    boolean already_initialized = false;
    
    string inst_sys_tmp_directory = nil;
    
    string tmp_mount_directory = nil;
    
    // max tries when creating a temporary mount-directory
    integer counter_max = 10;
    
    integer tmp_archive_counter = 0;
    


    // --> Functions

    global define string CreateDirectoryIfMissing (string mnt_tmpdir);

    /**
     * Checks whether the directory exists and creates it if it is missing
     * If the path exists but it is not a directory, it tries to create another
     * directory and returns its name. 'nil' is returned when everythig fails.
     *
     * @param string mnt_tmpdir
     * #return string mnt_tmpdir (maybe changed)
     */
    global define string CreateDirectoryIfMissing (string create_directory) {
	// path already exists
	if (FileUtils::Exists(create_directory)) {
	    // exists as a directory
	    if (FileUtils::IsDirectory(create_directory)) {
		y2milestone("Directory %1 already exists", create_directory);
		return create_directory;
	    // exists but it's not a directory
	    } else {
		y2warning("Path %1 is not a directory", create_directory);
		string new_dir = nil;

		while (new_dir == nil && counter_max > 0) {
		    counter_max = counter_max - 1;
		    create_directory = create_directory + "x";
		    new_dir = CreateDirectoryIfMissing (create_directory);
		}
		
		return new_dir;
	    }

	// path doesn't exist
	} else {
	    SCR::Execute(.target.mkdir, create_directory);
	    // created successfully
	    if (FileUtils::Exists (create_directory)) {
		y2milestone("Directory %1 created", create_directory);
		return create_directory;
	    // cannot create
	    } else {
		y2error("Cannot create path %1", create_directory);
		return nil;
	    }
	}
    }
    
    /**
     * Sets and creates a temporary directory for files to lay
     * in inst-sys until they're copied to the installed system.
     * Sets and creates a temporary directory that is used for
     * mounting partitions when copying files from them.
     */
    boolean Initialize () {
	if (already_initialized) return true;

	inst_sys_tmp_directory = CreateDirectoryIfMissing("/tmp/tmp_dir_for_SystemFilesCopy_files");
	tmp_mount_directory    = CreateDirectoryIfMissing("/tmp/tmp_dir_for_SystemFilesCopy_mount");

	if (inst_sys_tmp_directory == nil || tmp_mount_directory == nil) {
	    y2error("Cannot create one of needed directories");
	    return false;
	}

	// everything is fine
	already_initialized = true;
	return true;
    }

    /**
     * Mounts the partition and proceeds the copying files from that partition
     * to the inst-sys.
     *
     * @struct partiton  == "/dev/sdb4"
     * @struct filenames == [ "/etc/123", "/etc/456" ]
     * @struct copy_to   == "/root/" (where to copy it to the installed system)
     */
    global boolean CopyFilesToTemp (string partition, list <string> filenames, string copy_to) {
	if (! Initialize ()) {
	    y2error("Cannot initialize!");
	    return false;
	}
	
	// creating full archive name (path)
	tmp_archive_counter = tmp_archive_counter + 1;
	string archive_name = sformat("%1/_inst_archive_%2.tgz", inst_sys_tmp_directory, tmp_archive_counter);

	y2milestone("Copying from '%1' files %2 to '%3'. Files will appear in '%4'",
	    partition, filenames, archive_name, copy_to
	);

	y2milestone("Mounting %1 to %2", partition, tmp_mount_directory);
	if (! (boolean) SCR::Execute(.target.mount, [partition, tmp_mount_directory], "-o ro")) {
	    y2error("Mounting failed!");
	    return false;
	}

	boolean ret = true;
	string archive_files = "";
	foreach (string filename, filenames, {
	    // removing the leading slash
	    if (substring(filename, 0, 1) == "/") filename = substring(filename, 1);
	    archive_files = archive_files + " '" + String::Quote (filename) + "'";
	});

	// archive files were already quoted
	string command = sformat (
	    // 'ignore failed read' is for optional files
	    // but needs to be entered after the archive name
	    // bugzilla #326055
	    "cd '%1'; tar --recursion -zcvf '%2' --ignore-failed-read %3",
	    tmp_mount_directory, String::Quote(archive_name), archive_files
	);
	map cmd_run = (map) SCR::Execute(.target.bash_output, command);
	if ((integer) cmd_run["exit"]:nil != 0) {
	    y2error(
		"Problem during archivation: %1, command >%2<",
		cmd_run, command
	    );
	    ret = false;
	} else {
	    y2milestone("Archived: %1", cmd_run);
	}

	y2milestone("Umounting %1", partition);
	if (! (boolean) SCR::Execute(.target.umount, tmp_mount_directory)) {
	    y2warning("Umounting failed!");
	}
	
	// add a new entry into the list of archives
	copy_files_to_installed_system = add (copy_files_to_installed_system, [archive_name, copy_to]);

	return ret;
    }

    /**
     * Proceeds the copying of all files in inst-sys (that were copied from
     * another partition before) to the directory.
     *
     * @param extract_to_dir (Installation::destdir for initial stage of installation)
     */
    global boolean CopyFilesToSystem (string extract_to_dir) {
	if (! already_initialized) {
	    y2error("CopyFilesToTemp() needs to be called first...");
	    return false;
	}
	
	boolean ret = true;

	// this should run before the SCR root is changed
	foreach (list <string> archive_to_extract, copy_files_to_installed_system, {
	    string archive_name     = archive_to_extract[0]:nil;
	    string where_to_extract = archive_to_extract[1]:nil;

	    if (archive_name == nil || where_to_extract == nil) {
		y2error("Something is wrong with the archive: %1", archive_to_extract);
		ret = false;
	    }

	    where_to_extract = sformat("%1%2", extract_to_dir, where_to_extract);

	    string command = sformat (
		"mkdir -p '%1'; cd '%1'; tar --preserve-permissions --preserve-order -xvzf '%2'",
		String::Quote (where_to_extract),
		String::Quote (archive_name)
	    );

	    map cmd_run = (map) SCR::Execute(.target.bash_output, command);
	    if ((integer) cmd_run["exit"]:nil != 0) {
		y2error(
		    "Problem during extracting an archive: %1, command >%2<",
		    cmd_run, command
		);
		ret = false;
	    } else {
		y2milestone("Extracted: %1 into %2", cmd_run, where_to_extract);
	    }
	});

	return true;
    }

    // internal functions for SaveInstSysContent -->

    string AdjustDirectoryPath (string directory_path) {
	list <string> dir_path_list = splitstring (directory_path, "/");

	dir_path_list = filter (string one_dir, dir_path_list, {
	    return (one_dir != "");
	});

	directory_path = mergestring (dir_path_list, "/");
	directory_path = sformat ("/%1/", directory_path);

	return directory_path;
    }

    boolean CopyFilesFromDirToDir (string dir_from, string dir_to) {
	string cmd = sformat (
	    "mkdir -p '%2' && cp -ar '%1.' '%2'",
	    String::Quote (dir_from),
	    String::Quote (dir_to)
	);
	map cmd_run = (map) WFM::Execute (.local.bash_output, cmd);

	if (cmd_run["exit"]:-1 != 0) {
	    y2error ("Command %1 failed %2", cmd, cmd_run);
	    return false;
	} else {
	    y2milestone ("Command >%1< succeeded", cmd);
	    return true;
	}
    }

    // <-- internal functions for SaveInstSysContent

    /**
     * Function reads <globals><save_instsys_content /></globals>
     * from control file and copies all content from inst-sys to
     * the just installed system.
     *
     * This function needs to be called in the inst-sys (first stage)
     * just before the disk is unmounted.
     *
     * FATE #301937
     * 
     * @struct
     * <globals>
     *     <save_instsys_content config:type="list">
     *         <save_instsys_item>
     *             <instsys_directory>/root/</instsys_directory>
     *             <system_directory>/root/inst-sys/</system_directory>
     *         </save_instsys_item>
     *     </save_instsys_content>
     * </globals>
     */
    global boolean SaveInstSysContent () {
	if (! Stage::initial()) {
	    y2error ("This function can be called in the initial stage only!");
	    return false;
	}

	map <string, any> globals_features = ProductFeatures::GetSection ("globals");

	if (globals_features == nil) {
	    y2warning ("No <globals> defined");
	    return false;
	} else if (globals_features["save_instsys_content"]:[] == []) {
	    y2milestone ("No items to copy from inst-sys");
	    return true;
	}
	
	list <map <string, string> > save_content =
	    (list <map <string, string> >) globals_features["save_instsys_content"]:nil;
	if (save_content == nil) {
	    y2error ("Cannot save inst-sys content: %1", globals_features["save_instsys_content"]:nil);
	    return false;
	}

	y2milestone ("Save inst-sys content: %1", save_content);
	foreach (map <string, string> copy_item, save_content, {
	    if (copy_item["instsys_directory"]:"" == "") {
		y2error ("Error: %1 is not defined", "instsys_directory");
		return;
	    } else if (copy_item["system_directory"]:"" == "") {
		y2error ("Error: %1 is not defined", "system_directory");
		return;
	    }

	    string dir_from = sformat ("/%1/", copy_item["instsys_directory"]:"");
	    string dir_to   = sformat ("/%1/%2/", Installation::destdir, copy_item["system_directory"]:"");
	    
	    dir_from = AdjustDirectoryPath (dir_from);
	    dir_to   = AdjustDirectoryPath (dir_to);

	    if (dir_from == dir_to) {
		y2error ("Dir 'from (%1)' and 'to (%2)' mustn't be the same", dir_from, dir_to);
		return;
	    }
	    // search ("/a", "/b") -> nil
	    // search ("/a/b", "/a") -> 0
	    // search ("/a/b/", "/b/") -> 2
	    integer position_str_in_str = search (dir_to, dir_from);
	    if (position_str_in_str != nil && position_str_in_str == 0) {
		y2error ("Cannot copy a directory content to itself (%1 -> %2)", dir_from, dir_to);
		return;
	    }

	    CopyFilesFromDirToDir (dir_from, dir_to);
	});

	return true;
    }

    // FATE #305019: configure the files to copy from a previous installation
    // -->

    /**
     * Sets whether copy_files from control file should be used
     *
     * @returns boolean whether to use them
     * @see SetUseControlFileDef
     */
    global boolean GetUseControlFileDef () {
	return (InstData::copy_files_use_control_file == true);
    }

    /**
     * Sets whether to use copy_files from control file
     *
     * @param boolean whether to use them
     * @see GetUseControlFileDef
     */
    global void SetUseControlFileDef (boolean new_value) {
	if (new_value == nil) {
	    y2error ("Wrong value: %1", new_value);
	    return;
	}

	InstData::copy_files_use_control_file = new_value;
	y2milestone ("Using copy_to_system from control file set to: %1", new_value);
    }

    /**
     * Returns list of copy_files definitions
     * @see SetCopySystemFiles for more info
     */
    global list <map> GetCopySystemFiles () {
	return InstData::additional_copy_files;
    }

    /**
     * Sets new rules which files will be copied during installation.
     *
     * @see FATE #305019: configure the files to copy from a previous installation
     * @param list <map> of new definitions
     *
     * @struct
     *    [
     *        "copy_to_dir" : (string) "system_directory_to_copy_to",
     *        "mandatory_files" : (list <string>) [ list of mandatory files ],
     *        "optional_files" : (list <string>) [ list of optional files ],
     *    ]
     *
     * @example
     *    SetCopySystemFiles ([
     *        $["copy_to_dir":"/root/backup", "mandatory_files":["/etc/passwd", "/etc/shadow"]]
     *        $["copy_to_dir":"/root/backup", "mandatory_files":["/etc/ssh/ssh_host_dsa_key"], "optional_files":["/etc/ssh/ssh_host_rsa_key.pub"]]
     *    ])
     */
    global void SetCopySystemFiles (list <map> new_copy_files) {
	InstData::additional_copy_files = [];

	boolean use_item = true;

	foreach (map one_copy_item, new_copy_files, {
	    string copy_to_dir = tostring (one_copy_item["copy_to_dir"]:Directory::vardir);
	    if (copy_to_dir == nil || copy_to_dir == "") {
		y2error ("(string) 'copy_to_dir' must be defined");
		use_item = false;
	    }

	    list <string> mandatory_files = (list <string>) one_copy_item["mandatory_files"]:[];
	    if (mandatory_files == nil || mandatory_files == []) {
		y2error ("(list <string>) 'mandatory_files' must be defined");
		use_item = false;
	    }

	    list <string> optional_files = (list <string>) one_copy_item["optional_files"]:[];
	    if (optional_files == nil) {
		y2error ("(list <string>) 'optional_files' wrong definition");
		use_item = false;
	    }

	    if (use_item) {
		InstData::additional_copy_files = add (InstData::additional_copy_files, one_copy_item);
	    }
	});
    }

    // <--

/* EOF */
}

ACC SHELL 2018