Skip to content

Commit

Permalink
Mac PrjFSLib: Use IOSharedDataQueue workaround library if available
Browse files Browse the repository at this point in the history
This allows PrjFSLib and the prjfs-log tool to use alternate versions of the IODataQueueDequeue() and IODataQueuePeek() functions from a dylib on OS versions where the built-in functions are buggy. On affected versions, a library "libSharedDataQueue.dylib" is loaded if available, and its implementations of the aforementioned functions are used for the message and kext logging queues instead of the ones supplied by the OS's IOKit.framework.
  • Loading branch information
pmj committed Oct 25, 2018
1 parent b1a1e42 commit a997ebd
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 5 deletions.
6 changes: 3 additions & 3 deletions ProjFS.Mac/PrjFSLib/PrjFSLib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ PrjFS_Result PrjFS_StartVirtualizationInstance(

while (1)
{
IODataQueueEntry* entry = IODataQueuePeek(dataQueue.queueMemory);
IODataQueueEntry* entry = DataQueue_Peek(dataQueue.queueMemory);
if (nullptr == entry)
{
// No more items in queue
Expand All @@ -226,13 +226,13 @@ PrjFS_Result PrjFS_StartVirtualizationInstance(
if (messageSize < sizeof(Message))
{
cerr << "Bad message size: got " << messageSize << " bytes, expected minimum of " << sizeof(Message) << ", skipping. Kernel/user version mismatch?\n";
IODataQueueDequeue(dataQueue.queueMemory, nullptr, nullptr);
DataQueue_Dequeue(dataQueue.queueMemory, nullptr, nullptr);
continue;
}

void* messageMemory = malloc(messageSize);
uint32_t dequeuedSize = messageSize;
IOReturn result = IODataQueueDequeue(dataQueue.queueMemory, messageMemory, &dequeuedSize);
IOReturn result = DataQueue_Dequeue(dataQueue.queueMemory, messageMemory, &dequeuedSize);
if (kIOReturnSuccess != result || dequeuedSize != messageSize)
{
cerr << "Unexpected result dequeueing message - result 0x" << hex << result << " dequeued " << dequeuedSize << "/" << messageSize << " bytes\n";
Expand Down
110 changes: 110 additions & 0 deletions ProjFS.Mac/PrjFSLib/PrjFSUser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
#include <IOKit/IOKitLib.h>
#include <iostream>
#include <mach/mach_port.h>
#include <sys/utsname.h>
#include <dlfcn.h>

struct DarwinVersion
{
unsigned long major, minor, revision;
};

typedef decltype(IODataQueueDequeue)* ioDataQueueDequeueFunctionPtr;
static ioDataQueueDequeueFunctionPtr ioDataQueueDequeueFunction = nullptr;
typedef decltype(IODataQueuePeek)* ioDataQueuePeekFunctionPtr;
static ioDataQueuePeekFunctionPtr ioDataQueuePeekFunction = nullptr;

static void InitDataQueueFunctions();


io_connect_t PrjFSService_ConnectToDriver(enum PrjFSServiceUserClientType clientType)
Expand Down Expand Up @@ -122,3 +136,99 @@ bool PrjFSService_DataQueueInit(
return false;
}

IOReturn DataQueue_Dequeue(IODataQueueMemory* dataQueue, void* data, uint32_t* dataSize)
{
if (nullptr == ioDataQueueDequeueFunction)
{
InitDataQueueFunctions();
}
return ioDataQueueDequeueFunction(dataQueue, data, dataSize);
}

IODataQueueEntry* DataQueue_Peek(IODataQueueMemory* dataQueue)
{
if (nullptr == ioDataQueuePeekFunction)
{
InitDataQueueFunctions();
}
return ioDataQueuePeekFunction(dataQueue);
}


static bool GetDarwinVersion(DarwinVersion& outVersion)
{
utsname unameInfo = {};
if (0 != uname(&unameInfo))
{
return false;
}

printf("Uname: Darwin release '%s', version '%s'\n", unameInfo.release, unameInfo.version);
char* fieldEnd = nullptr;
unsigned long majorVersion = strtoul(unameInfo.release, &fieldEnd, 10);
if (nullptr == fieldEnd || *fieldEnd != '.')
{
return false;
}

unsigned long minorVersion = strtoul(fieldEnd + 1, &fieldEnd, 10);
if (nullptr == fieldEnd || (*fieldEnd != '.' && *fieldEnd != '\0'))
{
return false;
}

outVersion.major = majorVersion;
outVersion.minor = minorVersion;
outVersion.revision = 0;

if (*fieldEnd != '\0')
{
unsigned long revision = strtoul(fieldEnd + 1, &fieldEnd, 10);
if (nullptr == fieldEnd || (*fieldEnd != '.' && *fieldEnd != '\0'))
{
return false;
}
outVersion.revision = revision;
}

return true;
}

static void InitDataQueueFunctions()
{
ioDataQueueDequeueFunction = &IODataQueueDequeue;
ioDataQueuePeekFunction = &IODataQueuePeek;

DarwinVersion osVersion = {};
if (!GetDarwinVersion(osVersion))
{
return;
}

if ((osVersion.major == 17 && osVersion.minor >= 7) // macOS 10.13.6+
|| (osVersion.major == 18 && osVersion.minor == 0)) // macOS 10.14(.0) exactly
{
void* dataQueueLibrary = dlopen("libSharedDataQueue.dylib", RTLD_LAZY);
if (nullptr == dataQueueLibrary)
{
fprintf(stderr, "Error opening data queue client library: %s\n", dlerror());
}
else
{
void* sym = dlsym(dataQueueLibrary, "IODataQueueDequeue");
if (nullptr != sym)
{
ioDataQueueDequeueFunction = reinterpret_cast<ioDataQueueDequeueFunctionPtr>(sym);
}

sym = dlsym(dataQueueLibrary, "IODataQueuePeek");
if (nullptr != sym)
{
ioDataQueuePeekFunction = reinterpret_cast<ioDataQueuePeekFunctionPtr>(sym);
}

// Allow the dataQueueLibrary handle to leak; if we called dlclose(),
// the library would be unloaded, breaking our function pointers.
}
}
}
3 changes: 3 additions & 0 deletions ProjFS.Mac/PrjFSLib/PrjFSUser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ bool PrjFSService_DataQueueInit(
uint32_t clientPortType,
uint32_t clientMemoryType,
dispatch_queue_t eventHandlingQueue);

IODataQueueEntry* DataQueue_Peek(IODataQueueMemory* dataQueue);
IOReturn DataQueue_Dequeue(IODataQueueMemory* dataQueue, void* data, uint32_t* dataSize);
4 changes: 2 additions & 2 deletions ProjFS.Mac/PrjFSLib/prjfs-log/prjfs-log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ int main(int argc, const char * argv[])

while(true)
{
IODataQueueEntry* entry = IODataQueuePeek(dataQueue.queueMemory);
IODataQueueEntry* entry = DataQueue_Peek(dataQueue.queueMemory);
if(entry == nullptr)
{
break;
Expand All @@ -65,7 +65,7 @@ int main(int argc, const char * argv[])
lineCount++;
}

IODataQueueDequeue(dataQueue.queueMemory, nullptr, nullptr);
DataQueue_Dequeue(dataQueue.queueMemory, nullptr, nullptr);
}
});
dispatch_resume(dataQueue.dispatchSource);
Expand Down

0 comments on commit a997ebd

Please sign in to comment.