From 626128a0fabc11c0c0d18c0b743b6bed802d898c Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 30 Jul 2021 18:38:35 +0200 Subject: [PATCH] device: Remove local storage prints if they've been removed from device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a print we have stored locally is not available in device anymore, we need to cleanup the local database. We do not get a proper DATA_NOT_FOUND error for most devices (indeed, at this point no device does this properly). As such, do this when we see a DATA_NOT_FOUND error and the first time that we get a verify-no-match results on a device which is capable of listing all known prints. Co-Authored-by: Marco Trevisan (TreviƱo) --- src/device.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/fprintd.py | 45 +++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/src/device.c b/src/device.c index 628f2fd..2aa8631 100644 --- a/src/device.c +++ b/src/device.c @@ -101,6 +101,8 @@ typedef struct FpDevice *dev; SessionData *_session; + gboolean local_storage_checked; + guint verify_stop_wait_timeout_id; PolkitAuthority *auth; @@ -1273,6 +1275,93 @@ report_verify_status (FprintDevice *rdev, session->verify_status_reported = TRUE; } +static void +check_local_storage (FprintDevice *rdev, + gboolean found_match, + GError *error) +{ + g_autoptr(GError) err = NULL; + g_autoptr(GPtrArray) device_prints = NULL; + g_autoptr(GPtrArray) host_prints = NULL; + FprintDevicePrivate *priv = fprint_device_get_instance_private (rdev); + unsigned i; + + g_return_if_fail (priv->current_action == ACTION_VERIFY || + priv->current_action == ACTION_IDENTIFY); + + /* This only ever sense if the device can list prints. */ + if (!fp_device_has_feature (priv->dev, FP_DEVICE_FEATURE_STORAGE_LIST)) + return; + + /* We do not have any proper driver that correctly reports DATA_NOT_FOUND + * errors. Only synaptics, but there the feature is being disabled on the + * firmware side. + * As such, just always run a test the first time we get a match failure. + */ + if (g_error_matches (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_NOT_FOUND)) + { + if (priv->local_storage_checked) + g_warning ("Device %s reported that a passed print did not exist during action %d, but we verified the local storage!", + fp_device_get_name (priv->dev), priv->current_action); + else + g_debug ("Device %s reported that a passed print did not exist during action %d", + fp_device_get_name (priv->dev), priv->current_action); + } + else if (error || priv->local_storage_checked) + { + return; + } + else if (!found_match) + { + g_debug ("Device %s failed to match during action %d, verifying local storage", + fp_device_get_name (priv->dev), priv->current_action); + } + else + { + return; + } + + priv->local_storage_checked = TRUE; + + device_prints = fp_device_list_prints_sync (priv->dev, NULL, &err); + if (!device_prints) + { + g_warning ("Failed to query prints: %s", err->message); + return; + } + + host_prints = load_all_prints (rdev); + + for (i = 0; i < host_prints->len; i++) + { + FpPrint *print = g_ptr_array_index (host_prints, i); + int r; + + if (g_ptr_array_find_with_equal_func (device_prints, + print, + (GEqualFunc) fp_print_equal, + NULL)) + continue; + + /* Print not known by device, remove locally */ + if ((r = store.print_data_delete (priv->dev, + fp_print_get_finger (print), + fp_print_get_username (print))) == 0) + { + g_message ("Deleted stored finger %d for user %s as it is unknown to device.", + fp_print_get_finger (print), + fp_print_get_username (print)); + } + else + { + g_warning ("Error deleting finger %d for user %s that is unknown to device: %d!", + fp_print_get_finger (print), + fp_print_get_username (print), + r); + } + } +} + static gboolean can_start_action (FprintDevice *rdev, GError **error) { @@ -1489,6 +1578,8 @@ verify_cb (FpDevice *dev, GAsyncResult *res, void *user_data) error->message); } + check_local_storage (rdev, match, error); + stoppable_action_completed (rdev); } } @@ -1532,6 +1623,8 @@ identify_cb (FpDevice *dev, GAsyncResult *res, void *user_data) error->message); } + check_local_storage (rdev, match != NULL, error); + stoppable_action_completed (rdev); } } diff --git a/tests/fprintd.py b/tests/fprintd.py index 0cf0d6e..a274793 100644 --- a/tests/fprintd.py +++ b/tests/fprintd.py @@ -1086,6 +1086,51 @@ class FPrintdVirtualStorageDeviceTests(FPrintdVirtualStorageDeviceBaseTest): prints = self.get_stored_prints() self.assertEqual(set(prints), set(garbage_prints)) + def test_local_storage_cleanup_data_error(self): + # Enroll a print and delete it + self.enroll_print('deleted-print', finger='left-thumb') + self.send_command('REMOVE', 'deleted-print') + + # Note: would be thrown anyway by the storage device if we scan something + self.send_error(FPrint.DeviceError.DATA_NOT_FOUND) + self.device.VerifyStart('(s)', 'any') + + self.wait_for_result('verify-no-match') + self.device.VerifyStop() + + # At this point, there is no print left + with self.assertFprintError('NoEnrolledPrints'): + self.device.ListEnrolledFingers('(s)', 'testuser') + + def test_local_storage_cleanup_no_match(self): + # Enroll a print and delete it + self.enroll_print('existing-print', finger='right-index-finger') + self.enroll_print('deleted-print', finger='left-thumb') + self.send_command('REMOVE', 'deleted-print') + + # We need to send a print that is known to the device + self.send_image('other-print') + self.device.VerifyStart('(s)', 'right-index-finger') + + self.wait_for_result('verify-no-match') + self.device.VerifyStop() + + # At this point, the deleted print has disappeared + self.assertEqual(set(self.device.ListEnrolledFingers('(s)', 'testuser')), {'right-index-finger'}) + + # Now, do the same thing, and the print will not be deleted + self.enroll_print('deleted-print', finger='left-thumb') + self.send_command('REMOVE', 'deleted-print') + + self.send_image('other-print') + self.device.VerifyStart('(s)', 'right-index-finger') + + self.wait_for_result('verify-no-match') + self.device.VerifyStop() + + # At this point, the deleted print is still there + self.assertEqual(set(self.device.ListEnrolledFingers('(s)', 'testuser')), {'right-index-finger', 'left-thumb'}) + def test_enroll_with_one_stage_only(self): self._maybe_reduce_enroll_stages(stages=1)