Bug 902587 - Part 2B: filename method for PoisonIOInterposer; r=BenWa,ehsan

This commit is contained in:
Aaron Klotz 2014-01-31 20:14:03 -07:00
parent 5f91ce5f1d
commit 3e37f64342
11 changed files with 566 additions and 85 deletions

View File

@ -89,6 +89,40 @@ void VectorRemove(std::vector<T>& vector, const T& element)
} // anonymous namespace
IOInterposeObserver::Observation::Observation(Operation aOperation,
const char* aReference,
bool aShouldReport)
: mOperation(aOperation)
, mReference(aReference)
, mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
aShouldReport)
{
if (mShouldReport) {
mStart = TimeStamp::Now();
}
}
IOInterposeObserver::Observation::Observation(Operation aOperation,
const TimeStamp& aStart,
const TimeStamp& aEnd,
const char* aReference)
: mOperation(aOperation)
, mStart(aStart)
, mEnd(aEnd)
, mReference(aReference)
, mShouldReport(false)
{
}
void
IOInterposeObserver::Observation::Report()
{
if (mShouldReport) {
mEnd = TimeStamp::Now();
IOInterposer::Report(*this);
}
}
// Flags tracking which operations are being observed
IOInterposeObserver::Operation IOInterposer::sObservedOperations =
IOInterposeObserver::OpNone;

View File

