mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-06-10 11:00:22 +00:00
udiskie: unmount drive as superuser
Since storage devices are auto-mounted as root, they also need to be unmounted as root. The assumption here is that this wouldn't have any impact on being able to write to the devices. Fixes #1411 Signed-off-by: Joseph Nuthalapati <njoseph@thoughtworks.com> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
c2dc2cb2e2
commit
ed09028fcd
@ -15,16 +15,18 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Configuration helper for disks manager.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from plinth import utils
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
"""Return parsed command line arguments as dictionary."""
|
||||
@ -40,8 +42,11 @@ def parse_arguments():
|
||||
subparser = subparsers.add_parser(
|
||||
'expand-partition',
|
||||
help='Expand a partition to take adjacent free space')
|
||||
subparser.add_argument(
|
||||
'device', help='Partition which needs to be resized')
|
||||
subparser.add_argument('device',
|
||||
help='Partition which needs to be resized')
|
||||
|
||||
subparser = subparsers.add_parser('eject', help='Eject a storage device')
|
||||
subparser.add_argument('device', help='Path of the device to eject')
|
||||
|
||||
subparsers.required = True
|
||||
return parser.parse_args()
|
||||
@ -219,6 +224,83 @@ def _interpret_unit(value):
|
||||
return int(value)
|
||||
|
||||
|
||||
def subcommand_eject(arguments):
|
||||
"""Eject a device by its path."""
|
||||
device_path = arguments.device
|
||||
drive = eject_drive_of_device(device_path)
|
||||
print(json.dumps(drive))
|
||||
|
||||
|
||||
def _get_options():
|
||||
"""Return the common options used for udisks2 operations."""
|
||||
glib = utils.import_from_gi('GLib', '2.0')
|
||||
options = glib.Variant(
|
||||
'a{sv}', {'auth.no_user_interaction': glib.Variant('b', True)})
|
||||
return options
|
||||
|
||||
|
||||
def eject_drive_of_device(device_path):
|
||||
"""Eject a device after unmounting all of its partitions.
|
||||
|
||||
Return the details (model, vendor) of drives ejected.
|
||||
"""
|
||||
udisks = utils.import_from_gi('UDisks', '2.0')
|
||||
client = udisks.Client.new_sync()
|
||||
object_manager = client.get_object_manager()
|
||||
|
||||
found_objects = [
|
||||
obj for obj in object_manager.get_objects()
|
||||
if obj.get_block() and obj.get_block().props.device == device_path
|
||||
]
|
||||
|
||||
if not found_objects:
|
||||
raise ValueError(
|
||||
_('No such device - {device_path}').format(
|
||||
device_path=device_path))
|
||||
|
||||
obj = found_objects[0]
|
||||
|
||||
# Unmount filesystems
|
||||
block_device = obj.get_block()
|
||||
drive_object_path = block_device.props.drive
|
||||
if drive_object_path != '/':
|
||||
umount_all_filesystems_of_drive(drive_object_path)
|
||||
else:
|
||||
# Block device has not associated drive
|
||||
umount_filesystem(obj.get_filesystem())
|
||||
|
||||
# Eject the drive
|
||||
drive = client.get_drive_for_block(block_device)
|
||||
if drive:
|
||||
drive.call_eject_sync(_get_options(), None)
|
||||
return {
|
||||
'vendor': drive.props.vendor,
|
||||
'model': drive.props.model,
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def umount_filesystem(filesystem):
|
||||
"""Unmount a filesystem """
|
||||
if filesystem and filesystem.props.mount_points:
|
||||
filesystem.call_unmount_sync(_get_options())
|
||||
|
||||
|
||||
def umount_all_filesystems_of_drive(drive_object_path):
|
||||
"""Unmount all filesystems on block devices of a drive."""
|
||||
udisks = utils.import_from_gi('UDisks', '2.0')
|
||||
client = udisks.Client.new_sync()
|
||||
object_manager = client.get_object_manager()
|
||||
|
||||
for obj in object_manager.get_objects():
|
||||
block_device = obj.get_block()
|
||||
if not block_device or block_device.props.drive != drive_object_path:
|
||||
continue
|
||||
|
||||
umount_filesystem(obj.get_filesystem())
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse arguments and perform all duties."""
|
||||
arguments = parse_arguments()
|
||||
|
||||
@ -24,14 +24,6 @@ from plinth import utils
|
||||
from plinth.modules.storage import format_bytes
|
||||
|
||||
|
||||
def _get_options():
|
||||
"""Return the common options used for udisks2 operations."""
|
||||
glib = utils.import_from_gi('GLib', '2.0')
|
||||
options = glib.Variant(
|
||||
'a{sv}', {'auth.no_user_interaction': glib.Variant('b', True)})
|
||||
return options
|
||||
|
||||
|
||||
def list_devices():
|
||||
"""List devices that can be ejected."""
|
||||
udisks = utils.import_from_gi('UDisks', '2.0')
|
||||
@ -70,68 +62,6 @@ def list_devices():
|
||||
return devices
|
||||
|
||||
|
||||
def eject_drive_of_device(device_path):
|
||||
"""Eject a device after unmounting all of its partitions.
|
||||
|
||||
Return the details (model, vendor) of drives ejected.
|
||||
"""
|
||||
udisks = utils.import_from_gi('UDisks', '2.0')
|
||||
client = udisks.Client.new_sync()
|
||||
object_manager = client.get_object_manager()
|
||||
|
||||
found_objects = [
|
||||
obj for obj in object_manager.get_objects()
|
||||
if obj.get_block() and obj.get_block().props.device == device_path
|
||||
]
|
||||
|
||||
if not found_objects:
|
||||
raise ValueError(
|
||||
_('No such device - {device_path}').format(
|
||||
device_path=device_path))
|
||||
|
||||
obj = found_objects[0]
|
||||
|
||||
# Unmount filesystems
|
||||
block_device = obj.get_block()
|
||||
drive_object_path = block_device.props.drive
|
||||
if drive_object_path != '/':
|
||||
umount_all_filesystems_of_drive(drive_object_path)
|
||||
else:
|
||||
# Block device has not associated drive
|
||||
umount_filesystem(obj.get_filesystem())
|
||||
|
||||
# Eject the drive
|
||||
drive = client.get_drive_for_block(block_device)
|
||||
if drive:
|
||||
drive.call_eject_sync(_get_options(), None)
|
||||
return {
|
||||
'vendor': drive.props.vendor,
|
||||
'model': drive.props.model,
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def umount_filesystem(filesystem):
|
||||
"""Unmount a filesystem """
|
||||
if filesystem and filesystem.props.mount_points:
|
||||
filesystem.call_unmount_sync(_get_options())
|
||||
|
||||
|
||||
def umount_all_filesystems_of_drive(drive_object_path):
|
||||
"""Unmount all filesystems on block devices of a drive."""
|
||||
udisks = utils.import_from_gi('UDisks', '2.0')
|
||||
client = udisks.Client.new_sync()
|
||||
object_manager = client.get_object_manager()
|
||||
|
||||
for obj in object_manager.get_objects():
|
||||
block_device = obj.get_block()
|
||||
if not block_device or block_device.props.drive != drive_object_path:
|
||||
continue
|
||||
|
||||
umount_filesystem(obj.get_filesystem())
|
||||
|
||||
|
||||
def get_error_message(error):
|
||||
"""Return an error message given an exception."""
|
||||
udisks = utils.import_from_gi('UDisks', '2.0')
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
Views for storage module.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import urllib.parse
|
||||
|
||||
@ -28,6 +29,7 @@ from django.urls import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from plinth import actions
|
||||
from plinth.modules import storage
|
||||
from plinth.utils import format_lazy, is_user_admin
|
||||
|
||||
@ -125,7 +127,8 @@ def eject(request, device_path):
|
||||
device_path = urllib.parse.unquote(device_path)
|
||||
|
||||
try:
|
||||
drive = udisks2.eject_drive_of_device(device_path)
|
||||
drive = json.loads(
|
||||
actions.superuser_run('storage', ['eject', device_path]))
|
||||
if drive:
|
||||
messages.success(
|
||||
request,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user