def _unmount_as_user_fstab_fail(self, pipe, uid, gid, device): """ Try to unmount @device as user with given @uid and @gid. @device shouldn't be listed in /etc/fstab when running this, so this is expected to fail. """ os.setresgid(gid, gid, gid) os.setresuid(uid, uid, uid) # try to mount the device -- it should fail try: safe_dbus.call_sync(self.iface_prefix, self.path_prefix + '/block_devices/' + os.path.basename(device), self.iface_prefix + '.Filesystem', 'Unmount', GLib.Variant('(a{sv})', ({},))) except Exception as e: msg = 'GDBus.Error:org.freedesktop.UDisks2.Error.NotAuthorizedCanObtain: Not authorized to perform operation' if msg in str(e): pipe.send([True, '']) pipe.close() return else: pipe.send([False, 'Unmount DBus call failed with unexpected exception: %s' % str(e)]) pipe.close() return ret, _out = self.run_command('grep \"%s\" /proc/mounts' % device) if ret == 0: pipe.send([False, 'Unmount DBus call didn\'t fail but %s seems to be still mounted.' % device]) pipe.close() return else: pipe.send([False, '%s was unmounted for UID %d without proper record in fstab' % (device, uid)]) pipe.close() return
def _mount_as_user_fstab(self, pipe, uid, gid, device): """ Try to mount and then unmount @device as user with given @uid and @gid. @device should be listed in /etc/fstab with proper options so user is able to run these operations and this shouldn't fail. """ os.setresgid(gid, gid, gid) os.setresuid(uid, uid, uid) # try to mount the device try: safe_dbus.call_sync(self.iface_prefix, self.path_prefix + '/block_devices/' + os.path.basename(device), self.iface_prefix + '.Filesystem', 'Mount', GLib.Variant('(a{sv})', ({},))) except Exception as e: pipe.send([False, 'Mount DBus call failed: %s' % str(e)]) pipe.close() return ret, out = self.run_command('grep \"%s\" /proc/mounts' % device) if ret != 0: pipe.send([False, '%s not mounted' % device]) pipe.close() return if 'uid=%s,gid=%s' % (uid, gid) not in out: pipe.send([False, '%s not mounted with given uid/gid.\nMount info: %s' % (device, out)]) pipe.close() return pipe.send([True, '']) pipe.close() return
def _unmount_as_user_fstab(self, pipe, uid, gid, device): """ Try to unmount @device as user with given @uid and @gid. @device should be listed in /etc/fstab with "users" option when running this, so this is expected to succeed. """ os.setresgid(gid, gid, gid) os.setresuid(uid, uid, uid) # try to unmount the device try: safe_dbus.call_sync(self.iface_prefix, self.path_prefix + '/block_devices/' + os.path.basename(device), self.iface_prefix + '.Filesystem', 'Unmount', GLib.Variant('(a{sv})', ({},))) except Exception as e: pipe.send([False, 'Unmount DBus call failed: %s' % str(e)]) pipe.close() return ret, _out = self.run_command('grep \"%s\" /proc/mounts' % device) if ret == 0: pipe.send([False, 'Unmount DBus call didn\'t fail but %s seems to be still mounted.' % device]) pipe.close() return else: pipe.send([True, '']) pipe.close() return
def _secure_erase(self, devname): try: safe_dbus.call_sync(self.iface_prefix, self.path_prefix + '/block_devices/' + devname, self.iface_prefix + '.Block', 'Format', GLib.Variant('(sa{sv})', ('empty', {'erase': GLib.Variant("s", 'zero')}))) except Exception as e: self.exception = e
def test_job(self): '''Test basic Job functionality and properties''' disk_name = os.path.basename(self.vdevs[0]) obj_path = self.path_prefix + '/block_devices/' + disk_name start_time = time.time() watch_thread = threading.Thread(target=self._wait_for_job_thread, args=('format-erase', obj_path)) watch_thread.start() erase_thread = threading.Thread(target=self._secure_erase, args=(disk_name, )) erase_thread.start() erase_thread.join() # erase thread finished, stop job searching watch_thread.run = False watch_thread.join() # unexpected exception occured in erase thread -- raise it # timeout reached is actually ok -- zeroing the device may take a long # time, but we just want to check the job object if self.exception is not None and not str( self.exception).endswith('Timeout was reached'): raise self.exception # we should have the job dict now self.assertIsNotNone(self.job) # get all the properties from the dict properties = self.job[1][self.iface_prefix + '.Job'] _ret, disk_size = self.run_command( 'lsblk -d -b -no SIZE %s' % self.vdevs[0]) # get size of the device self.assertEqual(properties['Bytes'], int(disk_size)) self.assertEqual(properties['StartedByUID'], os.getuid()) # test start time -- should be less than ~0.5s after thread started # (dbus property is in micro seconds) self.assertLessEqual(abs(properties['StartTime'] - start_time * 10**6), 0.5 * 10**6) self.assertTrue(properties['Cancelable']) # job still exists -- erase call timed out -- try to cancel it if safe_dbus.check_object_available(self.iface_prefix, self.job[0], self.iface_prefix + '.Job'): try: safe_dbus.call_sync(self.iface_prefix, self.job[0], self.iface_prefix + '.Job', 'Cancel', GLib.Variant('(a{sv})', ({}, ))) except safe_dbus.DBusCallError: pass
def _mount_as_user_fstab(self, pipe, uid, gid, device): """ Try to mount and then unmount @device as user with given @uid and @gid. @device should be listed in /etc/fstab with proper options so user is able to run these operations and this shouldn't fail. """ os.setresgid(gid, gid, gid) os.setresuid(uid, uid, uid) # try to mount the device try: safe_dbus.call_sync(self.iface_prefix, self.path_prefix + '/block_devices/' + os.path.basename(device), self.iface_prefix + '.Filesystem', 'Mount', GLib.Variant('(a{sv})', ({},))) except Exception as e: pipe.send([False, 'Mount DBus call failed: %s' % str(e)]) pipe.close() return ret, out = self.run_command('grep \"%s\" /proc/mounts' % device) if ret != 0: pipe.send([False, '%s not mounted' % device]) pipe.close() return if 'uid=%s,gid=%s' % (uid, gid) not in out: pipe.send([False, '%s not mounted with given uid/gid.\nMount info: %s' % (device, out)]) pipe.close() return # and now try to unmount it try: safe_dbus.call_sync(self.iface_prefix, self.path_prefix + '/block_devices/' + os.path.basename(device), self.iface_prefix + '.Filesystem', 'Unmount', GLib.Variant('(a{sv})', ({},))) except Exception as e: pipe.send([False, 'Unmount DBus call failed: %s' % str(e)]) pipe.close() return ret, _out = self.run_command('grep \"%s\" /proc/mounts' % device) if ret == 0: pipe.send([False, '%s mounted after unmount called' % device]) pipe.close() return pipe.send([True, '']) pipe.close() return
def _get_objects(self): objects = safe_dbus.call_sync(self.iface_prefix, self.path_prefix, 'org.freedesktop.DBus.ObjectManager', 'GetManagedObjects', None) return objects
def test_cancel(self): '''Test if it's possible to cancel the Job''' disk_name = os.path.basename(self.vdevs[0]) obj_path = self.path_prefix + '/block_devices/' + disk_name watch_thread = threading.Thread(target=self._wait_for_job_thread, args=('format-erase', obj_path)) watch_thread.start() erase_thread = threading.Thread(target=self._secure_erase, args=(disk_name, )) erase_thread.start() # watch thread should end first -- we need the job before erase finishes watch_thread.join(timeout=10) if not self.job: watch_thread.run = False # stop the watch thread now if self.exception: # exception in erase thread -- just raise it raise self.exception else: # didn't find the job but no exception self.fail('Failed to find the job objects.') job_path = self.job[0] # cancel the job safe_dbus.call_sync(self.iface_prefix, job_path, self.iface_prefix + '.Job', 'Cancel', GLib.Variant('(a{sv})', ({}, ))) # job shouldn't be in managed objects objects = self._get_objects() self.assertNotIn(job_path, objects[0].items()) erase_thread.join() # erase thread should raise exception (it's stored in self.exception) self.assertIsNotNone(self.exception) self.assertTrue(isinstance(self.exception, safe_dbus.DBusCallError)) self.assertIn('Error erasing device: Job was canceled', str(self.exception))
def test_cancel(self): '''Test if it's possible to cancel the Job''' disk_name = os.path.basename(self.vdevs[0]) obj_path = self.path_prefix + '/block_devices/' + disk_name watch_thread = threading.Thread(target=self._wait_for_job_thread, args=('format-erase', obj_path)) watch_thread.start() erase_thread = threading.Thread(target=self._secure_erase, args=(disk_name,)) erase_thread.start() # watch thread should end first -- we need the job before erase finishes watch_thread.join(timeout=10) if not self.job: watch_thread.run = False # stop the watch thread now if self.exception: # exception in erase thread -- just raise it raise self.exception else: # didn't find the job but no exception self.fail('Failed to find the job objects.') job_path = self.job[0] # cancel the job safe_dbus.call_sync(self.iface_prefix, job_path, self.iface_prefix + '.Job', 'Cancel', GLib.Variant('(a{sv})', ({},))) # job shouldn't be in managed objects objects = self._get_objects() self.assertNotIn(job_path, objects[0].items()) erase_thread.join() # erase thread should raise exception (it's stored in self.exception) self.assertIsNotNone(self.exception) self.assertTrue(isinstance(self.exception, safe_dbus.DBusCallError)) self.assertIn('Error erasing device: Job was canceled', str(self.exception))