@ -35,16 +35,25 @@ public:
class Observation
{
protected:
Observation()
{
}
/**
* This constructor is for use by subclasses that are intended to take
* timing measurements via RAII. The |aShouldReport| parameter may be
* used to make the measurement and reporting conditional on the
* satisfaction of an arbitrary predicate that was evaluated
* in the subclass. Note that IOInterposer::IsObservedOperation() is
* always ANDed with aShouldReport, so the subclass does not need to
* include a call to that function explicitly.
*/
Observation(Operation aOperation, const char* aReference,
bool aShouldReport = true);
public:
/**
* Since this constructor accepts start and end times, it does *not* take
* its own timings, nor does it report itself.
*/
Observation(Operation aOperation, const TimeStamp& aStart,
const TimeStamp& aEnd, const char* aReference)
: mOperation(aOperation), mStart(aStart), mEnd(aEnd),
mReference(aReference)
{
}
const TimeStamp& aEnd, const char* aReference);
/**
* Operation observed, this is either OpRead, OpWrite or OpFSync,
@ -92,7 +101,7 @@ public:
}
/** Request filename associated with the I/O operation, null if unknown */
virtual const char* Filename()
virtual const char16_t* Filename()
{
return nullptr;
}
@ -100,11 +109,16 @@ public:
virtual ~Observation()
{
}
protected:
void
Report();
Operation mOperation;
TimeStamp mStart;
TimeStamp mEnd;
const char* mReference;
const char* mReference; // Identifies the source of the Observation
bool mShouldReport; // Measure and report if true
};
/**

View File

@ -28,28 +28,14 @@ class NSPRIOAutoObservation : public IOInterposeObserver::Observation
{
public:
NSPRIOAutoObservation(IOInterposeObserver::Operation aOp)
: mShouldObserve(IOInterposer::IsObservedOperation(aOp))
: IOInterposeObserver::Observation(aOp, "NSPRIOInterposer")
{
if (mShouldObserve) {
mOperation = aOp;
mStart = TimeStamp::Now();
}
}
~NSPRIOAutoObservation()
{
if (mShouldObserve) {
mEnd = TimeStamp::Now();
const char* ref = "NSPRIOInterposing";
mReference = ref;
// Report this auto observation
IOInterposer::Report(*this);
}
Report();
}
private:
bool mShouldObserve;
};
PRStatus PR_CALLBACK interposedClose(PRFileDesc* aFd)

View File

@ -21,15 +21,16 @@
#include "plstr.h"
#include "prio.h"
#include <vector>
#include <algorithm>
#include <string.h>
#include <vector>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <aio.h>
#include <dlfcn.h>
#include <fcntl.h>
namespace {
@ -54,48 +55,66 @@ bool IsIPCWrite(int fd, const struct stat &buf);
class MacIOAutoObservation : public IOInterposeObserver::Observation
{
public:
MacIOAutoObservation(IOInterposeObserver::Operation aOp,
const char* aReference, int aFd)
: mShouldObserve(sIsEnabled && IOInterposer::IsObservedOperation(aOp) &&
!IsDebugFile(aFd))
MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd)
: IOInterposeObserver::Observation(aOp, sReference, sIsEnabled &&
!IsDebugFile(aFd))
, mFd(aFd)
, mHasQueriedFilename(false)
, mFilename(nullptr)
{
if (mShouldObserve) {
mOperation = aOp;
mReference = aReference;
mStart = TimeStamp::Now();
}
}
MacIOAutoObservation(IOInterposeObserver::Operation aOp,
const char* aReference, int aFd, const void *aBuf,
size_t aCount)
: mShouldObserve(sIsEnabled && IOInterposer::IsObservedOperation(aOp) &&
!IsDebugFile(aFd))
MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd,
const void *aBuf, size_t aCount)
: IOInterposeObserver::Observation(aOp, sReference, sIsEnabled &&
!IsDebugFile(aFd) &&
IsValidWrite(aFd, aBuf, aCount))
, mFd(aFd)
, mHasQueriedFilename(false)
, mFilename(nullptr)
{
if (mShouldObserve) {
mShouldObserve = IsValidWrite(aFd, aBuf, aCount);
if (mShouldObserve) {
mOperation = aOp;
mReference = aReference;
mStart = TimeStamp::Now();
}
}
}
// Custom implementation of IOInterposeObserver::Observation::Filename
const char16_t* Filename() MOZ_OVERRIDE;
~MacIOAutoObservation()
{
if (mShouldObserve) {
mEnd = TimeStamp::Now();
// Report this observation
IOInterposer::Report(*this);
Report();
if (mFilename) {
NS_Free(mFilename);
mFilename = nullptr;
}
}
private:
bool mShouldObserve;
int mFd;
bool mHasQueriedFilename;
char16_t* mFilename;
static const char* sReference;
};
const char* MacIOAutoObservation::sReference = "PoisonIOInterposer";
// Get filename for this observation
const char16_t* MacIOAutoObservation::Filename()
{
// If mHasQueriedFilename is true, then we already have it
if (mHasQueriedFilename) {
return mFilename;
}
char filename[MAXPATHLEN];
if (fcntl(mFd, F_GETPATH, filename) != -1) {
mFilename = UTF8ToNewUnicode(nsDependentCString(filename));
} else {
mFilename = nullptr;
}
mHasQueriedFilename = true;
// Return filename
return mFilename;
}
/****************************** Write Validation ******************************/
// We want to detect "actual" writes, not IPC. Some IPC mechanisms are
@ -194,9 +213,7 @@ typedef ssize_t (*aio_write_t)(struct aiocb *aiocbp);
ssize_t wrap_aio_write(struct aiocb *aiocbp);
FuncData aio_write_data = { 0, (void*) wrap_aio_write, (void*) aio_write };
ssize_t wrap_aio_write(struct aiocb *aiocbp) {
const char* ref = "aio_write";
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref,
aiocbp->aio_fildes);
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aiocbp->aio_fildes);
aio_write_t old_write = (aio_write_t) aio_write_data.Buffer;
return old_write(aiocbp);
@ -207,8 +224,7 @@ ssize_t wrap_aio_write(struct aiocb *aiocbp) {
typedef ssize_t (*pwrite_t)(int fd, const void *buf, size_t nbyte, off_t offset);
template<FuncData &foo>
ssize_t wrap_pwrite_temp(int fd, const void *buf, size_t nbyte, off_t offset) {
const char* ref = "pwrite_*";
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd);
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, fd);
pwrite_t old_write = (pwrite_t) foo.Buffer;
return old_write(fd, buf, nbyte, offset);
}
@ -229,9 +245,7 @@ DEFINE_PWRITE_DATA(pwrite_NOCANCEL, "pwrite$NOCANCEL");
typedef ssize_t (*writev_t)(int fd, const struct iovec *iov, int iovcnt);
template<FuncData &foo>
ssize_t wrap_writev_temp(int fd, const struct iovec *iov, int iovcnt) {
const char* ref = "pwrite_*";
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd, nullptr,
iovcnt);
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, fd, nullptr, iovcnt);
writev_t old_write = (writev_t) foo.Buffer;
return old_write(fd, iov, iovcnt);
}
@ -251,9 +265,7 @@ DEFINE_WRITEV_DATA(writev_NOCANCEL, "writev$NOCANCEL");
typedef ssize_t (*write_t)(int fd, const void *buf, size_t count);
template<FuncData &foo>
ssize_t wrap_write_temp(int fd, const void *buf, size_t count) {
const char* ref = "pwrite_*";
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd, buf,
count);
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, fd, buf, count);
write_t old_write = (write_t) foo.Buffer;
return old_write(fd, buf, count);
}

View File

