Bug 1536796 - P2 - Handle disable string paring in nsLocalFileWin; r=sg,dom-workers-and-storage-reviewers,froydnj,janv

In the Windows API, the maximum length for a path is MAX_PATH in general.
However, the Windows API has many functions that also have Unicode versions to
permit an extended-length path for a maximum total path length of 32,767
characters. To specify an extended-length path, use the "\\?\" prefix.

A path component which ends with a dot is not allowed for Windows API. However,
using the "\\?\" prefix can also resolved this issue.

This patch aims to fix the issues which are mentioned above by prepending the
prefix to the path of nsLocalFile if mDisableStringParsing is set to true.

Differential Revision: https://phabricator.services.mozilla.com/D67014

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Tom Tung 2020-04-01 07:51:05 +00:00
parent 4debe01abb
commit 4e96941288
3 changed files with 140 additions and 16 deletions

View File

@ -19,6 +19,8 @@ bool IsAllowedPath(const nsAString& aFilePath);
bool IsAllowedPath(const nsACString& aFilePath);
#endif
extern const char kPathSeparator;
namespace testing {
void SetBlockUNCPaths(bool aBlock);

View File

@ -59,6 +59,7 @@
#include "mozilla/WidgetUtils.h"
using namespace mozilla;
using mozilla::FilePreferences::kPathSeparator;
#define CHECK_mWorkingPath() \
do { \
@ -78,6 +79,39 @@ using namespace mozilla;
# define DRIVE_REMOTE 4
#endif
constexpr auto kDevicePathSpecifier = NS_LITERAL_STRING("\\\\?\\");
namespace {
bool StartsWithDiskDesignatorAndBackslash(const nsAString& aAbsolutePath) {
// aAbsolutePath can only be (in regular expression):
// UNC path: ^\\\\.*
// A single backslash: ^\\.*
// A disk designator with a backslash: ^[A-Za-z]:\\.*
return aAbsolutePath.Length() >= 3 && aAbsolutePath.CharAt(1) == L':' &&
aAbsolutePath.CharAt(2) == kPathSeparator;
}
nsresult NewLocalFile(const nsAString& aPath, bool aFollowLinks,
bool aUseDOSDevicePathSyntax, nsIFile** aResult) {
RefPtr<nsLocalFile> file = new nsLocalFile();
file->SetFollowLinks(aFollowLinks);
file->SetUseDOSDevicePathSyntax(aUseDOSDevicePathSyntax);
if (!aPath.IsEmpty()) {
nsresult rv = file->InitWithPath(aPath);
if (NS_FAILED(rv)) {
return rv;
}
}
file.forget(aResult);
return NS_OK;
}
} // anonymous namespace
static HWND GetMostRecentNavigatorHWND() {
nsresult rv;
nsCOMPtr<nsIWindowMediator> winMediator(
@ -164,7 +198,7 @@ nsresult nsLocalFile::RevealFile(const nsString& aResolvedPath) {
class nsDriveEnumerator : public nsSimpleEnumerator,
public nsIDirectoryEnumerator {
public:
nsDriveEnumerator();
explicit nsDriveEnumerator(bool aUseDOSDevicePathSyntax);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSISIMPLEENUMERATOR
NS_FORWARD_NSISIMPLEENUMERATORBASE(nsSimpleEnumerator::)
@ -200,6 +234,7 @@ class nsDriveEnumerator : public nsSimpleEnumerator,
nsString mDrives;
nsAString::const_iterator mStartOfCurrentDrive;
nsAString::const_iterator mEndOfDrivesString;
const bool mUseDOSDevicePathSyntax;
};
//-----------------------------------------------------------------------------
@ -445,12 +480,21 @@ static void FileTimeToPRTime(const FILETIME* aFiletime, PRTime* aPrtm) {
// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} with some
// changes : PR_GetFileInfo64, _PR_MD_GETFILEINFO64
static nsresult GetFileInfo(const nsString& aName, PRFileInfo64* aInfo) {
WIN32_FILE_ATTRIBUTE_DATA fileData;
if (aName.IsEmpty() || aName.FindCharInSet(u"?*") != kNotFound) {
if (aName.IsEmpty()) {
return NS_ERROR_INVALID_ARG;
}
// Checking u"?*" for the file path excluding the kDevicePathSpecifier.
// ToDo: Check if checking "?" for the file path is still needed.
const int32_t offset = StringBeginsWith(aName, kDevicePathSpecifier)
? kDevicePathSpecifier.Length()
: 0;
if (aName.FindCharInSet(u"?*", offset) != kNotFound) {
return NS_ERROR_INVALID_ARG;
}
WIN32_FILE_ATTRIBUTE_DATA fileData;
if (!::GetFileAttributesExW(aName.get(), GetFileExInfoStandard, &fileData)) {
return ConvertWinError(GetLastError());
}
@ -488,9 +532,6 @@ static nsresult OpenDir(const nsString& aName, nsDir** aDir) {
}
*aDir = nullptr;
if (aName.Length() + 3 >= MAX_PATH) {
return NS_ERROR_FILE_NAME_TOO_LONG;
}
nsDir* d = new nsDir();
nsAutoString filename(aName);
@ -704,9 +745,13 @@ NS_IMPL_ISUPPORTS_INHERITED(nsDirEnumerator, nsSimpleEnumerator,
//-----------------------------------------------------------------------------
nsLocalFile::nsLocalFile()
: mDirty(true), mResolveDirty(true), mFollowSymlinks(false) {}
: mDirty(true),
mResolveDirty(true),
mFollowSymlinks(false),
mUseDOSDevicePathSyntax(false) {}
nsLocalFile::nsLocalFile(const nsAString& aFilePath) : mFollowSymlinks(false) {
nsLocalFile::nsLocalFile(const nsAString& aFilePath)
: mFollowSymlinks(false), mUseDOSDevicePathSyntax(false) {
InitWithPath(aFilePath);
}
@ -743,6 +788,7 @@ nsLocalFile::nsLocalFile(const nsLocalFile& aOther)
: mDirty(true),
mResolveDirty(true),
mFollowSymlinks(aOther.mFollowSymlinks),
mUseDOSDevicePathSyntax(aOther.mUseDOSDevicePathSyntax),
mWorkingPath(aOther.mWorkingPath) {}
// Resolve the shortcut file from mWorkingPath and write the path
@ -937,6 +983,15 @@ nsLocalFile::InitWithPath(const nsAString& aFilePath) {
mWorkingPath.Truncate(mWorkingPath.Length() - 1);
}
// Bug 1626514: make sure that we don't end up with multiple prefixes.
// Prepend the "\\?\" prefix if the useDOSDevicePathSyntax is set and the path
// starts with a disk designator and backslash.
if (mUseDOSDevicePathSyntax &&
StartsWithDiskDesignatorAndBackslash(mWorkingPath)) {
mWorkingPath = kDevicePathSpecifier + mWorkingPath;
}
return NS_OK;
}
@ -1468,6 +1523,8 @@ nsLocalFile::SetLeafName(const nsAString& aLeafName) {
NS_IMETHODIMP
nsLocalFile::GetPath(nsAString& aResult) {
MOZ_ASSERT_IF(mUseDOSDevicePathSyntax,
!StartsWithDiskDesignatorAndBackslash(mWorkingPath));
aResult = mWorkingPath;
return NS_OK;
}
@ -1618,6 +1675,24 @@ nsresult nsLocalFile::CopySingleFile(nsIFile* aSourceFile, nsIFile* aDestParent,
return rv;
}
#ifdef DEBUG
nsCOMPtr<nsILocalFileWin> srcWinFile = do_QueryInterface(aSourceFile);
MOZ_ASSERT(srcWinFile);
bool srcUseDOSDevicePathSyntax;
srcWinFile->GetUseDOSDevicePathSyntax(&srcUseDOSDevicePathSyntax);
nsCOMPtr<nsILocalFileWin> destWinFile = do_QueryInterface(aDestParent);
MOZ_ASSERT(destWinFile);
bool destUseDOSDevicePathSyntax;
destWinFile->GetUseDOSDevicePathSyntax(&destUseDOSDevicePathSyntax);
MOZ_ASSERT(srcUseDOSDevicePathSyntax == destUseDOSDevicePathSyntax,
"Copy or Move files with different values for "
"useDOSDevicePathSyntax would fail");
#endif
if (FilePreferences::IsBlockedUNCPath(destPath)) {
return NS_ERROR_FILE_ACCESS_DENIED;
}
@ -2547,8 +2622,8 @@ nsLocalFile::GetParent(nsIFile** aParent) {
nsCOMPtr<nsIFile> localFile;
nsresult rv =
NS_NewLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
NewLocalFile(parentPath, mFollowSymlinks, mUseDOSDevicePathSyntax,
getter_AddRefs(localFile));
if (NS_FAILED(rv)) {
return rv;
}
@ -2810,8 +2885,32 @@ nsLocalFile::Equals(nsIFile* aInFile, bool* aResult) {
nsAutoString inFilePath;
lf->GetCanonicalPath(inFilePath);
bool inUseDOSDevicePathSyntax;
lf->GetUseDOSDevicePathSyntax(&inUseDOSDevicePathSyntax);
// Remove the prefix for both inFilePath and mShortWorkingPath if the
// useDOSDevicePathSyntax from them are not the same.
// This is added because of Omnijar. It compare files from different moduals
// with itself
nsAutoString shortWorkingPath;
if (inUseDOSDevicePathSyntax == mUseDOSDevicePathSyntax) {
shortWorkingPath = mShortWorkingPath;
} else if (inUseDOSDevicePathSyntax &&
StringBeginsWith(inFilePath, kDevicePathSpecifier)) {
MOZ_ASSERT(!StringBeginsWith(mShortWorkingPath, kDevicePathSpecifier));
shortWorkingPath = mShortWorkingPath;
inFilePath = Substring(inFilePath, kDevicePathSpecifier.Length());
} else if (mUseDOSDevicePathSyntax &&
StringBeginsWith(mShortWorkingPath, kDevicePathSpecifier)) {
MOZ_ASSERT(!StringBeginsWith(inFilePath, kDevicePathSpecifier));
shortWorkingPath =
Substring(mShortWorkingPath, kDevicePathSpecifier.Length());
}
// Ok : Win9x
*aResult = _wcsicmp(mShortWorkingPath.get(), inFilePath.get()) == 0;
*aResult = _wcsicmp(shortWorkingPath.get(), inFilePath.get()) == 0;
return NS_OK;
}
@ -2851,6 +2950,9 @@ nsLocalFile::GetTarget(nsAString& aResult) {
aResult.Truncate();
Resolve();
MOZ_ASSERT_IF(mUseDOSDevicePathSyntax,
!StartsWithDiskDesignatorAndBackslash(mResolvedPath));
aResult = mResolvedPath;
return NS_OK;
}
@ -2873,7 +2975,8 @@ nsLocalFile::GetDirectoryEntriesImpl(nsIDirectoryEnumerator** aEntries) {
*aEntries = nullptr;
if (mWorkingPath.EqualsLiteral("\\\\.")) {
RefPtr<nsDriveEnumerator> drives = new nsDriveEnumerator;
RefPtr<nsDriveEnumerator> drives =
new nsDriveEnumerator(mUseDOSDevicePathSyntax);
rv = drives->Init();
if (NS_FAILED(rv)) {
return rv;
@ -2959,6 +3062,24 @@ nsLocalFile::GetUseDOSDevicePathSyntax(bool* aUseDOSDevicePathSyntax) {
NS_IMETHODIMP
nsLocalFile::SetUseDOSDevicePathSyntax(bool aUseDOSDevicePathSyntax) {
if (mUseDOSDevicePathSyntax == aUseDOSDevicePathSyntax) {
return NS_OK;
}
if (mUseDOSDevicePathSyntax) {
if (StringBeginsWith(mWorkingPath, kDevicePathSpecifier)) {
MakeDirty();
// Remove the prefix
mWorkingPath = Substring(mWorkingPath, kDevicePathSpecifier.Length());
}
} else {
if (StartsWithDiskDesignatorAndBackslash(mWorkingPath)) {
MakeDirty();
// Prepend the prefix
mWorkingPath = kDevicePathSpecifier + mWorkingPath;
}
}
mUseDOSDevicePathSyntax = aUseDOSDevicePathSyntax;
return NS_OK;
}
@ -3282,7 +3403,8 @@ void nsLocalFile::EnsureShortPath() {
NS_IMPL_ISUPPORTS_INHERITED(nsDriveEnumerator, nsSimpleEnumerator,
nsIDirectoryEnumerator)
nsDriveEnumerator::nsDriveEnumerator() {}
nsDriveEnumerator::nsDriveEnumerator(bool aUseDOSDevicePathSyntax)
: mUseDOSDevicePathSyntax(aUseDOSDevicePathSyntax) {}
nsDriveEnumerator::~nsDriveEnumerator() {}
@ -3326,7 +3448,7 @@ nsDriveEnumerator::GetNext(nsISupports** aNext) {
mStartOfCurrentDrive = ++driveEnd;
nsIFile* file;
nsresult rv = NS_NewLocalFile(drive, false, &file);
nsresult rv = NewLocalFile(drive, false, mUseDOSDevicePathSyntax, &file);
*aNext = file;
return rv;

View File

@ -65,7 +65,7 @@ class nsLocalFile final : public nsILocalFileWin {
bool mResolveDirty;
bool mFollowSymlinks; // should we follow symlinks when working on this file
bool mUseDOSDevicePathSyntax = false;
bool mUseDOSDevicePathSyntax;
// this string will always be in native format!
nsString mWorkingPath;