/* -*- 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 #include #include "mozilla/FileUtilsWin.h" #include "mozilla/DebugOnly.h" #include "nsCRTGlue.h" #include "gtest/gtest.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_RELEASE_ASSERT(mDriveLetter != prevDriveLetter); if (result && prevDriveLetter) { Disconnect(prevDriveLetter); } return result; } void DriveMapping::Disconnect(wchar_t aDriveLetter) { wchar_t drvTemplate[] = {aDriveLetter, L':', L'\0'}; DWORD result = WNetCancelConnection2W(drvTemplate, 0, TRUE); MOZ_RELEASE_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 == reinterpret_cast(aExpectedDosPath); } TEST(NtPathToDosPath, Tests) { nsAutoString cDrive; ASSERT_TRUE(DriveToNtPath(L'C', cDrive)); // empty string EXPECT_TRUE(TestNtPathToDosPath(L"", L"")); // non-existent device, must fail EXPECT_FALSE(TestNtPathToDosPath(L"\\Device\\ThisDeviceDoesNotExist\\Foo", nullptr)); // base case nsAutoString testPath(cDrive); testPath.Append(L"\\Program Files"); EXPECT_TRUE(TestNtPathToDosPath(testPath.get(), L"C:\\Program Files")); // short filename nsAutoString ntShortName(cDrive); ntShortName.Append(L"\\progra~1"); EXPECT_TRUE(TestNtPathToDosPath(ntShortName.get(), L"C:\\Program Files")); // drive letters as symbolic links (NtCreateFile uses these) EXPECT_TRUE(TestNtPathToDosPath(L"\\??\\C:\\Foo", L"C:\\Foo")); // other symbolic links (should fail) EXPECT_FALSE(TestNtPathToDosPath(L"\\??\\MountPointManager", nullptr)); // socket (should fail) EXPECT_FALSE(TestNtPathToDosPath(L"\\Device\\Afd\\Endpoint", nullptr)); // UNC path (using MUP) EXPECT_TRUE(TestNtPathToDosPath(L"\\Device\\Mup\\127.0.0.1\\C$", L"\\\\127.0.0.1\\C$")); // UNC path (using LanmanRedirector) EXPECT_TRUE(TestNtPathToDosPath(L"\\Device\\LanmanRedirector\\127.0.0.1\\C$", L"\\\\127.0.0.1\\C$")); 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; ASSERT_TRUE(DriveToNtPath(drvMapping.GetDriveLetter(), networkPath)); networkPath += u"\\"; EXPECT_TRUE(TestNtPathToDosPath(networkPath.get(), expected)); // 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. ASSERT_TRUE(drvMapping.ChangeDriveLetter()); expected[0] = drvMapping.GetDriveLetter(); ASSERT_TRUE(DriveToNtPath(drvMapping.GetDriveLetter(), networkPath)); networkPath += u"\\"; EXPECT_TRUE(TestNtPathToDosPath(networkPath.get(), expected)); } }