@ -15,6 +15,7 @@
#include <winternl.h>
#include "mozilla/Assertions.h"
#include "mozilla/FileUtilsWin.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
@ -76,32 +77,77 @@ class WinIOAutoObservation : public IOInterposeObserver::Observation
{
public:
WinIOAutoObservation(IOInterposeObserver::Operation aOp,
const char* aReference, HANDLE aFileHandle)
: mFileHandle(aFileHandle),
mShouldObserve(IOInterposer::IsObservedOperation(aOp) &&
!IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle)))
HANDLE aFileHandle, const LARGE_INTEGER* aOffset)
: IOInterposeObserver::Observation(aOp, sReference,
!IsDebugFile(reinterpret_cast<intptr_t>(
aFileHandle)))
, mFileHandle(aFileHandle)
, mHasQueriedFilename(false)
, mFilename(nullptr)
{
if (mShouldObserve) {
mOperation = aOp;
mReference = aReference;
mStart = TimeStamp::Now();
if (mShouldReport) {
mOffset.QuadPart = aOffset ? aOffset->QuadPart : 0;
}
}
WinIOAutoObservation(IOInterposeObserver::Operation aOp, nsAString& aFilename)
: IOInterposeObserver::Observation(aOp, sReference)
, mFileHandle(nullptr)
, mHasQueriedFilename(false)
, mFilename(nullptr)
{
if (mShouldReport) {
nsAutoString dosPath;
if (NtPathToDosPath(aFilename, dosPath)) {
mFilename = ToNewUnicode(dosPath);
mHasQueriedFilename = true;
}
mOffset.QuadPart = 0;
}
}
// Custom implementation of IOInterposeObserver::Observation::Filename
const char16_t* Filename() MOZ_OVERRIDE;
~WinIOAutoObservation()
{
if (mShouldObserve) {
mEnd = TimeStamp::Now();
// Report this observation
IOInterposer::Report(*this);
Report();
if (mFilename) {
MOZ_ASSERT(mHasQueriedFilename);
NS_Free(mFilename);
mFilename = nullptr;
}
}
private:
HANDLE mFileHandle;
bool mShouldObserve;
LARGE_INTEGER mOffset;
bool mHasQueriedFilename;
char16_t* mFilename;
static const char* sReference;
};
const char* WinIOAutoObservation::sReference = "PoisonIOInterposer";
// Get filename for this observation
const char16_t* WinIOAutoObservation::Filename()
{
// If mHasQueriedFilename is true, then filename is already stored in mFilename
if (mHasQueriedFilename) {
return mFilename;
}
nsAutoString utf16Filename;
if (HandleToFilename(mFileHandle, mOffset, utf16Filename)) {
// Heap allocate with leakable memory
mFilename = ToNewUnicode(utf16Filename);
}
mHasQueriedFilename = true;
// Return filename
return mFilename;
}
/*************************** IO Interposing Methods ***************************/
// Function pointers to original functions
@ -121,8 +167,8 @@ static NTSTATUS WINAPI InterposedNtWriteFile(
PULONG aKey)
{
// Report IO
const char* ref = "NtWriteFile";
WinIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, aFileHandle);
WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle,
aOffset);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtWriteFile);
@ -154,8 +200,8 @@ static NTSTATUS WINAPI InterposedNtWriteFileGather(
PULONG aKey)
{
// Report IO
const char* ref = "NtWriteFileGather";
WinIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, aFileHandle);
WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle,
aOffset);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtWriteFileGather);

63
xpcom/io/FileUtilsWin.cpp Normal file
View File

