Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mac PrjFSLib: Use IOSharedDataQueue workaround library if available #429

Merged
merged 1 commit into from
Oct 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
109 changes: 109 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,98 @@ 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))
{
Copy link
Contributor Author

@pmj pmj Oct 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unfortunately not aware of a nicer way to get the macOS version with just the C system libraries. (as opposed to Objective-C/Cocoa) There's also a "kern.osrelease" sysctl, but that returns the exact same version string as uname() that still needs to be parsed. Alternative suggestions welcome.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider sw_vers -productVersion? That should give you the macOS version number, not Darwin. You'd still have to do some parsing though.

Copy link
Contributor Author

@pmj pmj Oct 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would require shelling out or reading the file /System/Library/CoreServices/SystemVersion.plist and subsequently parsing the string anyway as you say. I think a simple syscall is preferable in that regard. The Darwin version tends to change on very minor revisions (security updates, supplemental updates, etc.) too, so in theory that would allow us to turn off the workaround if Apple changed their mind and supplied a fix with a supplementary update to 10.13.6. (For example, I think there was a supplemental update to 10.13.3, which would explain why 10.13.6 is Darwin 17.7, not 17.6.)

return false;
}

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