mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 902587 - Part 2B: filename method for PoisonIOInterposer; r=BenWa,ehsan
This commit is contained in:
parent
d21c9ac055
commit
ff910d3f87
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
63
xpcom/io/FileUtilsWin.cpp
Normal 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
101
xpcom/io/FileUtilsWin.h
Normal 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
|
@ -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:
|
||||
|
@ -5,4 +5,4 @@
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
OS_LIBS += $(call EXPAND_LIBNAME,rpcrt4 uuid)
|
||||
OS_LIBS += $(call EXPAND_LIBNAME,rpcrt4 uuid mpr)
|
||||
|
220
xpcom/tests/windows/TestNtPathToDosPath.cpp
Normal file
220
xpcom/tests/windows/TestNtPathToDosPath.cpp
Normal 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;
|
||||
}
|
||||
|
@ -6,5 +6,6 @@
|
||||
|
||||
CPP_UNIT_TESTS += [
|
||||
'TestCOM.cpp',
|
||||
'TestNtPathToDosPath.cpp',
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user