@ -0,0 +1,63 @@
/* -*- 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 "FileUtilsWin.h"
#include <windows.h>
#include <psapi.h>
#include "nsWindowsHelpers.h"
namespace {
// Scoped type used by HandleToFilename
struct ScopedMappedViewTraits
{
typedef void* type;
static void* empty() { return nullptr; }
static void release(void* ptr) { UnmapViewOfFile(ptr); }
};
typedef mozilla::Scoped<ScopedMappedViewTraits> ScopedMappedView;
} // anonymous namespace
namespace mozilla {
bool
HandleToFilename(HANDLE aHandle, const LARGE_INTEGER& aOffset,
nsAString& aFilename)
{
aFilename.Truncate();
// This implementation is nice because it uses fully documented APIs that
// are available on all Windows versions that we support.
nsAutoHandle fileMapping(CreateFileMapping(aHandle, nullptr, PAGE_READONLY,
0, 1, nullptr));
if (!fileMapping) {
return false;
}
ScopedMappedView view(MapViewOfFile(fileMapping, FILE_MAP_READ,
aOffset.HighPart, aOffset.LowPart, 1));
if (!view) {
return false;
}
nsAutoString mappedFilename;
DWORD len = 0;
SetLastError(ERROR_SUCCESS);
do {
mappedFilename.SetLength(mappedFilename.Length() + MAX_PATH);
len = GetMappedFileNameW(GetCurrentProcess(), view,
mappedFilename.BeginWriting(),
mappedFilename.Length());
} while (!len && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
if (!len) {
return false;
}
mappedFilename.Truncate(len);
return NtPathToDosPath(mappedFilename, aFilename);
}
} // namespace mozilla

101
xpcom/io/FileUtilsWin.h Normal file
View File

@ -0,0 +1,101 @@
/* -*- 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/. */
#ifndef mozilla_FileUtilsWin_h
#define mozilla_FileUtilsWin_h
#include <windows.h>
#include "mozilla/Scoped.h"
#include "nsStringGlue.h"
namespace mozilla {
inline bool
NtPathToDosPath(const nsAString& aNtPath, nsAString& aDosPath)
{
aDosPath.Truncate();
if (aNtPath.IsEmpty()) {
return true;
}
NS_NAMED_LITERAL_STRING(symLinkPrefix, "\\??\\");
uint32_t ntPathLen = aNtPath.Length();
uint32_t symLinkPrefixLen = symLinkPrefix.Length();
if (ntPathLen >= 6 && aNtPath.CharAt(5) == L':' &&
ntPathLen >= symLinkPrefixLen &&
Substring(aNtPath, 0, symLinkPrefixLen).Equals(symLinkPrefix)) {
// Symbolic link for DOS device. Just strip off the prefix.
aDosPath = aNtPath;
aDosPath.Cut(0, 4);
return true;
}
nsAutoString logicalDrives;
DWORD len = 0;
while(true) {
len = GetLogicalDriveStringsW(len, logicalDrives.BeginWriting());
if (!len) {
return false;
} else if (len > logicalDrives.Length()) {
logicalDrives.SetLength(len);
} else {
break;
}
}
const char16_t* cur = logicalDrives.BeginReading();
const char16_t* end = logicalDrives.EndReading();
nsString targetPath;
targetPath.SetLength(MAX_PATH);
wchar_t driveTemplate[] = L" :";
do {
// Unfortunately QueryDosDevice doesn't support the idiom for querying the
// output buffer size, so it may require retries.
driveTemplate[0] = *cur;
DWORD targetPathLen = 0;
SetLastError(ERROR_SUCCESS);
while (true) {
targetPathLen = QueryDosDeviceW(driveTemplate, targetPath.BeginWriting(),
targetPath.Length());
if (targetPathLen || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
break;
}
targetPath.SetLength(targetPath.Length() * 2);
}
if (targetPathLen) {
// Need to use wcslen here because targetPath contains embedded NULL chars
size_t firstTargetPathLen = wcslen(targetPath.get());
const char16_t* pathComponent = aNtPath.BeginReading() +
firstTargetPathLen;
bool found = _wcsnicmp(aNtPath.BeginReading(), targetPath.get(),
firstTargetPathLen) == 0 &&
*pathComponent == L'\\';
if (found) {
aDosPath = driveTemplate;
aDosPath += pathComponent;
return true;
}
}
// Advance to the next NUL character in logicalDrives
while (*cur++);
} while (cur != end);
// Code for handling UNC paths would go here, if eventually required.
#if defined(DEBUG)
NS_NAMED_LITERAL_STRING(deviceMupPrefix, "\\Device\\Mup\\");
uint32_t deviceMupPrefixLen = deviceMupPrefix.Length();
if (ntPathLen >= deviceMupPrefixLen &&
Substring(aNtPath, 0, deviceMupPrefixLen).Equals(deviceMupPrefix)) {
NS_WARNING("UNC paths not yet supported in NtPathToDosPath");
}
#endif // defined(DEBUG)
return false;
}
bool
HandleToFilename(HANDLE aHandle, const LARGE_INTEGER& aOffset,
nsAString& aFilename);
} // namespace mozilla
#endif // mozilla_FileUtilsWin_h

View File

@ -54,7 +54,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'os2':
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
EXPORTS += ['nsLocalFileWin.h']
EXPORTS.mozilla += [
'FileUtilsWin.h',
]
SOURCES += [
'FileUtilsWin.cpp',
'nsLocalFileWin.cpp',
]
else:

View File

@ -5,4 +5,4 @@
include $(topsrcdir)/config/rules.mk
OS_LIBS += $(call EXPAND_LIBNAME,rpcrt4 uuid)
OS_LIBS += $(call EXPAND_LIBNAME,rpcrt4 uuid mpr)

View File

@ -0,0 +1,220 @@
/* -*- 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 <windows.h>
#include <winnetwk.h>
#include "mozilla/FileUtilsWin.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)
: mRemoteUNCPath(aRemoteUNCPath)
, mDriveLetter(0)
{
}
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 = 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'};
DWORD 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, 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(wcslen(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 += L"\\";
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 += L"\\";
if (!TestNtPathToDosPath(networkPath.get(), expected)) {
fail("Re-mapped UNC path");
result = 1;
}
}
return result;
}

View File

@ -6,5 +6,6 @@
CPP_UNIT_TESTS += [
'TestCOM.cpp',
'TestNtPathToDosPath.cpp',
]