mirror of
https://github.com/linuxmint/timeshift.git
synced 2024-10-26 18:03:42 +03:00
Updated code for reading options from command line; Auto-select default mounts while restoring from GUI; Display a confirmation prompt for device mounts before restore
This commit is contained in:
parent
1f5181a63b
commit
ef778674de
270
src/Main.vala
270
src/Main.vala
@ -877,7 +877,7 @@ public class Main : GLib.Object{
|
||||
}
|
||||
}
|
||||
|
||||
public Device read_stdin_device(Gee.ArrayList<Device> device_list){
|
||||
public Device? read_stdin_device(Gee.ArrayList<Device> device_list){
|
||||
string? line = stdin.read_line();
|
||||
line = (line != null) ? line.strip() : line;
|
||||
|
||||
@ -892,44 +892,14 @@ public class Main : GLib.Object{
|
||||
log_error("Invalid input");
|
||||
}
|
||||
else if (line.contains("/")){
|
||||
bool found = false;
|
||||
foreach(Device pi in device_list) {
|
||||
if (!pi.has_linux_filesystem()) { continue; }
|
||||
if (pi.device == line){
|
||||
selected_device = pi;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
foreach(string symlink in pi.symlinks){
|
||||
if (symlink == line){
|
||||
selected_device = pi;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found){ break; }
|
||||
}
|
||||
}
|
||||
if (!found){
|
||||
selected_device = get_device_from_name(device_list, line);
|
||||
if (selected_device == null){
|
||||
log_error("Invalid input");
|
||||
}
|
||||
}
|
||||
else{
|
||||
int64 index;
|
||||
bool found = false;
|
||||
if (int64.try_parse(line, out index)){
|
||||
int i = -1;
|
||||
foreach(Device pi in device_list) {
|
||||
if ((pi.devtype == "partition") && !pi.has_linux_filesystem()) { continue; }
|
||||
if (++i == index){
|
||||
selected_device = pi;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found){
|
||||
selected_device = get_device_from_index(device_list, line);
|
||||
if (selected_device == null){
|
||||
log_error("Invalid input");
|
||||
}
|
||||
}
|
||||
@ -937,6 +907,78 @@ public class Main : GLib.Object{
|
||||
return selected_device;
|
||||
}
|
||||
|
||||
public Device? read_stdin_device_mounts(Gee.ArrayList<Device> device_list, MountEntry mnt){
|
||||
string? line = stdin.read_line();
|
||||
line = (line != null) ? line.strip() : line;
|
||||
|
||||
Device selected_device = null;
|
||||
|
||||
if ((line == null)||(line.length == 0)||(line.down() == "c")||(line.down() == "d")){
|
||||
//set default
|
||||
if (mirror_system){
|
||||
return restore_target; //root device
|
||||
}
|
||||
else{
|
||||
return mnt.device; //keep current
|
||||
}
|
||||
}
|
||||
else if (line.down() == "a"){
|
||||
log_msg("Aborted.");
|
||||
exit_app();
|
||||
exit(0);
|
||||
}
|
||||
else if ((line.down() == "n")||(line.down() == "r")){
|
||||
return restore_target; //root device
|
||||
}
|
||||
else if (line.contains("/")){
|
||||
selected_device = get_device_from_name(device_list, line);
|
||||
if (selected_device == null){
|
||||
log_error("Invalid input");
|
||||
}
|
||||
}
|
||||
else{
|
||||
selected_device = get_device_from_index(device_list, line);
|
||||
if (selected_device == null){
|
||||
log_error("Invalid input");
|
||||
}
|
||||
}
|
||||
|
||||
return selected_device;
|
||||
}
|
||||
|
||||
public Device? get_device_from_index(Gee.ArrayList<Device> device_list, string index_string){
|
||||
int64 index;
|
||||
if (int64.try_parse(index_string, out index)){
|
||||
int i = -1;
|
||||
foreach(Device pi in device_list) {
|
||||
if ((pi.devtype == "partition") && !pi.has_linux_filesystem()) { continue; }
|
||||
if (++i == index){
|
||||
return pi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Device? get_device_from_name(Gee.ArrayList<Device> device_list, string device_name){
|
||||
foreach(Device pi in device_list) {
|
||||
if (!pi.has_linux_filesystem()) { continue; }
|
||||
if (pi.device == device_name){
|
||||
return pi;
|
||||
}
|
||||
else {
|
||||
foreach(string symlink in pi.symlinks){
|
||||
if (symlink == device_name){
|
||||
return pi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public TimeShiftBackup read_stdin_snapshot(){
|
||||
string? line = stdin.read_line();
|
||||
line = (line != null) ? line.strip() : line;
|
||||
@ -994,16 +1036,16 @@ public class Main : GLib.Object{
|
||||
string? line = stdin.read_line();
|
||||
line = (line != null) ? line.strip() : line;
|
||||
|
||||
if (line.down() == "a"){
|
||||
if ((line == null)||(line.length == 0)){
|
||||
log_error("Invalid input");
|
||||
return false;
|
||||
}
|
||||
else if (line.down() == "a"){
|
||||
log_msg("Aborted.");
|
||||
exit_app();
|
||||
exit(0);
|
||||
return true;
|
||||
}
|
||||
else if ((line == null)||(line.length == 0)){
|
||||
log_error("Invalid input");
|
||||
return false;
|
||||
}
|
||||
else if (line.down() == "y"){
|
||||
cmd_skip_grub = false;
|
||||
reinstall_grub2 = true;
|
||||
@ -1024,6 +1066,30 @@ public class Main : GLib.Object{
|
||||
}
|
||||
}
|
||||
|
||||
public bool read_stdin_change_device(){
|
||||
string? line = stdin.read_line();
|
||||
line = (line != null) ? line.strip() : line;
|
||||
|
||||
if ((line == null)||(line.length == 0)){
|
||||
return false; //default=n
|
||||
}
|
||||
else if (line.down() == "a"){
|
||||
log_msg("Aborted.");
|
||||
exit_app();
|
||||
exit(0);
|
||||
return true;
|
||||
}
|
||||
else if (line.down() == "y"){
|
||||
return true;
|
||||
}
|
||||
else if (line.down() == "n"){
|
||||
return false;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool read_stdin_restore_confirm(){
|
||||
string? line = stdin.read_line();
|
||||
line = (line != null) ? line.strip() : line;
|
||||
@ -1992,7 +2058,7 @@ public class Main : GLib.Object{
|
||||
//prompt user for target device
|
||||
if (restore_target == null){
|
||||
log_msg("");
|
||||
log_msg(TERM_COLOR_YELLOW + _("Select target device") + ":\n" + TERM_COLOR_RESET);
|
||||
log_msg(TERM_COLOR_YELLOW + _("Select target device") + " (/):\n" + TERM_COLOR_RESET);
|
||||
list_devices();
|
||||
log_msg("");
|
||||
|
||||
@ -2017,13 +2083,71 @@ public class Main : GLib.Object{
|
||||
log_msg(TERM_COLOR_YELLOW + string.nfill(78, '*') + TERM_COLOR_RESET);
|
||||
log_msg(_("Target Device") + ": %s".printf(restore_target.device + ((symlink.length > 0) ? " → " + symlink : "")), true);
|
||||
log_msg(TERM_COLOR_YELLOW + string.nfill(78, '*') + TERM_COLOR_RESET);
|
||||
}
|
||||
else{
|
||||
//print error
|
||||
log_error(_("Target device not specified!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
//select other devices in mount_list --------------------
|
||||
|
||||
if (app_mode != ""){ //command line mode
|
||||
init_mount_list();
|
||||
|
||||
for(int i = mount_list.size - 1; i >= 0; i--){
|
||||
MountEntry mnt = mount_list[i];
|
||||
Device dev = null;
|
||||
string default_device = "";
|
||||
|
||||
if (mnt.mount_point == "/"){ continue; }
|
||||
|
||||
if (mirror_system){
|
||||
default_device = restore_target.device;
|
||||
}
|
||||
else{
|
||||
default_device = mnt.device.device;
|
||||
}
|
||||
|
||||
//prompt user for device
|
||||
if (dev == null){
|
||||
log_msg("");
|
||||
log_msg(TERM_COLOR_YELLOW + _("Select '%s' device (default = %s)").printf(mnt.mount_point, default_device) + ":\n" + TERM_COLOR_RESET);
|
||||
list_devices();
|
||||
log_msg("");
|
||||
|
||||
while (dev == null){
|
||||
stdout.printf(TERM_COLOR_YELLOW + _("[a = Abort, d = Default (%s), r = Root device]").printf(default_device) + "\n\n" + TERM_COLOR_RESET);
|
||||
stdout.printf(TERM_COLOR_YELLOW + _("Enter device name or number") + ": " + TERM_COLOR_RESET);
|
||||
stdout.flush();
|
||||
dev = read_stdin_device_mounts(partition_list, mnt);
|
||||
}
|
||||
log_msg("");
|
||||
}
|
||||
|
||||
if (dev != null){
|
||||
mnt.device = dev;
|
||||
if (dev.device == restore_target.device){
|
||||
mount_list.remove_at(i);
|
||||
}
|
||||
|
||||
log_msg(TERM_COLOR_YELLOW + string.nfill(78, '*') + TERM_COLOR_RESET);
|
||||
if (dev.device == restore_target.device){
|
||||
log_msg(_("'%s' will be on root device").printf(mnt.mount_point), true);
|
||||
}
|
||||
else{
|
||||
log_msg(_("'%s' will be on '%s'").printf(mnt.mount_point, mnt.device.device), true);
|
||||
}
|
||||
log_msg(TERM_COLOR_YELLOW + string.nfill(78, '*') + TERM_COLOR_RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//mount selected devices ---------------------------------------
|
||||
|
||||
if (restore_target != null){
|
||||
if (app_mode != ""){ //commandline mode
|
||||
//init mount list
|
||||
mount_list.clear();
|
||||
mount_list.add(new MountEntry(restore_target,"/"));
|
||||
|
||||
//mount target device
|
||||
//mount target device and other devices
|
||||
bool status = mount_target_device(null);
|
||||
if (status == false){
|
||||
return false;
|
||||
@ -2151,6 +2275,60 @@ public class Main : GLib.Object{
|
||||
return thr_success;
|
||||
}
|
||||
|
||||
public void init_mount_list(){
|
||||
mount_list.clear();
|
||||
|
||||
Gee.ArrayList<FsTabEntry> fstab_list = null;
|
||||
if (mirror_system){
|
||||
string fstab_path = "/etc/fstab";
|
||||
fstab_list = FsTabEntry.read_fstab_file(fstab_path);
|
||||
}
|
||||
else{
|
||||
string fstab_path = snapshot_to_restore.path + "/localhost/etc/fstab";
|
||||
fstab_list = FsTabEntry.read_fstab_file(fstab_path);
|
||||
}
|
||||
|
||||
foreach(FsTabEntry mnt in fstab_list){
|
||||
switch(mnt.mount_point){
|
||||
case "/":
|
||||
case "/boot":
|
||||
case "/home":
|
||||
Device mnt_dev = null;
|
||||
if (mnt.device.down().has_prefix("uuid=")){
|
||||
string uuid = mnt.device.down()["uuid=".length:mnt.device.length];
|
||||
if (partition_map.has_key(uuid)){
|
||||
mnt_dev = partition_map[uuid];
|
||||
}
|
||||
}
|
||||
else{
|
||||
foreach(Device dev in partition_list){
|
||||
if (dev.device == mnt.device){
|
||||
mnt_dev = dev;
|
||||
break;
|
||||
}
|
||||
else{
|
||||
foreach(string symlink in dev.symlinks){
|
||||
if (symlink == mnt.device){
|
||||
mnt_dev = dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mnt_dev != null) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mnt_dev != null){
|
||||
mount_list.add(new MountEntry(mnt_dev, mnt.mount_point));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*foreach(MountEntry mnt in mount_list){
|
||||
log_msg("Entry:%s -> %s".printf(mnt.device.device,mnt.mount_point));
|
||||
}*/
|
||||
}
|
||||
|
||||
public string unlock_encrypted_device(Device dev, Gtk.Window? parent_win){
|
||||
string mapped_name = "%s_unlocked".printf(dev.name);
|
||||
string[] name_list = { "%s_unlocked".printf(dev.name), "%s_crypt".printf(dev.name), "luks-%s".printf(dev.uuid)};
|
||||
|
@ -154,11 +154,15 @@ public class RestoreWindow : Gtk.Dialog{
|
||||
radio_sys.toggled.connect(() => {
|
||||
sw_partitions.sensitive = radio_other.active;
|
||||
|
||||
refresh_tv_partitions();
|
||||
|
||||
if (radio_sys.active){
|
||||
App.restore_target = App.root_device;
|
||||
}
|
||||
|
||||
refresh_tv_partitions();
|
||||
else{
|
||||
init_mounts();
|
||||
}
|
||||
|
||||
//tv_partitions_select_target();
|
||||
cmb_boot_device_select_default();
|
||||
});
|
||||
@ -595,6 +599,46 @@ public class RestoreWindow : Gtk.Dialog{
|
||||
});
|
||||
|
||||
set_app_page_state();
|
||||
|
||||
init_mounts();
|
||||
|
||||
}
|
||||
|
||||
private void init_mounts(){
|
||||
TreeIter iter;
|
||||
ListStore store;
|
||||
|
||||
App.init_mount_list();
|
||||
|
||||
if (App.mirror_system){
|
||||
//default all mount points to root device except /boot
|
||||
for(int i = App.mount_list.size - 1; i >= 0; i--){
|
||||
MountEntry mnt = App.mount_list[i];
|
||||
if (mnt.mount_point != "/boot"){
|
||||
App.mount_list.remove_at(i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Note:
|
||||
* While cloning the system, /boot is the only mount point that we will leave unchanged (to avoid encrypted systems from breaking)
|
||||
* All other mounts like /home will be defaulted to target device (to prevent the "cloned" system from using the original device)
|
||||
* */
|
||||
}
|
||||
|
||||
//find the root mount point set by user
|
||||
store = (ListStore) tv_partitions.model;
|
||||
for (bool next = store.get_iter_first (out iter); next; next = store.iter_next (ref iter)) {
|
||||
Device pi;
|
||||
string mount_point;
|
||||
store.get(iter, 0, out pi);
|
||||
store.get(iter, 1, out mount_point);
|
||||
|
||||
foreach(MountEntry mnt in App.mount_list){
|
||||
if (mnt.device.device == pi.device){
|
||||
store.set(iter, 1, mnt.mount_point, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void set_app_page_state(){
|
||||
@ -1145,10 +1189,8 @@ public class RestoreWindow : Gtk.Dialog{
|
||||
//refresh treeview
|
||||
refresh_tv_exclude();
|
||||
}
|
||||
|
||||
|
||||
private void btn_restore_clicked(){
|
||||
|
||||
//check if backup device is online
|
||||
if (!check_backup_device_online()) { return; }
|
||||
|
||||
@ -1226,6 +1268,14 @@ public class RestoreWindow : Gtk.Dialog{
|
||||
App.exclude_list_restore.add("/boot/*");
|
||||
}
|
||||
|
||||
//display and confirm mount points ------------
|
||||
|
||||
if (!radio_sys.active){
|
||||
if (show_mount_list() != Gtk.ResponseType.OK){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//last option to quit - show disclaimer ------------
|
||||
|
||||
if (show_disclaimer() == Gtk.ResponseType.YES){
|
||||
@ -1278,14 +1328,13 @@ public class RestoreWindow : Gtk.Dialog{
|
||||
no_mount_points_set_by_user = false;
|
||||
|
||||
App.mount_list.add(new MountEntry(pi,mount_point));
|
||||
|
||||
|
||||
if (mount_point == "/"){
|
||||
App.restore_target = pi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (App.restore_target == null){
|
||||
//no root mount point was set by user
|
||||
|
||||
@ -1373,6 +1422,52 @@ public class RestoreWindow : Gtk.Dialog{
|
||||
return response;
|
||||
}
|
||||
|
||||
private int show_mount_list(){
|
||||
string msg = _("Following mounts will be used for restored system:") + "\n\n";
|
||||
|
||||
|
||||
int max_mount = _("Mount").length;
|
||||
int max_dev = _("Device").length;
|
||||
|
||||
foreach(MountEntry mnt in App.mount_list){
|
||||
string symlink = "";
|
||||
foreach(string sym in mnt.device.symlinks){
|
||||
if (sym.has_prefix("/dev/mapper/")){
|
||||
symlink = sym.replace("/dev/mapper/","");
|
||||
}
|
||||
}
|
||||
string dev_name = mnt.device.device.replace("/dev/","") + ((symlink.length > 0) ? " (" + symlink + ")" : "");//→
|
||||
if (dev_name.length > max_dev){ max_dev = dev_name.length; }
|
||||
if (mnt.mount_point.length > max_mount){ max_mount = mnt.mount_point.length; }
|
||||
}
|
||||
|
||||
msg += "<tt>";
|
||||
msg += "<b>";
|
||||
msg += ("%%-%ds %%-%ds\n\n".printf(max_dev, max_mount)).printf(_("Device"),_("Mount"));
|
||||
msg += "</b>";
|
||||
foreach(MountEntry mnt in App.mount_list){
|
||||
string symlink = "";
|
||||
foreach(string sym in mnt.device.symlinks){
|
||||
if (sym.has_prefix("/dev/mapper/")){
|
||||
symlink = sym.replace("/dev/mapper/","");
|
||||
}
|
||||
}
|
||||
|
||||
msg += ("%%-%ds %%-%ds\n\n".printf(max_dev, max_mount)).printf(mnt.device.device.replace("/dev/","") + ((symlink.length > 0) ? " (" + symlink + ")" : ""), mnt.mount_point);
|
||||
}
|
||||
msg += "</tt>";
|
||||
msg += "\n" + _("Click OK to continue") + "\n";
|
||||
|
||||
var dialog = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, msg);
|
||||
dialog.set_title(_("Confirm Mounts"));
|
||||
dialog.set_default_size (200, -1);
|
||||
dialog.set_transient_for(this);
|
||||
dialog.set_modal(true);
|
||||
int response = dialog.run();
|
||||
dialog.destroy();
|
||||
return response;
|
||||
}
|
||||
|
||||
private void btn_cancel_clicked(){
|
||||
App.unmount_target_device();
|
||||
this.response(Gtk.ResponseType.CANCEL);
|
||||
|
@ -1134,7 +1134,7 @@ namespace TeeJee.Devices{
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public static string create_fstab_file(FsTabEntry[] fstab_entries, bool keep_comments_and_empty_lines = true){
|
||||
string text = "";
|
||||
foreach(FsTabEntry entry in fstab_entries){
|
||||
|
@ -18,11 +18,11 @@ long_line_column=80
|
||||
|
||||
[files]
|
||||
current_page=2
|
||||
FILE_NAME_0=101697;Vala;0;EUTF-8;1;1;1;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FMain.vala;0;4
|
||||
FILE_NAME_1=23341;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FMainWindow.vala;0;4
|
||||
FILE_NAME_2=25124;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FRestoreWindow.vala;0;4
|
||||
FILE_NAME_0=26322;Vala;0;EUTF-8;1;1;1;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FMain.vala;0;4
|
||||
FILE_NAME_1=9360;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FMainWindow.vala;0;4
|
||||
FILE_NAME_2=19883;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FRestoreWindow.vala;0;4
|
||||
FILE_NAME_3=844;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FSettingsWindow.vala;0;4
|
||||
FILE_NAME_4=12263;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FUtility.vala;0;4
|
||||
FILE_NAME_4=26647;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FUtility.vala;0;4
|
||||
FILE_NAME_5=13;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Finstaller%2Finstall.sh;0;4
|
||||
FILE_NAME_6=0;Vala;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2FAboutWindow.vala;0;4
|
||||
FILE_NAME_7=946;Make;0;EUTF-8;1;1;0;%2Fhome%2Fteejee%2Fprojects%2Flinux%2Ftimeshift%2Fsrc%2Fmakefile;0;4
|
||||
|
410
timeshift.pot
410
timeshift.pot
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user