diff -r 000000000000 -r 6474c204b198 xpcom/tests/windows/TestNtPathToDosPath.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xpcom/tests/windows/TestNtPathToDosPath.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,222 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TestHarness.h" + +#include +#include + +#include "mozilla/FileUtilsWin.h" +#include "mozilla/DebugOnly.h" +#include "nsCRTGlue.h" + +class DriveMapping +{ +public: + DriveMapping(const nsAString& aRemoteUNCPath); + ~DriveMapping(); + + bool + Init(); + bool + ChangeDriveLetter(); + wchar_t + GetDriveLetter() { return mDriveLetter; } + +private: + bool + DoMapping(); + void + Disconnect(wchar_t aDriveLetter); + + wchar_t mDriveLetter; + nsString mRemoteUNCPath; +}; + +DriveMapping::DriveMapping(const nsAString& aRemoteUNCPath) + : mDriveLetter(0) + , mRemoteUNCPath(aRemoteUNCPath) +{ +} + +bool +DriveMapping::Init() +{ + if (mDriveLetter) { + return false; + } + return DoMapping(); +} + +bool +DriveMapping::DoMapping() +{ + wchar_t drvTemplate[] = L" :"; + NETRESOURCEW netRes = {0}; + netRes.dwType = RESOURCETYPE_DISK; + netRes.lpLocalName = drvTemplate; + netRes.lpRemoteName = reinterpret_cast(mRemoteUNCPath.BeginWriting()); + wchar_t driveLetter = L'D'; + DWORD result = NO_ERROR; + do { + drvTemplate[0] = driveLetter; + result = WNetAddConnection2W(&netRes, nullptr, nullptr, CONNECT_TEMPORARY); + } while (result == ERROR_ALREADY_ASSIGNED && ++driveLetter <= L'Z'); + if (result != NO_ERROR) { + return false; + } + mDriveLetter = driveLetter; + return true; +} + +bool +DriveMapping::ChangeDriveLetter() +{ + wchar_t prevDriveLetter = mDriveLetter; + bool result = DoMapping(); + MOZ_ASSERT(mDriveLetter != prevDriveLetter); + if (result && prevDriveLetter) { + Disconnect(prevDriveLetter); + } + return result; +} + +void +DriveMapping::Disconnect(wchar_t aDriveLetter) +{ + wchar_t drvTemplate[] = {aDriveLetter, L':', L'\0'}; + mozilla::DebugOnly result = WNetCancelConnection2W(drvTemplate, 0, TRUE); + MOZ_ASSERT(result == NO_ERROR); +} + +DriveMapping::~DriveMapping() +{ + if (mDriveLetter) { + Disconnect(mDriveLetter); + } +} + +bool +DriveToNtPath(const wchar_t aDriveLetter, nsAString& aNtPath) +{ + const wchar_t drvTpl[] = {aDriveLetter, L':', L'\0'}; + aNtPath.SetLength(MAX_PATH); + DWORD pathLen; + while (true) { + pathLen = QueryDosDeviceW(drvTpl, reinterpret_cast(aNtPath.BeginWriting()), aNtPath.Length()); + if (pathLen || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + break; + } + aNtPath.SetLength(aNtPath.Length() * 2); + } + if (!pathLen) { + return false; + } + // aNtPath contains embedded NULLs, so we need to figure out the real length + // via wcslen. + aNtPath.SetLength(NS_strlen(aNtPath.BeginReading())); + return true; +} + +bool +TestNtPathToDosPath(const wchar_t* aNtPath, + const wchar_t* aExpectedDosPath) +{ + nsAutoString output; + bool result = mozilla::NtPathToDosPath(nsDependentString(aNtPath), output); + return result && output == aExpectedDosPath; +} + +int main(int argc, char* argv[]) +{ + ScopedXPCOM xpcom("NtPathToDosPath"); + if (xpcom.failed()) { + fail("XPCOM Startup"); + return 1; + } + nsAutoString cDrive; + if (!DriveToNtPath(L'C', cDrive)) { + fail("Querying for this machine's C:"); + return 1; + } + + int result = 0; + + // empty string + if (!TestNtPathToDosPath(L"", L"")) { + fail("Empty string"); + result = 1; + } + // non-existent device, must fail + if (TestNtPathToDosPath(L"\\Device\\ThisDeviceDoesNotExist\\Foo", nullptr)) { + fail("Non-existent device"); + result = 1; + } + // base case + nsAutoString testPath(cDrive); + testPath.Append(L"\\Foo"); + if (!TestNtPathToDosPath(testPath.get(), L"C:\\Foo")) { + fail("Base case"); + result = 1; + } + // drive letters as symbolic links (NtCreateFile uses these) + if (!TestNtPathToDosPath(L"\\??\\C:\\Foo", L"C:\\Foo")) { + fail("Path specified as symbolic link"); + result = 1; + } + // other symbolic links (should fail) + if (TestNtPathToDosPath(L"\\??\\MountPointManager", nullptr)) { + fail("Other symbolic link"); + result = 1; + } + // socket (should fail) + if (TestNtPathToDosPath(L"\\Device\\Afd\\Endpoint", nullptr)) { + fail("Socket"); + result = 1; + } + // currently UNC paths that are not mapped to drive letters are unsupported, + // so this should fail + if (TestNtPathToDosPath(L"\\Device\\Mup\\127.0.0.1\\C$", nullptr)) { + fail("Unmapped UNC path"); + result = 1; + } + DriveMapping drvMapping(NS_LITERAL_STRING("\\\\127.0.0.1\\C$")); + // Only run these tests if we were able to map; some machines don't have perms + if (drvMapping.Init()) { + wchar_t expected[] = L" :\\"; + expected[0] = drvMapping.GetDriveLetter(); + nsAutoString networkPath; + if (!DriveToNtPath(drvMapping.GetDriveLetter(), networkPath)) { + fail("Querying network drive"); + return 1; + } + networkPath += MOZ_UTF16("\\"); + if (!TestNtPathToDosPath(networkPath.get(), expected)) { + fail("Mapped UNC path"); + result = 1; + } + // NtPathToDosPath must correctly handle paths whose drive letter mapping has + // changed. We need to test this because the APIs called by NtPathToDosPath + // return different info if this has happened. + if (!drvMapping.ChangeDriveLetter()) { + fail("Change drive letter"); + return 1; + } + expected[0] = drvMapping.GetDriveLetter(); + if (!DriveToNtPath(drvMapping.GetDriveLetter(), networkPath)) { + fail("Querying second network drive"); + return 1; + } + networkPath += MOZ_UTF16("\\"); + if (!TestNtPathToDosPath(networkPath.get(), expected)) { + fail("Re-mapped UNC path"); + result = 1; + } + } + + return result; +} +