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