Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow GUI to only mount as a drive letter on Windows #450

Merged
merged 4 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 51 additions & 7 deletions gui/mountPrimaryWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from sys import platform
import os
from shutil import which
import ctypes
import string

# Import QT libraries
from PySide6.QtCore import Qt, QSettings
Expand Down Expand Up @@ -69,13 +71,23 @@ def __init__(self):
# https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#file-and-directory-names
# Disallow the following [<,>,.,",|,?,*] - note, we still need directory characters to declare a path
self.lineEdit_mountPoint.setValidator(QtGui.QRegularExpressionValidator(r'^[^<>."|?\0*]*$',self))
self.button_driveLetter = QtWidgets.QPushButton('Drive Letter')
self.button_driveLetter.setToolTip('Select an unused drive letter for mounting')

self.horizontalLayout_3.addWidget(self.button_driveLetter)
self.button_driveLetter.clicked.connect(self.chooseDriveLetter)
else:
# Allow anything BUT Nul
# Note: Different versions of Python don't like the embedded null character, send in the raw string instead
self.lineEdit_mountPoint.setValidator(QtGui.QRegularExpressionValidator(r'^[^\0]*$',self))

self.button_browse = QtWidgets.QPushButton('Browse')
self.button_browse.setToolTip('Browse to a pre-existing directory to mount')

self.horizontalLayout_3.addWidget(self.button_browse)
self.button_browse.clicked.connect(self.getFileDirInput)

# Set up the signals for all the interactive entities
self.button_browse.clicked.connect(self.getFileDirInput)
self.button_config.clicked.connect(self.showSettingsWidget)
self.button_mount.clicked.connect(self.mountBucket)
self.button_unmount.clicked.connect(self.unmountBucket)
Expand All @@ -84,11 +96,9 @@ def __init__(self):
self.lineEdit_mountPoint.editingFinished.connect(self.updateMountPointInSettings)
self.dropDown_bucketSelect.currentIndexChanged.connect(self.modifyPipeline)
if platform == 'win32':
self.lineEdit_mountPoint.setToolTip('Designate a new location to mount the bucket, do not create the directory')
self.button_browse.setToolTip("Browse to a new location but don't create a new directory")
self.lineEdit_mountPoint.setToolTip('Designate a drive letter to mount to')
else:
self.lineEdit_mountPoint.setToolTip('Designate a location to mount the bucket - the directory must already exist')
self.button_browse.setToolTip('Browse to a pre-existing directory')

def checkConfigDirectory(self):
workingDir = self.getWorkingDir()
Expand Down Expand Up @@ -162,12 +172,16 @@ def mountBucket(self):
except ValueError as e:
self.addOutputText(f"Invalid mount path: {str(e)}")
return
directory = os.path.join(directory, mountDirSuffix)
# get config path
configPath = os.path.join(self.getWorkingDir(), 'config.yaml')

# on Windows, the mount directory should not exist (yet)
if platform == 'win32':
drive, tail = os.path.splitdrive(directory)
# Only append the cloudfuse suffix if not mounting to a drive letter
if not(drive and (tail == '' or tail in ['\\', '/'])):
directory = os.path.join(directory, mountDirSuffix)

if os.path.exists(directory):
self.addOutputText(f"Directory {directory} already exists! Aborting new mount.")
self.errorMessageBox(f"Error: Cloudfuse needs to create the directory {directory}, but it already exists!")
Expand Down Expand Up @@ -226,7 +240,10 @@ def unmountBucket(self):
directory = str(self.lineEdit_mountPoint.text())
commandParts = []
# TODO: properly handle unmount. This is relying on the line_edit not being changed by the user.
directory = os.path.join(directory, mountDirSuffix)
drive, tail = os.path.splitdrive(directory)
# Only append the cloudfuse suffix if not mounting to a drive letter
if not(drive and (tail == '' or tail in ['\\', '/'])):
directory = os.path.join(directory, mountDirSuffix)
commandParts = [cloudfuseCli, 'unmount', directory]
if platform != 'win32':
commandParts.append('--lazy')
Expand Down Expand Up @@ -267,7 +284,7 @@ def runCommand(self, commandParts):
# run command
try:
process = subprocess.run(
commandParts,
commandParts,
capture_output=True,
creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0)
stdOut = process.stdout.decode().strip()
Expand All @@ -291,3 +308,30 @@ def errorMessageBox(self, messageString, titleString='Error'):
msg.setText(messageString)
# Show the message box
msg.exec()

def chooseDriveLetter(self):
unused_letters = get_unused_driver_letters()
if not unused_letters:
QtWidgets.QMessageBox.warning(self, 'No Drive Letters', 'No unused drive letters available.')
return

drive, ok = QtWidgets.QInputDialog.getItem(
self,
'Select Drive Letter',
'Available drive letters:',
unused_letters,
0,
False
)
if ok and drive:
self.lineEdit_mountPoint.setText(f"{drive}")
self.updateMountPointInSettings()

def get_unused_driver_letters():
bitmask = ctypes.windll.kernel32.GetLogicalDrives()
unused = []
for letter in string.ascii_uppercase:
if not ((bitmask & 1) or (letter == 'A' or letter == 'B')):
unused.append(letter + ':')
bitmask >>=1
return unused
12 changes: 1 addition & 11 deletions gui/mountPrimaryWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,7 @@
<item>
<widget class="QLineEdit" name="lineEdit_mountPoint">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Designate a location to mount the bucket - the directory must exist on Linux, but not on Windows&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button_browse">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Choose a location to mount the bucket - the directory must exist on Linux, but not on Windows&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Browse</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Designate a location to mount the bucket&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
Expand Down
Loading