-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Support UNC path capacity on Windows #22202
base: master
Are you sure you want to change the base?
Conversation
QT seems doesn't have full support of UNC paths in general, a workaround is to mount it as a drive, mentioned at PCSX2/pcsx2#6258 and https://forum.qt.io/topic/96418/how-to-create-a-folder-and-write-a-file-in-it-using-the-unc-path/2 |
c66a932
to
4a58443
Compare
hi @glassez I have reworked this feature and added a bunch of testcases to avoid impacting normal path handling. |
@@ -209,6 +209,17 @@ Path Utils::Fs::toValidPath(const QString &name, const QString &pad) | |||
|
|||
qint64 Utils::Fs::freeDiskSpaceOnPath(const Path &path) | |||
{ | |||
#if defined(Q_OS_WIN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to further simplify this PR?
I suppose you only need the following to make it work:
bool Path::isUNCPath() const
It will check if the path is a valid UNC path.- This code at
Utils::Fs::freeDiskSpaceOnPath()
I especially would like to retain the old QString cleanPath(const QString &path)
code if it won't block/get in the way of UNC paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree perhaps that the cleanPath(path)
can be done in an more elegant way. Something like path.toStdWString().find_last_not_of(u"\\"_s)+1
can replace the loops.
Few tricky things to consider when distinguishing UNC paths from normal paths:
-
QDir::cleanPath is designed for "normal paths", it normalizes all back slashes into forward slashes and try to eliminate extra slashes.
\\host\drive\folder
will become//host/drive/folder
-
PathIsUNCW()
from win32 api in my 1st iteration is not reliable as well, it returns true whenever the string starts with double back slashes\\
-
Path::rootItem()
andPath::parentPath()
is required inqueryFreeDiskSpace(const Path &path)
in order to traverse through folders for capacity and create new folders when input path does not exist, but the current version depends on forward slashes/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to just declare UNC paths officially unsupported?
@@ -51,6 +51,11 @@ class Path final | |||
bool isAbsolute() const; | |||
bool isRelative() const; | |||
|
|||
#if defined(Q_OS_WIN) | |||
bool isUNCPath() const; | |||
QString getUNCRootPathStr() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it is part of implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it is part of implementation.
So?
|
||
QCOMPARE(Path(uR"(\\nas01\drive)"_s).rootItem(), Path(uR"(\\nas01\drive)"_s)); | ||
QCOMPARE(Path(uR"(\\nas01\drive\xxx)"_s).rootItem(), Path(uR"(\\nas01\drive)"_s)); | ||
QCOMPARE(Path(uR"(\\nas01\drive\xxx\yyy)"_s).rootItem(), Path(uR"(\\nas01\drive)"_s)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why \\nas01\drive
is root item?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is the ordinary usecase where we mount a network drive on windows, are there usecases where a UNC path is usable in form of \\host
without specifying the share component?
I don't have a nas at home handy, the test is done by mounting 2 folder as network drive
C:\Users\Z>net use
New connections will be remembered.
Status Local Remote Network
-------------------------------------------------------------------------------
OK X: \\mydesktop\mount1
Microsoft Windows Network
OK Y: \\mydesktop\mount2
Microsoft Windows Network
The command completed successfully.
edit: Found a doc from microsoft stating that it must have >= 2 components: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dfsc/149a3039-98ce-491a-9268-2f5ddef08192
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't done any tests with this PR yet, but here's what net use
returns on my setup (xxx.xxx.xxx.xxx = IP) with 3x NAS connected.
C:\WINDOWS\system32>net use
New connections will be remembered.
Status Local Remote Network
-------------------------------------------------------------------------------
OK T: \\xxx.xxx.xxx.xxx\Movies Microsoft Windows Network
OK V: \\xxx.xxx.xxx.xxx\NAS3 Microsoft Windows Network
OK W: \\xxx.xxx.xxx.xxx\Tv Shows
Microsoft Windows Network
The command completed successfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
\\nas01\drive
is root item?
Can \\servername\share
not be used like the docs show?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ps. Some preliminary review. Haven't done a full round.
if (isUNCPath()) | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (isUNCPath()) | |
return true; | |
#if defined(Q_OS_WIN) | |
if (isUNCPath()) | |
return true; | |
#endif |
QString cleanPath(const QString &path) | ||
{ | ||
#if defined(Q_OS_WIN) | ||
// QT's cleanPath does not support UNC path | ||
if (isQStrUNCPath(path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this is full validation which is too expensive here. You should use fast/lightweight checks instead. This function doesn't need validation for constructing paths.
bool isQStrUNCPath(const QString &path) | ||
{ | ||
// strict format of \\host\drive\folder with no forbidden char to avoid conflict with normal path | ||
if (path.left(2) != uR"(\\)"_s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use QStringView::startsWith
// strict format of \\host\drive\folder with no forbidden char to avoid conflict with normal path | ||
if (path.left(2) != uR"(\\)"_s) | ||
return false; | ||
const auto lastSlash = path.lastIndexOf(u'\\'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no auto
here please.
const auto lastSlash = path.lastIndexOf(u'\\'); | ||
if ((lastSlash - 1) <= 2) | ||
return false; | ||
const QRegularExpression regex {u"[\\0-\\37:?\"*<>|:/]"_s}; // no drive letter allowed C:/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const QRegularExpression regex {u"[\\0-\\37:?\"*<>|:/]"_s}; // no drive letter allowed C:/ | |
static const QRegularExpression regex {u"[\\0-\\37:?\"*<>|:/]"_s}; // no drive letter allowed C:/ |
if ((lastSlash - 1) <= 2) | ||
return false; | ||
const QRegularExpression regex {u"[\\0-\\37:?\"*<>|:/]"_s}; // no drive letter allowed C:/ | ||
return !regex.match(path.data()).hasMatch(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use QStringView::contains
QString cleanPath(const QString &path) | ||
{ | ||
#if defined(Q_OS_WIN) | ||
// QT's cleanPath does not support UNC path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another idea.
You could try to split the UNC path into 2 fields. First is host-name
and the second is the rest of the path. host-name
will have its own private QString variable in Path
class and the second part will be stored in m_pathStr
. The second part will look like a relative path.
This way you could reuse existing code and you only need to concat the host-name
back when full path is expected in various methods.
@@ -52,8 +52,35 @@ const int PATHLIST_TYPEID = qRegisterMetaType<PathList>("PathList"); | |||
|
|||
namespace | |||
{ | |||
#if defined(Q_OS_WIN) | |||
bool isQStrUNCPath(const QString &path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try to use QStringView
as input parameter type.
// only trim off trailing \ which is meaningless | ||
int index = path.size(); | ||
while (path[index - 1] == u'\\') | ||
--index; | ||
if (index <= (path.size() - 1)) | ||
return path.left(index); | ||
return path; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like this way. All paths are QDir::cleanPath
processed except UNC paths. This creates discrepancy behavior which might create unforeseen surprises in the future.
#if defined(Q_OS_WIN) | ||
bool isUNCPath() const; | ||
QString getUNCRootPathStr() const; | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move them to the bottom of public:
section. To right before private:
section.
@@ -52,8 +52,35 @@ const int PATHLIST_TYPEID = qRegisterMetaType<PathList>("PathList"); | |||
|
|||
namespace | |||
{ | |||
#if defined(Q_OS_WIN) | |||
bool isQStrUNCPath(const QString &path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool isQStrUNCPath(const QString &path) | |
bool isUNCPath(const QString &path) |
WinDirStat supports it, maybe you can look how it has been done: |
Add UNC path capacity calculation capability by invoking Windows API directly.
For #21954,
Can we get a confirmation on linux as well?