[baobab/admin-scan] Add ability to scan as administrator



commit ce44cadab0db3eb988e50550f0dbf63476a922ca
Author: Stefano Facchini <stefano facchini gmail com>
Date:   Sat Jun 13 18:52:43 2020 +0200

    Add ability to scan as administrator
    
    When we encounter an inaccessible subdirectory, we try the GIO URI "admin:///path/to/directory".
    If the user fails to authenticate, we just go on with the regular scan.
    
    Since the "admin://" scheme is used only for subdirectories that can't be read as
    a regular user, it shouldn't be *too* much slower, hopefully.

 data/ui/baobab-main-window.ui | 42 +++++++++++++++++++++++++++-------
 src/baobab-application.vala   |  2 +-
 src/baobab-scanner.vala       | 45 +++++++++++++++++++++++++++++++++++--
 src/baobab-window.vala        | 52 +++++++++++++++++++++----------------------
 4 files changed, 103 insertions(+), 38 deletions(-)
---
diff --git a/data/ui/baobab-main-window.ui b/data/ui/baobab-main-window.ui
index fbded4c..e48dbaa 100644
--- a/data/ui/baobab-main-window.ui
+++ b/data/ui/baobab-main-window.ui
@@ -27,6 +27,16 @@
       </item>
     </section>
   </menu>
+  <menu id="rescan_menu">
+    <item>
+      <attribute name="label" translatable="yes">Rescan</attribute>
+      <attribute name="action">win.rescan</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">Rescan as administrator…</attribute>
+      <attribute name="action">win.rescan_as_admin</attribute>
+    </item>
+  </menu>
   <object class="GtkMenu" id="treeview_popup_menu">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -86,18 +96,34 @@
       </packing>
     </child>
     <child>
-      <object class="GtkButton" id="reload_button">
-        <property name="valign">center</property>
-        <property name="can_focus">True</property>
-        <property name="action_name">win.reload</property>
+      <object class="GtkBox" id="rescan_box">
         <style>
-          <class name="image-button"/>
+          <class name="linked"/>
         </style>
         <child>
-          <object class="GtkImage" id="reload_button_image">
+          <object class="GtkButton" id="rescan_button">
             <property name="visible">True</property>
-            <property name="icon_size">1</property>
-            <property name="icon_name">view-refresh-symbolic</property>
+            <property name="valign">center</property>
+            <property name="can_focus">True</property>
+            <property name="action_name">win.rescan</property>
+            <style>
+              <class name="image-button"/>
+            </style>
+            <child>
+              <object class="GtkImage" id="reload_button_image">
+                <property name="visible">True</property>
+                <property name="icon_size">1</property>
+                <property name="icon_name">view-refresh-symbolic</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkMenuButton" id="rescan_menu_button">
+            <property name="visible">True</property>
+            <property name="valign">center</property>
+            <property name="menu_model">rescan_menu</property>
+            <property name="can_focus">True</property>
           </object>
         </child>
       </object>
diff --git a/src/baobab-application.vala b/src/baobab-application.vala
index 50492ef..c491cc7 100644
--- a/src/baobab-application.vala
+++ b/src/baobab-application.vala
@@ -83,7 +83,7 @@ namespace Baobab {
             set_accels_for_action ("win.show-home-page", { "<Alt>Left" });
             set_accels_for_action ("win.show-primary-menu", { "F10" });
             set_accels_for_action ("win.scan-folder", { "<Primary>o" });
-            set_accels_for_action ("win.reload", { "<Primary>r" });
+            set_accels_for_action ("win.rescan", { "<Primary>r" });
             set_accels_for_action ("win.help", { "F1" });
             set_accels_for_action ("app.quit", { "<Primary>q" });
         }
