~teejee2008/timeshift/trunk

302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1
/*
2
 * Device.vala
3
 *
4
 * Copyright 2016 Tony George <teejeetech@gmail.com>
340 by Tony George
Updated header comments
5
 *
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA 02110-1301, USA.
20
 *
21
 *
22
 */
23
 
24
/* Functions and classes for handling disk partitions */
25
26
using TeeJee.Logging;
27
using TeeJee.FileSystem;
28
using TeeJee.ProcessHelper;
29
using TeeJee.GtkHelper;
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
30
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
31
public class Device : GLib.Object{
32
33
	/* Class for storing disk information */
34
35
	public static double KB = 1000;
36
	public static double MB = 1000 * KB;
37
	public static double GB = 1000 * MB;
38
39
	public static double KiB = 1024;
40
	public static double MiB = 1024 * KiB;
41
	public static double GiB = 1024 * MiB;
42
	
43
	public string device = "";
44
	public string kname = "";
45
	public string pkname = "";
46
	public string name = "";
47
	public string mapped_name = "";
48
	
49
	public string type = ""; // disk, part, crypt, loop, rom, lvm
50
	public string fstype = ""; // iso9660, ext4, btrfs, ...
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
51
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
52
	public string label = "";
53
	public string uuid = "";
54
	public int order = -1;
55
	
56
	public string vendor = "";
57
	public string model = "";
58
	public string serial = "";
59
	public string revision = "";
60
61
	public bool removable = false;
62
	public bool read_only = false;
63
	
64
	public int64 size_bytes = 0;
65
	public int64 used_bytes = 0;
66
	public int64 available_bytes = 0;
67
	
68
	public string used_percent = "";
69
	public string dist_info = "";
70
	public Gee.ArrayList<MountEntry> mount_points;
71
	public Gee.ArrayList<string> symlinks;
72
73
	public Device parent = null;
74
	public Gee.ArrayList<Device> children = null;
75
76
	private static string lsblk_version = "";
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
77
	private static bool lsblk_is_ancient = false;
78
	
79
	private static Gee.ArrayList<Device> device_list;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
80
81
	public Device(){
82
		mount_points = new Gee.ArrayList<MountEntry>();
83
		symlinks = new Gee.ArrayList<string>();
84
		children = new Gee.ArrayList<Device>();
85
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
86
		test_lsblk_version();
87
	}
88
89
	public static void test_lsblk_version(){
90
91
		if ((lsblk_version != null) && (lsblk_version.length > 0)){
92
			return;
93
		}
94
		
95
		string std_out, std_err;
96
		int status = exec_sync("lsblk --version", out std_out, out std_err);
97
		if (status == 0){
98
			lsblk_version = std_out;
99
			lsblk_is_ancient = false;
100
		}
101
		else{
102
			lsblk_version = "ancient";
103
			lsblk_is_ancient = true;
104
		}
105
	}
106
	
107
	public int64 free_bytes{
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
108
		get{
109
			return (used_bytes == 0) ? 0 : (size_bytes - used_bytes);
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
110
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
111
	}
112
113
	public string size{
114
		owned get{
115
			if (size_bytes < GB){
116
				return "%.1f MB".printf(size_bytes / MB);
117
			}
118
			else if (size_bytes > 0){
119
				return "%.1f GB".printf(size_bytes / GB);
120
			} 
121
			else{
122
				return "";
123
			}
124
		}
125
	}
126
127
	public string used{
128
		owned get{
129
			return (used_bytes == 0) ? "" : "%.1f GB".printf(used_bytes / GB);
130
		}
131
	}
132
133
	public string free{
134
		owned get{
135
			return (free_bytes == 0) ? "" : "%.1f GB".printf(free_bytes / GB);
136
		}
137
	}
138
139
	public bool is_mounted{
140
		get{
141
			return (mount_points.size > 0);
142
		}
143
	}
144
145
	public bool has_linux_filesystem(){
146
		switch (fstype){
147
			case "ext2":
148
			case "ext3":
149
			case "ext4":
150
			case "reiserfs":
151
			case "reiser4":
152
			case "xfs":
153
			case "jfs":
154
			case "btrfs":
155
			case "lvm":
156
			case "lvm2":
157
			case "lvm2_member":
158
			case "luks":
159
			case "crypt":
160
			case "crypto_luks":
161
				return true;
162
			default:
163
				return false;
164
		}
165
	}
166
167
	public bool is_encrypted_partition(){
168
		return (type == "part") && fstype.down().contains("luks");
169
	}
170
171
	public bool is_on_encrypted_partition(){
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
172
		return (type == "crypt");
173
	}
174
175
	public bool is_lvm_partition(){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
176
		return (type == "part") && fstype.down().contains("lvm2_member");
177
	}
178
179
	public bool has_children(){
180
		return (children.size > 0);
181
	}
182
183
	public Device? first_linux_child(){
184
		
185
		foreach(var child in children){
186
			if (child.has_linux_filesystem()){
187
				return child;
188
			}
189
		}
190
		
191
		return null;
192
	}
193
194
	public bool has_parent(){
195
		return (parent != null);
196
	}
197
198
	// static --------------------------------
199
	
200
	public static Gee.ArrayList<Device> get_filesystems(bool get_space = true, bool get_mounts = true){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
201
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
202
		/* Returns list of block devices
203
		   Populates all fields in Device class */
204
205
		var list = get_block_devices_using_lsblk();
206
207
		if (get_space){
208
			//get used space for mounted filesystems
209
			var list_df = get_disk_space_using_df();
210
			foreach(var dev_df in list_df){
211
				var dev = find_device_in_list_by_uuid(list, dev_df.uuid);
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
212
				if (dev != null){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
213
					dev.size_bytes = dev_df.size_bytes;
214
					dev.used_bytes = dev_df.used_bytes;
215
					dev.available_bytes = dev_df.available_bytes;
216
					dev.used_percent = dev_df.used_percent;
217
				}
218
			}
219
		}
220
221
		if (get_mounts){
222
			//get mount points
223
			var list_mtab = get_mounted_filesystems_using_mtab();
224
			foreach(var dev_mtab in list_mtab){
225
				var dev = find_device_in_list_by_uuid(list, dev_mtab.uuid);
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
226
				if (dev != null){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
227
					dev.mount_points = dev_mtab.mount_points;
228
				}
229
			}
230
		}
231
232
		//print_device_list(list);
233
234
		//print_device_mounts(list);
235
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
236
		log_debug("Device: get_filesystems(): %d".printf(list.size));
237
		
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
238
		return list;
239
	}
240
241
	private static void find_child_devices(Gee.ArrayList<Device> list, Device parent){
242
		if (lsblk_is_ancient && (parent.type == "disk")){
343 by Tony George
Code cleanup
243
			foreach (var part in list){
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
244
				if ((part.kname != parent.kname) && part.kname.has_prefix(parent.kname)){
245
					parent.children.add(part);
246
					part.parent = parent;
247
					part.pkname = parent.kname;
248
					log_debug("%s -> %s".printf(parent.kname, part.kname));
249
				}
250
			}
251
		}
252
		else{
253
			foreach (var part in list){
254
				if (part.pkname == parent.kname){
255
					parent.children.add(part);
256
					part.parent = parent;
257
				}
258
			}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
259
		}
260
	}
261
262
	private static void find_child_devices_using_dmsetup(Gee.ArrayList<Device> list){
342 by Tony George
Link child devices using dmsetup if lsblk is ancient;
263
		
264
		string std_out, std_err;
265
		exec_sync("dmsetup deps -o blkdevname", out std_out, out std_err);
266
		
267
		/*
268
		sdb3_crypt: 1 dependencies	: (sdb3)
269
		sda5_crypt: 1 dependencies	: (sda5)
270
		mmcblk0_crypt: 1 dependencies	: (mmcblk0)
271
		*/
272
273
		Regex rex;
274
		MatchInfo match;
275
276
		foreach(string line in std_out.split("\n")){
277
			if (line.strip().length == 0) { continue; }
278
279
			try{
280
	
281
				rex = new Regex("""([^:]*)\:.*\((.*)\)""");
282
283
				if (rex.match (line, 0, out match)){
284
	
285
					string child_name = match.fetch(1).strip();
286
					string parent_kname = match.fetch(2).strip();
287
288
					Device parent = null;
289
					foreach(var dev in list){
290
						if ((dev.kname == parent_kname)){
291
							parent = dev;
292
							break;
293
						}
294
					}
295
296
					Device child = null;
297
					foreach(var dev in list){
298
						if ((dev.mapped_name == child_name)){
299
							child = dev;
300
							break;
301
						}
302
					}
303
					
304
					if ((parent != null) && (child != null)){
305
						child.pkname = parent.kname;
306
						log_debug("%s -> %s".printf(parent.kname, child.kname));
307
					}
308
					
309
				}
310
				else{
311
					log_debug("no-match: %s".printf(line));
312
				}
313
			}
314
			catch(Error e){
315
				log_error (e.message);
316
			}
317
		}
318
	}
319
320
	public static Gee.ArrayList<Device> get_block_devices_using_lsblk(string dev_name = ""){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
321
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
322
		/* Returns list of mounted partitions using 'lsblk' command
323
		   Populates device, type, uuid, label */
324
325
		test_lsblk_version();
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
326
		
327
		var list = new Gee.ArrayList<Device>();
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
328
329
		string std_out;
330
		string std_err;
331
		string cmd;
332
		int ret_val;
333
		Regex rex;
334
		MatchInfo match;
335
336
		if (lsblk_is_ancient){
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
337
			cmd = "lsblk --bytes --pairs --output NAME,KNAME,LABEL,UUID,TYPE,FSTYPE,SIZE,MOUNTPOINT,MODEL,RO,RM";
338
		}
339
		else{
340
			cmd = "lsblk --bytes --pairs --output NAME,KNAME,LABEL,UUID,TYPE,FSTYPE,SIZE,MOUNTPOINT,MODEL,RO,HOTPLUG,PKNAME,VENDOR,SERIAL,REV";
341
		}
342
343
		if (dev_name.length > 0){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
344
			cmd += " %s".printf(dev_name);
345
		}
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
346
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
347
		if (LOG_DEBUG){
348
			log_debug("");
349
			log_debug(cmd);
350
		}
351
			
352
		ret_val = exec_sync(cmd, out std_out, out std_err);
353
		
354
		//if (ret_val != 0){
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
355
		//	var msg = "lsblk: " + _("Failed to get partition list");
356
		//	msg += (device_file.length > 0) ? ": " + device_file : "";
357
		//	log_error (msg);
358
		//	return list; //return empty map
359
		//}
360
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
361
		/*
362
		sample output
363
		-----------------
364
		NAME="sda" KNAME="sda" PKNAME="" LABEL="" UUID="" FSTYPE="" SIZE="119.2G" MOUNTPOINT="" HOTPLUG="0"
365
		
366
		NAME="sda1" KNAME="sda1" PKNAME="sda" LABEL="" UUID="5345-E139" FSTYPE="vfat" SIZE="47.7M" MOUNTPOINT="/boot/efi" HOTPLUG="0"
367
		
368
		NAME="mmcblk0p1" KNAME="mmcblk0p1" PKNAME="mmcblk0" LABEL="" UUID="3c0e4bbf" FSTYPE="crypto_LUKS" SIZE="60.4G" MOUNTPOINT="" HOTPLUG="1"
369
		
370
		NAME="luks-3c0" KNAME="dm-1" PKNAME="mmcblk0p1" LABEL="" UUID="f0d933c0-" FSTYPE="ext4" SIZE="60.4G" MOUNTPOINT="/mnt/sdcard" HOTPLUG="0"
371
		*/
372
373
		/*
374
		Note: Multiple loop devices can have same UUIDs.
375
		Example: Loop devices created by mounting the same ISO multiple times.
376
		*/
377
378
		//parse output and build filesystem map -------------
379
380
		int index = -1;
381
		
382
		foreach(string line in std_out.split("\n")){
383
			if (line.strip().length == 0) { continue; }
384
385
			try{
386
				if (lsblk_is_ancient){
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
387
					rex = new Regex("""NAME="(.*)" KNAME="(.*)" LABEL="(.*)" UUID="(.*)" TYPE="(.*)" FSTYPE="(.*)" SIZE="(.*)" MOUNTPOINT="(.*)" MODEL="(.*)" RO="([0-9]+)" RM="([0-9]+)"""");
388
				}
389
				else{
390
					rex = new Regex("""NAME="(.*)" KNAME="(.*)" LABEL="(.*)" UUID="(.*)" TYPE="(.*)" FSTYPE="(.*)" SIZE="(.*)" MOUNTPOINT="(.*)" MODEL="(.*)" RO="([0-9]+)" HOTPLUG="([0-9]+)" PKNAME="(.*)" VENDOR="(.*)" SERIAL="(.*)" REV="(.*)"""");
391
				}
392
				
393
				if (rex.match (line, 0, out match)){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
394
395
					Device pi = new Device();
396
					pi.name = match.fetch(1).strip();
397
					pi.kname = match.fetch(2).strip();
398
					pi.label = match.fetch(3).strip();
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
399
					pi.uuid = match.fetch(4).strip();
400
					pi.type = match.fetch(5).strip().down();
401
					
402
					pi.fstype = match.fetch(6).strip().down();
403
					pi.fstype = (pi.fstype == "crypto_luks") ? "luks" : pi.fstype;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
404
					pi.fstype = (pi.fstype == "lvm2_member") ? "lvm2" : pi.fstype;
405
					
406
					pi.size_bytes = int64.parse(match.fetch(7).strip());
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
407
408
					var mp = match.fetch(8).strip();
409
					if (mp.length > 0){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
410
						pi.mount_points.add(new MountEntry(pi,mp,""));
411
					}
412
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
413
					pi.model = match.fetch(9).strip();
414
415
					pi.read_only = (match.fetch(10).strip() == "1");
416
417
					pi.removable = (match.fetch(11).strip() == "1");
418
419
					if (!lsblk_is_ancient){
420
						pi.pkname = match.fetch(12).strip();
421
						pi.vendor = match.fetch(13).strip();
422
						pi.serial = match.fetch(14).strip();
423
						pi.revision = match.fetch(15).strip();
424
					}
425
					
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
426
					pi.order = ++index;
427
					pi.device = "/dev/%s".printf(pi.kname);
428
429
					//if ((pi.type == "crypt") && (pi.pkname.length > 0)){
342 by Tony George
Link child devices using dmsetup if lsblk is ancient;
430
					//	pi.name = "%s (unlocked)".printf(pi.pkname);
431
					//}
432
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
433
					//if ((pi.uuid.length > 0) && (pi.pkname.length > 0)){
434
						list.add(pi);
435
					//}
436
				}
437
				else{
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
438
					log_debug("no-match: %s".printf(line));
439
				}
440
			}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
441
			catch(Error e){
442
				log_error (e.message);
443
			}
444
		}
445
446
		// already sorted
447
		/*list.sort((a,b)=>{
448
			return (a.order - b.order);
449
		});*/
450
451
		// add aliases from /dev/disk/by-uuid/
452
453
		foreach(var dev in list){
454
			var dev_by_uuid = path_combine("/dev/disk/by-uuid/", dev.uuid);
455
			if (file_exists(dev_by_uuid)){
456
				dev.symlinks.add(dev_by_uuid);
457
			}
458
		}
459
460
		// add aliases from /dev/mapper/
461
		
462
		try
463
		{
464
			File f_dev_mapper = File.new_for_path ("/dev/mapper");
465
			
466
			FileEnumerator enumerator = f_dev_mapper.enumerate_children (
467
				"%s,%s".printf(
468
					FileAttribute.STANDARD_NAME, FileAttribute.STANDARD_SYMLINK_TARGET),
469
				FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
470
				
471
			FileInfo info;
472
			while ((info = enumerator.next_file ()) != null) {
473
474
				if (info.get_name() == "control") { continue; }
475
				
476
				File f_mapped = f_dev_mapper.resolve_relative_path(info.get_name());
477
				
478
				string mapped_file = f_mapped.get_path();
479
				string mapped_device = info.get_symlink_target();
480
				mapped_device = mapped_device.replace("..","/dev");
481
				//log_debug("info.get_name(): %s".printf(info.get_name()));
482
				//log_debug("info.get_symlink_target(): %s".printf(info.get_symlink_target()));
483
				//log_debug("mapped_file: %s".printf(mapped_file));
484
				//log_debug("mapped_device: %s".printf(mapped_device));
485
				
486
				foreach(var dev in list){
487
					if (dev.device == mapped_device){
488
						dev.mapped_name = mapped_file.replace("/dev/mapper/","");
489
						dev.symlinks.add(mapped_file);
490
						log_debug("found link: %s -> %s".printf(mapped_file, dev.device));
491
						break;
492
					}
493
				}
494
			}
495
		}
496
		catch (Error e) {
497
			log_error (e.message);
498
		}
499
500
		device_list = list;
501
502
		foreach (var part in list){
330 by Tony George
Minor improvements in Device class
503
			find_child_devices(list, part);
504
		}
505
342 by Tony George
Link child devices using dmsetup if lsblk is ancient;
506
		if (lsblk_is_ancient){
507
			find_child_devices_using_dmsetup(list);
508
		}
509
		
330 by Tony George
Minor improvements in Device class
510
		print_device_list(list);
342 by Tony George
Link child devices using dmsetup if lsblk is ancient;
511
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
512
		log_debug("Device: get_block_devices_using_lsblk(): %d".printf(list.size));
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
513
		
514
		return list;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
515
	}
516
517
	// deprecated: use get_block_devices_using_lsblk() instead
518
	public static Gee.ArrayList<Device> get_block_devices_using_blkid(string dev_name = ""){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
519
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
520
		/* Returns list of mounted partitions using 'blkid' command
521
		   Populates device, type, uuid, label */
522
523
		var list = new Gee.ArrayList<Device>();
524
525
		string std_out;
526
		string std_err;
527
		string cmd;
528
		int ret_val;
529
		Regex rex;
530
		MatchInfo match;
531
532
		cmd = "/sbin/blkid" + ((dev_name.length > 0) ? " " + dev_name: "");
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
533
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
534
		if (LOG_DEBUG){
535
			log_debug(cmd);
536
		}
537
		
538
		ret_val = exec_script_sync(cmd, out std_out, out std_err);
539
		if (ret_val != 0){
540
			var msg = "blkid: " + _("Failed to get partition list");
541
			msg += (dev_name.length > 0) ? ": " + dev_name : "";
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
542
			log_error(msg);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
543
			return list; //return empty list
544
		}
545
546
		/*
547
		sample output
548
		-----------------
549
		/dev/sda1: LABEL="System Reserved" UUID="F476B08076B04560" TYPE="ntfs"
550
		/dev/sda2: LABEL="windows" UUID="BE00B6DB00B69A3B" TYPE="ntfs"
551
		/dev/sda3: UUID="03f3f35d-71fa-4dff-b740-9cca19e7555f" TYPE="ext4"
552
		*/
553
554
		//parse output and build filesystem map -------------
555
556
		foreach(string line in std_out.split("\n")){
557
			if (line.strip().length == 0) { continue; }
558
559
			Device pi = new Device();
560
561
			pi.device = line.split(":")[0].strip();
562
563
			if (pi.device.length == 0) { continue; }
564
565
			//exclude non-standard devices --------------------
566
567
			if (!pi.device.has_prefix("/dev/")){
568
				continue;
569
			}
570
571
			if (pi.device.has_prefix("/dev/sd") || pi.device.has_prefix("/dev/hd") || pi.device.has_prefix("/dev/mapper/") || pi.device.has_prefix("/dev/dm")) {
572
				//ok
573
			}
574
			else if (pi.device.has_prefix("/dev/disk/by-uuid/")){
575
				//ok, get uuid
576
				pi.uuid = pi.device.replace("/dev/disk/by-uuid/","");
577
			}
578
			else{
579
				continue; //skip
580
			}
581
582
			//parse & populate fields ------------------
583
584
			try{
585
				rex = new Regex("""LABEL=\"([^\"]*)\"""");
586
				if (rex.match (line, 0, out match)){
587
					pi.label = match.fetch(1).strip();
588
				}
589
590
				rex = new Regex("""UUID=\"([^\"]*)\"""");
591
				if (rex.match (line, 0, out match)){
592
					pi.uuid = match.fetch(1).strip();
593
				}
594
595
				rex = new Regex("""TYPE=\"([^\"]*)\"""");
596
				if (rex.match (line, 0, out match)){
597
					pi.fstype = match.fetch(1).strip();
598
				}
599
			}
600
			catch(Error e){
601
				log_error (e.message);
602
			}
603
604
			//add to map -------------------------
605
606
			if (pi.uuid.length > 0){
607
				list.add(pi);
608
			}
609
		}
610
611
		log_debug("Device: get_block_devices_using_blkid(): %d".printf(list.size));
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
612
		
613
		return list;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
614
	}
615
616
	public static Gee.ArrayList<Device> get_disk_space_using_df(string dev_name_or_mount_point = ""){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
617
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
618
		/*
619
		Returns list of mounted partitions using 'df' command
620
		Populates device, type, size, used and mount_point_list
621
		*/
622
623
		var list = new Gee.ArrayList<Device>();
624
625
		string std_out;
626
		string std_err;
627
		string cmd;
628
		int ret_val;
629
630
		cmd = "df -T -B1";
631
632
		if (dev_name_or_mount_point.length > 0){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
633
			cmd += " '%s'".printf(escape_single_quote(dev_name_or_mount_point));
634
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
635
636
		if (LOG_DEBUG){
637
			log_debug(cmd);
638
		}
639
640
		ret_val = exec_sync(cmd, out std_out, out std_err);
641
		//ret_val is not reliable, no need to check
642
643
		/*
644
		sample output
645
		-----------------
646
		Filesystem     Type     1M-blocks    Used Available Use% Mounted on
647
		/dev/sda3      ext4        25070M  19508M     4282M  83% /
648
		none           tmpfs           1M      0M        1M   0% /sys/fs/cgroup
649
		udev           devtmpfs     3903M      1M     3903M   1% /dev
650
		tmpfs          tmpfs         789M      1M      788M   1% /run
651
		none           tmpfs           5M      0M        5M   0% /run/lock
652
		/dev/sda3      ext4        25070M  19508M     4282M  83% /mnt/timeshift
653
		*/
654
655
		string[] lines = std_out.split("\n");
656
657
		int line_num = 0;
658
		foreach(string line in lines){
659
660
			if (++line_num == 1) { continue; }
661
			if (line.strip().length == 0) { continue; }
662
663
			Device pi = new Device();
664
665
			//parse & populate fields ------------------
666
667
			int k = 1;
668
			foreach(string val in line.split(" ")){
669
670
				if (val.strip().length == 0){ continue; }
671
672
				switch(k++){
673
					case 1:
674
						pi.device = val.strip();
675
						break;
676
					case 2:
677
						pi.fstype = val.strip();
678
						break;
679
					case 3:
680
						pi.size_bytes = int64.parse(val.strip());
681
						break;
682
					case 4:
683
						pi.used_bytes = int64.parse(val.strip());
684
						break;
685
					case 5:
686
						pi.available_bytes = int64.parse(val.strip());
687
						break;
688
					case 6:
689
						pi.used_percent = val.strip();
690
						break;
691
					case 7:
692
						//string mount_point = val.strip();
693
						//if (!pi.mount_point_list.contains(mount_point)){
694
						//	pi.mount_point_list.add(mount_point);
695
						//}
696
						break;
697
				}
698
			}
699
700
			/* Note:
701
			 * The mount points displayed by 'df' are not reliable.
702
			 * For example, if same device is mounted at 2 locations, 'df' displays only the first location.
703
			 * Hence, we will not populate the 'mount_points' field in Device object
704
			 * Use get_mounted_filesystems_using_mtab() if mount info is required
705
			 * */
706
707
			// resolve device name --------------------
708
709
			pi.device = resolve_device_name(pi.device);
710
			
711
			// get uuid ---------------------------
712
713
			pi.uuid = get_device_uuid(pi.device);
714
715
			// add to map -------------------------
716
717
			if (pi.uuid.length > 0){
718
				list.add(pi);
719
			}
720
		}
721
		
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
722
		log_debug("Device: get_disk_space_using_df(): %d".printf(list.size));
723
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
724
		return list;
725
	}
726
727
	public static Gee.ArrayList<Device> get_mounted_filesystems_using_mtab(){
728
729
		/* Returns list of mounted partitions by reading /proc/mounts
730
		   Populates device, type and mount_point_list */
731
732
		var list = new Gee.ArrayList<Device>();
733
734
		string mtab_path = "/etc/mtab";
735
		string mtab_lines = "";
736
737
		File f;
738
739
		// find mtab file -----------
740
741
		mtab_path = "/proc/mounts";
742
		f = File.new_for_path(mtab_path);
743
		if(!f.query_exists()){
744
			mtab_path = "/proc/self/mounts";
745
			f = File.new_for_path(mtab_path);
746
			if(!f.query_exists()){
747
				mtab_path = "/etc/mtab";
748
				f = File.new_for_path(mtab_path);
749
				if(!f.query_exists()){
750
					return list; //empty list
751
				}
752
			}
753
		}
754
755
		/* Note:
756
		 * /etc/mtab represents what 'mount' passed to the kernel
757
		 * whereas /proc/mounts shows the data as seen inside the kernel
758
		 * Hence /proc/mounts is always up-to-date whereas /etc/mtab might not be
759
		 * */
760
761
		//read -----------
762
763
		mtab_lines = file_read(mtab_path);
764
765
		/*
766
		sample mtab
767
		-----------------
768
		/dev/sda3 / ext4 rw,errors=remount-ro 0 0
769
		proc /proc proc rw,noexec,nosuid,nodev 0 0
770
		sysfs /sys sysfs rw,noexec,nosuid,nodev 0 0
771
		none /sys/fs/cgroup tmpfs rw 0 0
772
		none /sys/fs/fuse/connections fusectl rw 0 0
773
		none /sys/kernel/debug debugfs rw 0 0
774
		none /sys/kernel/security securityfs rw 0 0
775
		udev /dev devtmpfs rw,mode=0755 0 0
776
777
		device - the device or remote filesystem that is mounted.
778
		mountpoint - the place in the filesystem the device was mounted.
779
		filesystemtype - the type of filesystem mounted.
780
		options - the mount options for the filesystem
781
		dump - used by dump to decide if the filesystem needs dumping.
782
		fsckorder - used by fsck to detrmine the fsck pass to use.
783
		*/
784
785
		/* Note:
786
		 * We are interested only in the last device that was mounted at a given mount point
787
		 * Hence the lines must be parsed in reverse order (from last to first)
788
		 * */
789
790
		//parse ------------
791
792
		string[] lines = mtab_lines.split("\n");
793
		var mount_list = new Gee.ArrayList<string>();
794
795
		for (int i = lines.length - 1; i >= 0; i--){
796
797
			string line = lines[i].strip();
798
			if (line.length == 0) { continue; }
799
800
			var pi = new Device();
801
802
			var mp = new MountEntry(pi,"","");
803
804
			//parse & populate fields ------------------
805
806
			int k = 1;
807
			foreach(string val in line.split(" ")){
808
				if (val.strip().length == 0){ continue; }
809
				switch(k++){
810
					case 1: //device
811
						pi.device = val.strip();
812
						break;
813
					case 2: //mountpoint
814
						mp.mount_point = val.strip();
815
						if (!mount_list.contains(mp.mount_point)){
816
							mount_list.add(mp.mount_point);
817
							pi.mount_points.add(mp);
818
						}
819
						break;
820
					case 3: //filesystemtype
821
						pi.fstype = val.strip();
822
						break;
823
					case 4: //options
824
						mp.mount_options = val.strip();
825
						break;
826
					default:
827
						//ignore
828
						break;
829
				}
830
			}
831
832
			// resolve device names ----------------
833
834
			pi.device = resolve_device_name(pi.device);
835
836
			// get uuid ---------------------------
837
838
			pi.uuid = get_device_uuid(pi.device);
839
840
			// add to map -------------------------
841
842
			if (pi.uuid.length > 0){
843
				var dev = find_device_in_list_by_uuid(list, pi.uuid);
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
844
				if (dev == null){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
845
					list.add(pi);
846
				}
847
				else{
848
					// add mount points to existing device
849
					foreach(var item in pi.mount_points){
850
						dev.mount_points.add(item);
851
					}
852
				}
853
			}
854
		}
855
856
		log_debug("Device: get_mounted_filesystems_using_mtab(): %d".printf(list.size));
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
857
		
858
		return list;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
859
	}
860
861
	// helpers ----------------------------------
862
863
	public static Device? find_device_in_list_by_name(Gee.ArrayList<Device> list, string dev_name){
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
864
865
		foreach(var dev in list){
866
			if (dev.device == dev_name){
867
				return dev;
868
			}
869
		}
870
		
871
		return null;
872
	}
873
874
	public static Device? find_device_in_list_by_uuid(Gee.ArrayList<Device> list, string dev_uuid){
875
876
		foreach(var dev in list){
877
			if (dev.uuid == dev_uuid){
878
				return dev;
879
			}
880
		}
881
		
882
		return null;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
883
	}
884
885
	public static Device? get_device_by_uuid(string uuid){
886
		foreach(var dev in device_list){
887
			if (dev.uuid == uuid){
888
				return dev;
889
			}
890
		}
891
		
892
		return null;
893
	}
894
895
	public static Device? get_device_by_name(string file_name){
896
897
		var device_name = resolve_device_name(file_name);
898
		
899
		foreach(var dev in device_list){
900
			if (dev.device == device_name){
901
				return dev;
902
			}
903
		}
904
		
905
		return null;
906
	}
907
908
	public static Device? get_device_by_path(string path_to_check){
909
		var list = Device.get_disk_space_using_df(path_to_check);
910
		if (list.size > 0){
911
			return list[0];
912
		}
913
		return null;
914
	}
915
	
916
	public static string get_device_uuid(string device){
917
		if (device_list == null){
918
			device_list = get_block_devices_using_lsblk();
919
		}
920
		foreach(Device dev in device_list){
921
			if (dev.device == device){
922
				return dev.uuid;
923
			}
924
		}
925
		return "";
926
	}
927
928
	public static Gee.ArrayList<MountEntry> get_device_mount_points(string dev_name_or_uuid){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
929
		string device = "";
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
930
		string uuid = "";
931
932
		if (dev_name_or_uuid.has_prefix("/dev")){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
933
			device = dev_name_or_uuid;
934
			uuid = get_device_uuid(dev_name_or_uuid);
935
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
936
		else{
937
			uuid = dev_name_or_uuid;
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
938
			device = "/dev/disk/by-uuid/%s".printf(uuid);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
939
			device = resolve_device_name(device);
940
		}
941
942
		var list_mtab = get_mounted_filesystems_using_mtab();
943
		
944
		var dev = find_device_in_list_by_uuid(list_mtab, uuid);
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
945
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
946
		if (dev != null){
947
			return dev.mount_points;
948
		}
949
		else{
950
			return (new Gee.ArrayList<MountEntry>());
951
		}
952
	}
953
954
	public static bool device_is_mounted(string dev_name_or_uuid){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
955
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
956
		var mps = Device.get_device_mount_points(dev_name_or_uuid);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
957
		if (mps.size > 0){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
958
			return true;
959
		}
960
961
		return false;
962
	}
963
964
	public static bool mount_point_in_use(string mount_point){
965
		var list = Device.get_mounted_filesystems_using_mtab();
966
		foreach (var dev in list){
967
			foreach(var mp in dev.mount_points){
968
				if (mp.mount_point.has_prefix(mount_point)){
969
					// check for any mount point at or under the given mount_point
970
					return true;
971
				}
972
			}
973
		}
974
		return false;
975
	}
976
977
	public static string resolve_device_name(string dev_alias){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
978
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
979
		string resolved = dev_alias;
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
980
		
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
981
		if (dev_alias.has_prefix("/dev/mapper/")){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
982
			var link_path = file_get_symlink_target(dev_alias);
983
			if (link_path.has_prefix("../")){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
984
				resolved = link_path.replace("../","/dev/");
985
			}
986
		}
987
988
		if (dev_alias.has_prefix("/dev/disk/")){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
989
			var link_path = file_get_symlink_target(dev_alias);
990
			if (link_path.has_prefix("../../")){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
991
				resolved = link_path.replace("../../","/dev/");
992
			}
993
		}
994
995
		if (dev_alias != resolved){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
996
			//log_msg("resolved '%s' to '%s'".printf(dev_alias, resolved));
997
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
998
999
		return resolved;
1000
	}
1001
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
1002
	public void copy_fields_from(Device dev2){
1003
		
1004
		this.device = dev2.device;
1005
		this.name = dev2.name;
1006
		this.kname = dev2.kname;
1007
		this.pkname = dev2.pkname;
1008
		this.label = dev2.label;
1009
		this.uuid = dev2.uuid;
1010
		this.mapped_name = dev2.mapped_name;
1011
		
1012
		this.type = dev2.type;
1013
		this.fstype = dev2.fstype;
1014
1015
		this.size_bytes = dev2.size_bytes;
1016
		this.used_bytes = dev2.used_bytes;
1017
		this.available_bytes = dev2.available_bytes;
1018
		
1019
		this.mount_points = dev2.mount_points;
1020
		this.symlinks = dev2.symlinks;
1021
		this.parent = dev2.parent;
1022
		this.children = dev2.children;
1023
1024
		this.vendor = dev2.vendor;
1025
		this.model = dev2.model;
1026
		this.serial = dev2.serial;
1027
		this.revision = dev2.revision;
1028
1029
		this.removable = dev2.removable;
1030
		this.read_only = dev2.read_only;
1031
	}
1032
1033
	public Device? query_changes(){
1034
		
1035
		Device dev_new = null;
1036
		
1037
		foreach (var dev in get_block_devices_using_lsblk()){
1038
			if (uuid.length > 0){
1039
				if (dev.uuid == uuid){
1040
					dev_new = dev;
1041
					break;
1042
				}
1043
			}
1044
			else{
1045
				if (dev.device == device){
1046
					dev_new = dev;
1047
					break;
1048
				}
1049
			}
1050
		}
1051
		
1052
		return dev_new;
1053
	}
1054
	
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1055
	public void query_disk_space(){
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
1056
1057
		/* Updates disk space info and returns the given Device object */
1058
1059
		var list_df = get_disk_space_using_df(device);
1060
		
1061
		var dev_df = find_device_in_list_by_uuid(list_df, uuid);
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
1062
		
341 by Tony George
Added workaround for older versions of lsblk; Fixed support for Ubuntu 14.04 and Mint 17.x;
1063
		if (dev_df != null){
1064
			// update dev fields
1065
			size_bytes = dev_df.size_bytes;
1066
			used_bytes = dev_df.used_bytes;
1067
			available_bytes = dev_df.available_bytes;
1068
			used_percent = dev_df.used_percent;
1069
		}
1070
	}
1071
1072
	// mounting ---------------------------------
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1073
	
1074
	public static bool automount_udisks(string dev_name_or_uuid, Gtk.Window? parent_window){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1075
		
336 by Tony George
Fixed error handling for udisks automount() and unmount()
1076
		if (dev_name_or_uuid.length == 0){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1077
			log_error(_("Device name is empty!"));
336 by Tony George
Fixed error handling for udisks automount() and unmount()
1078
			return false;
1079
		}
1080
		
1081
		var cmd = "udisksctl mount -b '%s'".printf(dev_name_or_uuid);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1082
		log_debug(cmd);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1083
		int status = Posix.system(cmd);
1084
330 by Tony George
Minor improvements in Device class
1085
		if (status != 0){
1086
			if (parent_window != null){
1087
				string msg = "Failed to mount: %s".printf(dev_name_or_uuid);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1088
				gtk_messagebox("Error", msg, parent_window, true);
330 by Tony George
Minor improvements in Device class
1089
			}
1090
		}
1091
1092
		return (status == 0);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1093
	}
1094
1095
	public static bool automount_udisks_iso(string iso_file_path, out string loop_device, Gtk.Window? parent_window){
330 by Tony George
Minor improvements in Device class
1096
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1097
		loop_device = "";
1098
336 by Tony George
Fixed error handling for udisks automount() and unmount()
1099
		if (!file_exists(iso_file_path)){
1100
			string msg = "%s: %s".printf(_("Could not find file"), iso_file_path);
1101
			log_error(msg);
1102
			return false;
1103
		}
1104
1105
		var cmd = "udisksctl loop-setup -r -f '%s'".printf(
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1106
			escape_single_quote(iso_file_path));
1107
			
1108
		log_debug(cmd);
1109
		string std_out, std_err;
1110
		int exit_code = exec_sync(cmd, out std_out, out std_err);
1111
		
1112
		if (exit_code == 0){
1113
			log_msg("%s".printf(std_out));
1114
			//log_msg("%s".printf(std_err));
1115
1116
			if (!std_out.contains(" as ")){
1117
				log_error("Could not determine loop device");
1118
				return false;
1119
			}
1120
1121
			loop_device = std_out.split(" as ")[1].replace(".","").strip();
1122
			log_msg("loop_device: %s".printf(loop_device));
1123
		
1124
			var list = get_block_devices_using_lsblk();
1125
			foreach(var dev in list){
1126
				if ((dev.pkname == loop_device.replace("/dev/","")) && (dev.fstype == "iso9660")){
1127
					loop_device = dev.device;
1128
					return automount_udisks(dev.device, parent_window);
330 by Tony George
Minor improvements in Device class
1129
				}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1130
			}
1131
		}
1132
		
1133
		return false;
1134
	}
1135
1136
	public static bool unmount_udisks(string dev_name_or_uuid, Gtk.Window? parent_window){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1137
336 by Tony George
Fixed error handling for udisks automount() and unmount()
1138
		if (dev_name_or_uuid.length == 0){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1139
			log_error(_("Device name is empty!"));
336 by Tony George
Fixed error handling for udisks automount() and unmount()
1140
			return false;
1141
		}
1142
		
1143
		var cmd = "udisksctl unmount -b '%s'".printf(dev_name_or_uuid);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1144
		log_debug(cmd);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1145
		int status = Posix.system(cmd);
1146
330 by Tony George
Minor improvements in Device class
1147
		if (status != 0){
1148
			if (parent_window != null){
1149
				string msg = "Failed to unmount: %s".printf(dev_name_or_uuid);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1150
				gtk_messagebox("Error", msg, parent_window, true);
330 by Tony George
Minor improvements in Device class
1151
			}
1152
		}
1153
		
1154
		return (status == 0);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1155
	}
1156
1157
	public static Device? luks_unlock(
1158
		Device luks_device, string mapped_name, string passphrase, Gtk.Window? parent_window, 
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1159
		out string message, out string details){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1160
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1161
		/* Unlocks a LUKS device using provided passphrase.
1162
		 * Prompts the user for passphrase if empty.
1163
		 * Displays a GTK prompt if parent_window is not null
1164
		 * Otherwise prompts user on terminal with a timeout of 20 secsonds
1165
		 * */
1166
		 
1167
		Device unlocked_device = null;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1168
		string std_out = "", std_err = "";
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1169
		
1170
		// check if not encrypted
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1171
		if (!luks_device.fstype.contains("luks") && !luks_device.fstype.contains("crypt")){
1172
			message = _("This device is not encrypted");
1173
			details = _("Failed to unlock device");
1174
			return null;
1175
		}
1176
1177
		// check if already unlocked
1178
		var list = get_block_devices_using_lsblk();
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1179
		foreach(var part in list){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1180
			if (part.pkname == luks_device.kname){
1181
				unlocked_device = part;
1182
				message = _("Device is unlocked");
1183
				details = _("Unlocked device is mapped to '%s'").printf(part.mapped_name);
1184
				return part; 
1185
			}
1186
		}
1187
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1188
		string luks_pass = passphrase;
1189
		string luks_name = mapped_name;
1190
1191
		if ((luks_name == null) || (luks_name.length == 0)){
1192
			luks_name = "%s_crypt".printf(luks_device.kname);
1193
		}
1194
1195
		if (parent_window == null){
1196
1197
			// console mode
1198
			
1199
			if ((luks_pass == null) || (luks_pass.length == 0)){
1200
1201
				// prompt user on terminal and unlock, else timeout after 20 secs
1202
				
1203
				var counter = new TimeoutCounter();
1204
				counter.kill_process_on_timeout("cryptsetup", 20, true);
1205
				string cmd = "cryptsetup luksOpen '%s' '%s'".printf(luks_device.device, luks_name);
312 by Tony George
RestoreWindow: Hide unlocked luks partitions from combo; Fixed an issue with unlock_luks(); Log milliseconds in terminal output;
1206
330 by Tony George
Minor improvements in Device class
1207
				log_debug(cmd);
312 by Tony George
RestoreWindow: Hide unlocked luks partitions from combo; Fixed an issue with unlock_luks(); Log milliseconds in terminal output;
1208
				Posix.system(cmd);
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1209
				counter.stop();
1210
				log_msg("");
1211
				
1212
			}
1213
			else{
1214
1215
				// use password to unlock
1216
1217
				var cmd = "echo -n -e '%s' | cryptsetup luksOpen --key-file - '%s' '%s'\n".printf(
1218
					luks_pass, luks_device.device, luks_name);
312 by Tony George
RestoreWindow: Hide unlocked luks partitions from combo; Fixed an issue with unlock_luks(); Log milliseconds in terminal output;
1219
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1220
				log_debug(cmd.replace(luks_pass, "**PASSWORD**"));
312 by Tony George
RestoreWindow: Hide unlocked luks partitions from combo; Fixed an issue with unlock_luks(); Log milliseconds in terminal output;
1221
				
1222
				int status = exec_script_sync(cmd, out std_out, out std_err, false, true);
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1223
1224
				switch (status){
1225
				case 512: // invalid passphrase
1226
					message = _("Wrong password");
1227
					details = _("Failed to unlock device");
1228
					log_error(message);
1229
					log_error(details);
1230
					break;
1231
				}
1232
			}
1233
1234
		}
1235
		else{
1236
1237
			// gui mode
1238
1239
			if ((luks_pass == null) || (luks_pass.length == 0)){
1240
1241
				// show input prompt
1242
312 by Tony George
RestoreWindow: Hide unlocked luks partitions from combo; Fixed an issue with unlock_luks(); Log milliseconds in terminal output;
1243
				log_debug("Prompting user for passphrase..");
1244
				
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1245
				luks_pass = gtk_inputbox(
1246
						_("Encrypted Device"),
1247
						_("Enter passphrase to unlock '%s'").printf(luks_device.name),
1248
						parent_window, true);
1249
1250
				if (luks_pass == null){
1251
					// cancelled by user
1252
					message = _("Failed to unlock device");
1253
					details = _("User cancelled the password prompt");
1254
					log_debug("User cancelled the password prompt");
1255
					return null;
1256
				}
1257
			}
1258
1259
			if ((luks_pass != null) && (luks_pass.length > 0)){
1260
1261
				// use password to unlock
1262
1263
				var cmd = "echo -n -e '%s' | cryptsetup luksOpen --key-file - '%s' '%s'\n".printf(
1264
					luks_pass, luks_device.device, luks_name);
312 by Tony George
RestoreWindow: Hide unlocked luks partitions from combo; Fixed an issue with unlock_luks(); Log milliseconds in terminal output;
1265
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1266
				log_debug(cmd.replace(luks_pass, "**PASSWORD**"));
312 by Tony George
RestoreWindow: Hide unlocked luks partitions from combo; Fixed an issue with unlock_luks(); Log milliseconds in terminal output;
1267
				
1268
				int status = exec_script_sync(cmd, out std_out, out std_err, false, true);
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1269
1270
				switch (status){
1271
				case 512: // invalid passphrase
1272
					message = _("Wrong password");
1273
					details = _("Failed to unlock device");
1274
					log_error(message);
1275
					log_error(details);
1276
					break;
1277
				}
1278
			}
1279
			
1280
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1281
1282
		// find unlocked device
1283
		list = get_block_devices_using_lsblk();
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1284
		foreach(var part in list){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1285
			if (part.pkname == luks_device.kname){
1286
				unlocked_device = part;
1287
				break; 
1288
			}
1289
		}
1290
1291
		bool is_error = false;
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1292
		if (unlocked_device == null){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1293
			message = _("Failed to unlock device") + " '%s'".printf(luks_device.device);
1294
			details = std_err;
1295
			is_error = true;
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1296
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1297
		else{
1298
			message = _("Unlocked successfully");
1299
			details = _("Unlocked device is mapped to '%s'").printf(unlocked_device.mapped_name);
1300
		}
1301
309 by Tony George
Device: Moved the code for unlocking luks devices to Device class; Removed redundant code for unlocking devices;
1302
		if (parent_window != null){
1303
			gtk_messagebox(message, details, parent_window, is_error);
1304
		}
1305
1306
		return unlocked_device;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1307
	}
1308
1309
	public static bool luks_lock(string kname, Gtk.Window? parent_window){
330 by Tony George
Minor improvements in Device class
1310
		var cmd = "cryptsetup luksClose %s".printf(kname);
1311
1312
		log_debug(cmd);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1313
330 by Tony George
Minor improvements in Device class
1314
		string std_out, std_err;
1315
		int status = exec_script_sync(cmd, out std_out, out std_err, false, true);
1316
		log_msg(std_out);
1317
		log_msg(std_err);
1318
		
1319
		if (status != 0){
1320
			if (parent_window != null){
1321
				string msg = "Failed to lock device: %s".printf(kname);
1322
				gtk_messagebox("Error", msg, parent_window, true);
1323
			}
1324
		}
1325
		
1326
		return (status == 0);
1327
		
1328
		/*log_debug(cmd);
1329
		
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1330
		if (bash_admin_shell != null){
1331
			int status = bash_admin_shell.execute(cmd);
1332
			return (status == 0);
1333
		}
1334
		else{
1335
			int status = exec_script_sync(cmd,null,null,false,true);
1336
			return (status == 0);
1337
		}*/
330 by Tony George
Minor improvements in Device class
1338
	}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1339
1340
	public static bool mount(
1341
		string dev_name_or_uuid, string mount_point, string mount_options = "", bool silent = false){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1342
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1343
		/*
1344
		 * Mounts specified device at specified mount point.
1345
		 * 
1346
		 * */
1347
1348
		string cmd = "";
1349
		string std_out;
1350
		string std_err;
1351
		int ret_val;
1352
1353
		string device = "";
1354
		string uuid = "";
1355
1356
		if (dev_name_or_uuid.has_prefix("/dev")){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1357
			device = dev_name_or_uuid;
1358
			uuid = get_device_uuid(dev_name_or_uuid);
1359
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1360
		else{
1361
			uuid = dev_name_or_uuid;
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1362
			device = "/dev/disk/by-uuid/%s".printf(uuid);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1363
			device = resolve_device_name(device);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1364
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1365
1366
		if (dir_exists(mount_point)){
1367
			dir_create(mount_point);
1368
		}
1369
		
1370
		// check if already mounted ------------------
1371
		
1372
		var mps = Device.get_device_mount_points(dev_name_or_uuid);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1373
		foreach(var mp in mps){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1374
			if (mp.mount_point.contains(mount_point)){
1375
				if (!silent){
351 by Tony George
Code cleanup; Display progress in terminal while creating and deleting snapshots; Added --comments option for setting the comment when new snapshot is created; Cleaned-up terminal messages;
1376
					string msg = "";
352 by Tony George
Code cleanup; Fixed crash on updating /etc/crypttab after restore; Fixed an issue in timeshift-gtk where user is prompted for passphrase on terminal instead of GUI;
1377
					msg += "%s: %s %s".printf(_("Mounted"), _("device"), dev_name_or_uuid);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1378
					if (mp.mount_options.length > 0){
352 by Tony George
Code cleanup; Fixed crash on updating /etc/crypttab after restore; Fixed an issue in timeshift-gtk where user is prompted for passphrase on terminal instead of GUI;
1379
						msg += ", %s".printf(mp.mount_options);
1380
					}
1381
					msg += " at '%s'".printf(mount_point);
1382
				}
351 by Tony George
Code cleanup; Display progress in terminal while creating and deleting snapshots; Added --comments option for setting the comment when new snapshot is created; Cleaned-up terminal messages;
1383
				return true;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1384
			}
1385
		}
1386
		
1387
1388
		try{
1389
			// check and create mount point -------------
1390
1391
			File file = File.new_for_path(mount_point);
1392
			if (!file.query_exists()){
1393
				file.make_directory_with_parents();
1394
			}
1395
1396
			// mount the device -----------------------------
1397
1398
			if (mount_options.length > 0){
1399
				cmd = "mount -o %s \"%s\" \"%s\"".printf(mount_options, device, mount_point);
1400
			}
1401
			else{
1402
				cmd = "mount \"%s\" \"%s\"".printf(device, mount_point);
1403
			}
1404
1405
			Process.spawn_command_line_sync(cmd, out std_out, out std_err, out ret_val);
1406
1407
			if (ret_val != 0){
1408
				log_error ("Failed to mount device '%s' at mount point '%s'".printf(device, mount_point));
1409
				log_error (std_err);
1410
				return false;
1411
			}
1412
			else{
1413
				if (!silent){
351 by Tony George
Code cleanup; Display progress in terminal while creating and deleting snapshots; Added --comments option for setting the comment when new snapshot is created; Cleaned-up terminal messages;
1414
					Device dev = get_device_by_name(device);
1415
					log_msg ("Mounted '%s'%s at '%s'".printf(
358 by Tony George
RestoreSummary: Show subvolume name beside device name
1416
						(dev == null) ? device : dev.device_name_with_parent,
1417
						(mount_options.length > 0) ? " (%s)".printf(mount_options) : "",
1418
						mount_point));
1419
				}
351 by Tony George
Code cleanup; Display progress in terminal while creating and deleting snapshots; Added --comments option for setting the comment when new snapshot is created; Cleaned-up terminal messages;
1420
				return true;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1421
			}
1422
		}
1423
		catch(Error e){
1424
			log_error (e.message);
1425
			return false;
1426
		}
1427
1428
		// check if mounted successfully ------------------
1429
1430
		/*mps = Device.get_device_mount_points(dev_name_or_uuid);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1431
		if (mps.contains(mount_point)){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1432
			log_msg("Device '%s' is mounted at '%s'".printf(dev_name_or_uuid, mount_point));
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1433
			return true;
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1434
		}
1435
		else{
1436
			return false;
1437
		}*/
1438
	}
1439
1440
	public static string automount(
1441
		string dev_name_or_uuid, string mount_options = "", string mount_prefix = "/mnt"){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1442
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1443
		/* Returns the mount point of specified device.
1444
		 * If unmounted, mounts the device to /mnt/<uuid> and returns the mount point.
1445
		 * */
1446
1447
		string device = "";
1448
		string uuid = "";
1449
1450
		// get uuid -----------------------------
1451
1452
		if (dev_name_or_uuid.has_prefix("/dev")){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1453
			device = dev_name_or_uuid;
1454
			uuid = Device.get_device_uuid(dev_name_or_uuid);
1455
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1456
		else{
1457
			uuid = dev_name_or_uuid;
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1458
			device = "/dev/disk/by-uuid/%s".printf(uuid);
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1459
			device = resolve_device_name(device);
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1460
		}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1461
1462
		// check if already mounted and return mount point -------------
1463
1464
		var list = Device.get_block_devices_using_lsblk();
1465
		var dev = find_device_in_list_by_uuid(list, uuid);
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
1466
		if (dev != null){
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1467
			return dev.mount_points[0].mount_point;
1468
		}
1469
1470
		// check and create mount point -------------------
1471
1472
		string mount_point = "%s/%s".printf(mount_prefix, uuid);
1473
1474
		try{
1475
			File file = File.new_for_path(mount_point);
1476
			if (!file.query_exists()){
1477
				file.make_directory_with_parents();
1478
			}
1479
		}
1480
		catch(Error e){
1481
			log_error (e.message);
1482
			return "";
1483
		}
1484
1485
		// mount the device and return mount_point --------------------
1486
1487
		if (mount(uuid, mount_point, mount_options)){
1488
			return mount_point;
1489
		}
1490
		else{
1491
			return "";
1492
		}
1493
	}
1494
1495
	public static bool unmount(string mount_point){
1496
1497
		/* Recursively unmounts all devices at given mount_point and subdirectories
1498
		 * */
1499
1500
		string cmd = "";
1501
		string std_out;
1502
		string std_err;
1503
		int ret_val;
1504
1505
		// check if mount point is in use
1506
		if (!Device.mount_point_in_use(mount_point)) {
1507
			return true;
1508
		}
1509
1510
		// try to unmount ------------------
1511
1512
		try{
1513
1514
			string cmd_unmount = "cat /proc/mounts | awk '{print $2}' | grep '%s' | sort -r | xargs umount".printf(mount_point);
1515
1516
			log_debug(_("Unmounting from") + ": '%s'".printf(mount_point));
1517
1518
			//sync before unmount
1519
			cmd = "sync";
1520
			Process.spawn_command_line_sync(cmd, out std_out, out std_err, out ret_val);
1521
			//ignore success/failure
1522
1523
			//unmount
1524
			ret_val = exec_script_sync(cmd_unmount, out std_out, out std_err);
1525
1526
			if (ret_val != 0){
1527
				log_error (_("Failed to unmount"));
1528
				log_error (std_err);
1529
			}
1530
		}
1531
		catch(Error e){
1532
			log_error (e.message);
1533
			return false;
1534
		}
1535
1536
		// check if mount point is in use
1537
		if (!Device.mount_point_in_use(mount_point)) {
1538
			return true;
1539
		}
1540
		else{
1541
			return false;
1542
		}
1543
	}
1544
1545
	// description helpers
360 by Tony George
Fixed: Free space is diplayed as =size when used space is zero; Code cleanup; Fixed GTK warnings on RestoreWindow;
1546
1547
	public string full_name_with_alias{
1548
		owned get{
1549
			string text = device;
1550
			if (mapped_name.length > 0){
1551
				text += " (%s)".printf(mapped_name);
1552
			}
1553
			return text;
1554
		}
1555
	}
1556
1557
	public string full_name_with_parent{
1558
		owned get{
1559
			return device_name_with_parent;
1560
		}
1561
	}
1562
1563
	public string short_name_with_alias{
1564
		owned get{
1565
			string text = kname;
1566
			if (mapped_name.length > 0){
1567
				text += " (%s)".printf(mapped_name);
1568
			}
1569
			return text;
1570
		}
1571
	}
1572
1573
	public string short_name_with_parent{
1574
		owned get{
1575
			string text = kname;
1576
1577
			if (has_parent() && (parent.type == "part")){
1578
				text += " (%s)".printf(pkname);
1579
			}
1580
			
1581
			return text;
1582
		}
1583
	}
1584
1585
	public string device_name_with_parent{
1586
		owned get{
1587
			string text = device;
1588
1589
			if (has_parent() && (parent.type == "part")){
1590
				text += " (%s)".printf(parent.kname);
1591
			}
1592
			
1593
			return text;
1594
		}
1595
	}
1596
1597
	public string description(){
1598
		return description_formatted().replace("<b>","").replace("</b>","");
1599
	}
1600
1601
	public string description_formatted(){
1602
		string s = "";
1603
1604
		if (type == "disk"){
1605
			s += "<b>" + kname + "</b> ~";
1606
			if (vendor.length > 0){
1607
				s += " " + vendor;
1608
			}
1609
			if (model.length > 0){
1610
				s += " " + model;
1611
			}
1612
			if (size_bytes > 0) {
1613
				s += " (%s)".printf(format_file_size(size_bytes, false, "", true, 0));
1614
			}
1615
		}
1616
		else{
1617
			s += "<b>" + short_name_with_parent + "</b>" ;
1618
			s += (label.length > 0) ? " (" + label + ")": "";
1619
			s += (fstype.length > 0) ? " ~ " + fstype : "";
1620
			if (size_bytes > 0) {
1621
				s += " (%s)".printf(format_file_size(size_bytes, false, "", true, 0));
1622
			}
1623
		}
1624
1625
		return s.strip();
1626
	}
1627
	
1628
	public string description_simple(){
1629
		return description_simple_formatted().replace("<b>","").replace("</b>","");
1630
	}
1631
	
1632
	public string description_simple_formatted(){
1633
		
1634
		string s = "";
1635
1636
		if (type == "disk"){
1637
			if (vendor.length > 0){
1638
				s += " " + vendor;
1639
			}
1640
			if (model.length > 0){
1641
				s += " " + model;
1642
			}
1643
			if (size_bytes > 0) {
1644
				if (s.strip().length == 0){
1645
					s += "%s Device".printf(format_file_size(size_bytes, false, "", true, 0));
1646
				}
1647
				else{
1648
					s += " (%s)".printf(format_file_size(size_bytes, false, "", true, 0));
1649
				}
1650
			}
1651
		}
1652
		else{
1653
			s += "<b>" + short_name_with_parent + "</b>" ;
1654
			s += (label.length > 0) ? " (" + label + ")": "";
1655
			s += (fstype.length > 0) ? " ~ " + fstype : "";
1656
			if (size_bytes > 0) {
1657
				s += " (%s)".printf(format_file_size(size_bytes, false, "", true, 0));
1658
			}
1659
		}
1660
1661
		return s.strip();
1662
	}
1663
1664
	public string description_full_free(){
1665
		string s = "";
1666
1667
		if (type == "disk"){
1668
			s += "%s %s".printf(model, vendor).strip();
1669
			if (s.length == 0){
1670
				s = "%s Disk".printf(format_file_size(size_bytes));
1671
			}
1672
			else{
1673
				s += " (%s Disk)".printf(format_file_size(size_bytes));
1674
			}
1675
		}
1676
		else{
1677
			s += kname;
1678
			if (label.length > 0){
1679
				s += " (%s)".printf(label);
1680
			}
1681
			if (fstype.length > 0){
1682
				s += " ~ %s".printf(fstype);
1683
			}
1684
			if (free_bytes > 0){
1685
				s += " ~ %s".printf(description_free());
1686
			}
1687
		}
1688
1689
		return s;
1690
	}
1691
1692
	public string description_full(){
1693
		string s = "";
1694
		s += device;
1695
		s += (label.length > 0) ? " (" + label + ")": "";
1696
		s += (uuid.length > 0) ? " ~ " + uuid : "";
1697
		s += (fstype.length > 0) ? " ~ " + fstype : "";
1698
		s += (used.length > 0) ? " ~ " + used + " / " + size + " GB used (" + used_percent + ")" : "";
1699
		
1700
		return s;
1701
	}
1702
1703
	public string description_usage(){
1704
		if (used.length > 0){
1705
			return used + " / " + size + " used (" + used_percent + ")";
1706
		}
1707
		else{
1708
			return "";
1709
		}
1710
	}
1711
1712
	public string description_free(){
1713
		if (used.length > 0){
1714
			return format_file_size(free_bytes, false, "g", false)
1715
				+ " / " + format_file_size(size_bytes, false, "g", true) + " free";
1716
		}
1717
		else{
1718
			return "";
1719
		}
1720
	}
1721
1722
	public string tooltip_text(){
1723
		string tt = "";
1724
1725
		if (type == "disk"){
1726
			tt += "%-15s: %s\n".printf(_("Device"), device);
1727
			tt += "%-15s: %s\n".printf(_("Vendor"), vendor);
1728
			tt += "%-15s: %s\n".printf(_("Model"), model);
1729
			tt += "%-15s: %s\n".printf(_("Serial"), serial);
1730
			tt += "%-15s: %s\n".printf(_("Revision"), revision);
1731
1732
			tt += "%-15s: %s\n".printf( _("Size"),
1733
				(size_bytes > 0) ? format_file_size(size_bytes) : "N/A");
1734
		}
1735
		else{
1736
			tt += "%-15s: %s\n".printf(_("Device"),
1737
				(mapped_name.length > 0) ? "%s → %s".printf(device, mapped_name) : device);
1738
				
1739
			if (has_parent()){
1740
				tt += "%-15s: %s\n".printf(_("Parent Device"), parent.device);
1741
			}
1742
			tt += "%-15s: %s\n".printf(_("UUID"),uuid);
1743
			tt += "%-15s: %s\n".printf(_("Type"),type);
1744
			tt += "%-15s: %s\n".printf(_("Filesystem"),fstype);
1745
			tt += "%-15s: %s\n".printf(_("Label"),label);
1746
			
1747
			tt += "%-15s: %s\n".printf(_("Size"),
1748
				(size_bytes > 0) ? format_file_size(size_bytes) : "N/A");
1749
				
1750
			tt += "%-15s: %s\n".printf(_("Used"),
1751
				(used_bytes > 0) ? format_file_size(used_bytes) : "N/A");
1752
1753
			tt += "%-15s: %s\n".printf(_("System"),dist_info);
1754
		}
1755
1756
		return "<tt>%s</tt>".printf(tt);
1757
	}
1758
1759
	private string display_name(bool short_name = true, bool show_label = true, bool show_parent = true, bool show_alias = false){
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1760
	
1761
		string txt = "";
1762
1763
		if (short_name){
1764
			txt += kname;
1765
		}
1766
		else{
1767
			txt += device;
1768
		}
1769
1770
		if (type == "disk"){
1771
			if (vendor.length > 0){
1772
				txt += " " + vendor;
1773
			}
1774
			if (model.length > 0){
1775
				txt += " " + model;
1776
			}
1777
			if (size_bytes > 0) {
1778
				if (txt.strip().length == 0){
1779
					txt += "%s Device".printf(format_file_size(size_bytes, false, "", true, 0));
1780
				}
1781
				else{
1782
					txt += " (%s)".printf(format_file_size(size_bytes, false, "", true, 0));
1783
				}
1784
			}
1785
		}
1786
		else{
1787
			if (show_label && (label.length > 0)){
1788
				txt += "(%s)".printf(label);
1789
			}
1790
			if (show_parent && has_parent() && (parent.type == "part")){ // TODO: if parent is crypt (like lvm on luks)
1791
				txt += "(%s)".printf(pkname);
1792
			}
1793
			if (show_alias && (mapped_name.length > 0)){
1794
				txt += "(%s)".printf(mapped_name);
1795
			}
1796
		}
1797
		
1798
		return txt;
1799
	}	
1800
	
1801
	// testing -----------------------------------
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1802
1803
	public static void test_all(){
1804
		var list = get_block_devices_using_lsblk();
1805
		log_msg("\n> get_block_devices_using_lsblk()");
1806
		print_device_list(list);
1807
1808
		log_msg("");
1809
		
1810
		//list = get_block_devices_using_blkid();
1811
		//log_msg("\nget_block_devices_using_blkid()\n");
1812
		//print_device_list(list);
1813
1814
		list = get_mounted_filesystems_using_mtab();
1815
		log_msg("\n> get_mounted_filesystems_using_mtab()");
1816
		print_device_mounts(list);
1817
1818
		log_msg("");
1819
1820
		list = get_disk_space_using_df();
1821
		log_msg("\n> get_disk_space_using_df()");
1822
		print_device_disk_space(list);
1823
1824
		log_msg("");
1825
1826
		list = get_filesystems();
1827
		log_msg("\n> get_filesystems()");
1828
		print_device_list(list);
1829
		print_device_mounts(list);
1830
		print_device_disk_space(list);
1831
		
1832
		log_msg("");
1833
	}
1834
1835
	public static void print_device_list(Gee.ArrayList<Device> list){
1836
1837
		log_debug("");
1838
		
1839
		log_debug("%-12s ,%-5s ,%-5s ,%-36s ,%s".printf(
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1840
			"device",
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1841
			"pkname",
1842
			"kname",
1843
			"uuid",
1844
			"mapped_name"));
1845
1846
		log_debug(string.nfill(100, '-'));
1847
1848
		foreach(var dev in list){
1849
			log_debug("%-12s ,%-5s ,%-5s ,%-36s ,%s".printf(
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1850
				dev.device ,
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1851
				dev.pkname,
1852
				dev.kname,
1853
				dev.uuid,
1854
				dev.mapped_name
1855
				));
1856
		}
1857
1858
		log_debug("");
1859
		
1860
		/*
343 by Tony George
Code cleanup
1861
		log_debug("%-20s %-20s %s %s %s %s".printf(
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1862
			"device",
1863
			"label",
1864
			"vendor",
1865
			"model",
1866
			"serial",
1867
			"rev"));
1868
1869
		log_debug(string.nfill(100, '-'));
1870
1871
		foreach(var dev in list){
1872
			log_debug("%-20s %-20s %s %s %s %s".printf(
1873
				dev.device,
1874
				dev.label,
1875
				dev.vendor,
1876
				dev.model,
1877
				dev.serial,
1878
				dev.revision
1879
				));
1880
		}
1881
1882
		log_debug("");
1883
		*/
343 by Tony George
Code cleanup
1884
		
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1885
		/*log_debug("%-20s %-10s %-15s %-3s %-3s %15s %15s".printf(
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1886
			"device",
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1887
			"type",
1888
			"fstype",
1889
			"REM",
1890
			"RO",
1891
			"size",
1892
			"used"));
1893
1894
		log_debug(string.nfill(100, '-'));
1895
1896
		foreach(var dev in list){
1897
			log_debug("%-20s %-10s %-15s %-3s %-3s %15s %15s".printf(
1898
				dev.device,
1899
				dev.type,
1900
				dev.fstype,
1901
				dev.removable ? "1" : "0",
1902
				dev.read_only ? "1" : "0",
1903
				format_file_size(dev.size_bytes, true),
1904
				format_file_size(dev.used_bytes, true)
1905
				));
1906
		}*/
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1907
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1908
		//log_debug("");
361 by Tony George
RestoreWindow: Fixed some issues in device selection logic; Comboboxes would remain unselected in some scenarios; User will be prompted to unlock default devices before the restore window is displayed;
1909
	}
302 by Tony George
Device: Fixed handling of LVM volumes; Moved Device class to separate source file;
1910
1911
	public static void print_device_mounts(Gee.ArrayList<Device> list){
1912
1913
		log_debug("");
1914
		
1915
		log_debug("%-15s %s".printf(
1916
			"device",
1917
			//"fstype",
1918
			"> mount_points (mount_options)"
1919
		));
1920
1921
		log_debug(string.nfill(100, '-'));
1922
1923
		foreach(var dev in list){
1924
1925
			string mps = "";
1926
			foreach(var mp in dev.mount_points){
1927
				mps += "\n    %s -> ".printf(mp.mount_point);
1928
				if (mp.mount_options.length > 0){
1929
					mps += " %s".printf(mp.mount_options);
1930
				}
1931
			}
1932
1933
			log_debug("%-15s %s".printf(
1934
				dev.device,
1935
				//dev.fstype,
1936
				mps
1937
			));
1938
			
1939
		}
1940
1941
		log_debug("");
1942
	}
1943
1944
	public static void print_device_disk_space(Gee.ArrayList<Device> list){
1945
		log_debug("");
1946
		
1947
		log_debug("%-15s %-12s %15s %15s %15s %10s".printf(
1948
			"device",
1949
			"fstype",
1950
			"size",
1951
			"used",
1952
			"available",
1953
			"used_percent"
1954
		));
1955
1956
		log_debug(string.nfill(100, '-'));
1957
1958
		foreach(var dev in list){
1959
			log_debug("%-15s %-12s %15s %15s %15s %10s".printf(
1960
				dev.device,
1961
				dev.fstype,
1962
				format_file_size(dev.size_bytes, true),
1963
				format_file_size(dev.used_bytes, true),
1964
				format_file_size(dev.available_bytes, true),
1965
				dev.used_percent
1966
			));
1967
		}
1968
1969
		log_debug("");
1970
	}
1971
}
1972
1973
1974
1975
1976
1977
1978
1979