diff --git a/src/baobab-scanner.vala b/src/baobab-scanner.vala
index b3ba68c..71abe06 100644
--- a/src/baobab-scanner.vala
+++ b/src/baobab-scanner.vala
@@ -86,8 +86,12 @@ namespace Baobab {
             FileAttribute.UNIX_NLINK + "," +
             FileAttribute.UNIX_INODE + "," +
             FileAttribute.UNIX_DEVICE + "," +
+            FileAttribute.ACCESS_CAN_EXECUTE + "," +
             FileAttribute.ACCESS_CAN_READ;
 
+        bool scan_as_admin = false;
+        bool auth_failed = false;
+
         [Compact]
         class HardLink {
             internal uint64 inode;
@@ -265,9 +269,21 @@ namespace Baobab {
 
             var results = new Results (info, parent);
 
+            File admin_directory = directory;
+            bool try_admin_access = scan_as_admin &&
+                                    !(info.get_attribute_boolean (FileAttribute.ACCESS_CAN_READ) &&
+                                      info.get_attribute_boolean (FileAttribute.ACCESS_CAN_EXECUTE)) &&
+                                    !auth_failed;
+            if (try_admin_access) {
+                admin_directory = File.new_for_uri ("admin://" + directory.get_path ());
+            }
+
             try {
-                add_children (directory, results, results_array);
+                add_children (admin_directory, results, results_array);
             } catch (Error e) {
+                if (e is IOError.PERMISSION_DENIED) {
+                    auth_failed = true;
+                }
                 results.error = e;
             }
 
@@ -287,6 +303,23 @@ namespace Baobab {
             return results;
         }
 
+        void mount_enclosing_volume_sync (File file) {
+            var context = MainContext.get_thread_default ();
+            var loop = new MainLoop (context);
+
+            var operation = new Gtk.MountOperation (null);
+            file.mount_enclosing_volume.begin (MountMountFlags.NONE, operation, cancellable, (obj, res) => {
+                try {
+                    file.mount_enclosing_volume.end (res);
+                } catch (Error e) {
+                }
+
+                loop.quit ();
+            });
+
+            loop.run ();
+        }
+
         void* scan_in_thread () {
             try {
                 var array = new ResultsArray ();
@@ -424,16 +457,24 @@ namespace Baobab {
 
             cancellable.reset ();
             scan_error = null;
+            auth_failed = false;
         }
 
-        public void scan (bool force) {
+        public void scan (bool force, bool scan_as_admin=false) {
             if (force) {
                 successful = false;
             }
 
+            this.scan_as_admin = scan_as_admin;
+
             if (!successful) {
                 cancel_and_reset ();
 
+                if (scan_as_admin) {
+                    var admin_directory = File.new_for_uri ("admin://" + directory.get_path ());
+                    mount_enclosing_volume_sync (admin_directory);
+                }
+
                 // the thread owns a reference on the Scanner object
                 this.self = this;
 
diff --git a/src/baobab-window.vala b/src/baobab-window.vala
index 5674f2e..165f5a3 100644
--- a/src/baobab-window.vala
+++ b/src/baobab-window.vala
@@ -31,7 +31,7 @@ namespace Baobab {
         [GtkChild]
         private Gtk.Button back_button;
         [GtkChild]
-        private Gtk.Button reload_button;
+        private Gtk.Box rescan_box;
         [GtkChild]
         private Gtk.MenuButton menu_button;
         [GtkChild]
@@ -81,7 +81,8 @@ namespace Baobab {
             { "show-primary-menu", on_show_primary_menu_activate, null, "false", null },
             { "show-home-page", on_show_home_page_activate },
             { "scan-folder", on_scan_folder_activate },
-            { "reload", on_reload_activate },
+            { "rescan", on_rescan_activate },
+            { "rescan_as_admin", on_rescan_as_admin_activate },
             { "clear-recent", on_clear_recent },
             { "help", on_help_activate },
             { "about", on_about_activate }
@@ -251,13 +252,12 @@ namespace Baobab {
             });
         }
 
-        void on_reload_activate () {
-            if (active_location != null) {
-                if (active_location.scanner != null) {
-                    active_location.scanner.cancel ();
-                }
-                scan_active_location (true);
-            }
+        void on_rescan_activate () {
+            scan_active_location (true);
+        }
+
+        void on_rescan_as_admin_activate () {
+            scan_active_location (true, true);
         }
 
         void on_clear_recent () {
@@ -469,18 +469,15 @@ namespace Baobab {
 
         void set_ui_state (Gtk.Widget child, bool busy) {
             menu_button.visible = (child == home_page);
-            reload_button.visible = (child == result_page);
+            rescan_box.visible = (child == result_page);
             back_button.visible = (child == result_page);
 
             set_busy (busy);
 
             if (child == home_page) {
-                var action = lookup_action ("reload") as SimpleAction;
-                action.set_enabled (false);
                 header_bar.title = _("Devices & Locations");
+                active_location = null;
             } else {
-                var action = lookup_action ("reload") as SimpleAction;
-                action.set_enabled (true);
                 header_bar.title = active_location.name;
             }
 
@@ -509,9 +506,17 @@ namespace Baobab {
             treemap_chart.model = model;
         }
 
-        void scan_active_location (bool force) {
+        void scan_active_location (bool force, bool scan_as_admin=false) {
+            if (active_location == null) {
+                return;
+            }
+
             var scanner = active_location.scanner;
 
+            if (scanner != null) {
+                scanner.cancel ();
+            }
+
             scan_completed_handler = scanner.completed.connect(() => {
                 if (scan_completed_handler > 0) {
                     scanner.disconnect (scan_completed_handler);
@@ -523,18 +528,11 @@ namespace Baobab {
                 } catch (IOError.CANCELLED e) {
                     // Handle cancellation silently
                     return;
+                } catch (IOError.PERMISSION_DENIED e) {
+                    // Do nothing
                 } catch (Error e) {
-                    Gtk.TreeIter iter;
-                    Scanner.State state;
-                    scanner.get_iter_first (out iter);
-                    scanner.get (iter, Scanner.Columns.STATE, out state);
-                    if (state == Scanner.State.ERROR) {
-                        var primary = _("Could not scan folder “%s”").printf 
(scanner.directory.get_parse_name ());
-                        message (primary, e.message, Gtk.MessageType.ERROR);
-                    } else {
-                        var primary = _("Could not scan some of the folders contained in “%s”").printf 
(scanner.directory.get_parse_name ());
-                        message (primary, e.message, Gtk.MessageType.WARNING);
-                    }
+                    var primary = _("An error occured while scanning “%s”").printf 
(scanner.directory.get_parse_name ());
+                    message (primary, e.message, Gtk.MessageType.ERROR);
                 }
 
                 set_chart_model (active_location.scanner);
@@ -555,7 +553,7 @@ namespace Baobab {
             clear_message ();
             set_ui_state (result_page, true);
 
-            scanner.scan (force);
+            scanner.scan (force, scan_as_admin);
 
             treeview.model = scanner;
             expand_first_row ();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]