Skip to content
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
202 changes: 107 additions & 95 deletions src/Core/Unix/MacOSX/CoreMacOSX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <fstream>
#include <stdio.h>
#include <unistd.h>
#include <vector>
#include <CoreFoundation/CoreFoundation.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
Expand All @@ -30,73 +32,66 @@

namespace VeraCrypt
{
static string DecodePlistXmlString (const string &xmlString)
// RAII wrapper so CFRelease is never forgotten on early returns / exceptions.
class CFHolder
{
string decoded;
public:
explicit CFHolder (CFTypeRef ref = nullptr) : Ref (ref) { }
~CFHolder () { if (Ref) CFRelease (Ref); }
CFTypeRef Get () const { return Ref; }
private:
CFHolder (const CFHolder &);
CFHolder &operator= (const CFHolder &);
CFTypeRef Ref;
};

static string CFStringToStdString (CFStringRef cfStr)
{
if (!cfStr)
return string();

for (size_t i = 0; i < xmlString.size(); ++i)
{
if (xmlString[i] != '&')
{
decoded += xmlString[i];
continue;
}
CFIndex length = CFStringGetLength (cfStr);
CFIndex maxSize = CFStringGetMaximumSizeForEncoding (length, kCFStringEncodingUTF8) + 1;
if (maxSize <= 0)
return string();

if (xmlString.compare (i, 5, "&amp;") == 0)
{
decoded += '&';
i += 4;
}
else if (xmlString.compare (i, 4, "&lt;") == 0)
{
decoded += '<';
i += 3;
}
else if (xmlString.compare (i, 4, "&gt;") == 0)
{
decoded += '>';
i += 3;
}
else if (xmlString.compare (i, 6, "&quot;") == 0)
{
decoded += '"';
i += 5;
}
else if (xmlString.compare (i, 6, "&apos;") == 0)
{
decoded += '\'';
i += 5;
}
else
decoded += xmlString[i];
}
vector <char> buffer ((size_t) maxSize);
if (!CFStringGetCString (cfStr, &buffer[0], maxSize, kCFStringEncodingUTF8))
return string();

return decoded;
return string (&buffer[0]);
}

static bool ExtractPlistString (const string &xml, const string &key, size_t start, size_t limit, string &value, size_t *endPos = nullptr)
// Fetch a string value from a CFDictionary by (C-string) key; empty if absent
// or not a string.
static string CFDictionaryGetStdString (CFDictionaryRef dict, const char *key)
{
// hdiutil currently emits simple <key>name</key><string>value</string> pairs.
// This lightweight parser assumes the value follows the requested key.
string keyTag = "<key>" + key + "</key>";
size_t p = xml.find (keyTag, start);
if (p == string::npos || p >= limit)
return false;
if (!dict)
return string();

p = xml.find ("<string>", p + keyTag.size());
if (p == string::npos || p >= limit)
return false;
p += 8;
CFHolder cfKey (CFStringCreateWithCString (kCFAllocatorDefault, key, kCFStringEncodingUTF8));
if (!cfKey.Get())
return string();

size_t e = xml.find ("</string>", p);
if (e == string::npos || e > limit)
return false;
CFTypeRef value = CFDictionaryGetValue (dict, cfKey.Get()); // borrowed reference
if (!value || CFGetTypeID (value) != CFStringGetTypeID())
return string();

value = DecodePlistXmlString (xml.substr (p, e - p));
if (endPos)
*endPos = e + 9;
return CFStringToStdString ((CFStringRef) value);
}

// Parse an hdiutil -plist (XML) document into a CFPropertyList. Returns nullptr
// on failure; the caller owns the result and must CFRelease it.
static CFPropertyListRef ParsePropertyList (const string &xml)
{
if (xml.empty())
return nullptr;

return true;
CFHolder data (CFDataCreate (kCFAllocatorDefault, (const UInt8 *) xml.data(), (CFIndex) xml.size()));
if (!data.Get())
return nullptr;

return CFPropertyListCreateWithData (kCFAllocatorDefault, (CFDataRef) data.Get(), kCFPropertyListImmutable, nullptr, nullptr);
}

static string NormalizeDiskImagePath (const string &path)
Expand Down Expand Up @@ -125,45 +120,40 @@ namespace VeraCrypt
return normalized;
}

static bool ExtractDiskImageDeviceAndMountPoint (const string &xml, size_t start, size_t limit, DevicePath &device, DirectoryPath &mountPoint)
// Walk a "system-entities" array (from hdiutil attach/info). Prefer the entity
// that carries a mount-point; otherwise fall back to the first dev-entry.
static bool ExtractDeviceAndMountPointFromEntities (CFArrayRef entities, DevicePath &device, DirectoryPath &mountPoint)
{
if (!entities || CFGetTypeID (entities) != CFArrayGetTypeID())
return false;

string firstDevice;
string mountedDevice;
string mountedPath;

for (size_t p = start; ; )
CFIndex count = CFArrayGetCount (entities);
for (CFIndex i = 0; i < count; ++i)
{
size_t devKeyPos = xml.find ("<key>dev-entry</key>", p);
if (devKeyPos == string::npos || devKeyPos >= limit)
break;
CFTypeRef entry = CFArrayGetValueAtIndex (entities, i); // borrowed reference
if (!entry || CFGetTypeID (entry) != CFDictionaryGetTypeID())
continue;

string devEntry;
size_t devValueEnd = 0;
if (!ExtractPlistString (xml, "dev-entry", devKeyPos, limit, devEntry, &devValueEnd))
{
p = devKeyPos + 1;
CFDictionaryRef entryDict = (CFDictionaryRef) entry;

string devEntry = StringConverter::Trim (CFDictionaryGetStdString (entryDict, "dev-entry"));
if (devEntry.empty())
continue;
}

devEntry = StringConverter::Trim (devEntry);
if (firstDevice.empty())
firstDevice = devEntry;

size_t nextDevKeyPos = xml.find ("<key>dev-entry</key>", devValueEnd);
if (nextDevKeyPos == string::npos || nextDevKeyPos > limit)
nextDevKeyPos = limit;

string currentMountPoint;
// hdiutil currently emits dev-entry before mount-point inside each entity.
if (ExtractPlistString (xml, "mount-point", devValueEnd, nextDevKeyPos, currentMountPoint)
&& !currentMountPoint.empty())
string currentMountPoint = CFDictionaryGetStdString (entryDict, "mount-point");
if (!currentMountPoint.empty())
{
mountedDevice = devEntry;
mountedPath = currentMountPoint;
break;
}

p = devValueEnd;
}

if (!mountedDevice.empty())
Expand All @@ -182,6 +172,20 @@ namespace VeraCrypt
return false;
}

// Parse "hdiutil attach -plist" output (top-level dict with "system-entities").
static bool ExtractDiskImageDeviceAndMountPoint (const string &attachXml, DevicePath &device, DirectoryPath &mountPoint)
{
CFHolder plist (ParsePropertyList (attachXml));
if (!plist.Get() || CFGetTypeID (plist.Get()) != CFDictionaryGetTypeID())
return false;

CFTypeRef entities = CFDictionaryGetValue ((CFDictionaryRef) plist.Get(), CFSTR ("system-entities")); // borrowed
if (!entities || CFGetTypeID (entities) != CFArrayGetTypeID())
return false;

return ExtractDeviceAndMountPointFromEntities ((CFArrayRef) entities, device, mountPoint);
}

static bool FindDiskImageInfoByImagePath (const string &imagePath, DevicePath &device, DirectoryPath &mountPoint)
{
list <string> args;
Expand All @@ -191,27 +195,35 @@ namespace VeraCrypt
string xml = Process::Execute ("/usr/bin/hdiutil", args);
string normalizedImagePath = NormalizeDiskImagePath (imagePath);

for (size_t p = 0; ; )
CFHolder plist (ParsePropertyList (xml));
if (!plist.Get() || CFGetTypeID (plist.Get()) != CFDictionaryGetTypeID())
return false;

CFTypeRef images = CFDictionaryGetValue ((CFDictionaryRef) plist.Get(), CFSTR ("images")); // borrowed
if (!images || CFGetTypeID (images) != CFArrayGetTypeID())
return false;

CFArrayRef imageArray = (CFArrayRef) images;
CFIndex count = CFArrayGetCount (imageArray);
for (CFIndex i = 0; i < count; ++i)
{
size_t imageKeyPos = xml.find ("<key>image-path</key>", p);
if (imageKeyPos == string::npos)
break;
CFTypeRef image = CFArrayGetValueAtIndex (imageArray, i); // borrowed
if (!image || CFGetTypeID (image) != CFDictionaryGetTypeID())
continue;

string currentImagePath;
size_t imageValueEnd = 0;
if (!ExtractPlistString (xml, "image-path", imageKeyPos, string::npos, currentImagePath, &imageValueEnd))
{
p = imageKeyPos + 1;
CFDictionaryRef imageDict = (CFDictionaryRef) image;

string currentImagePath = CFDictionaryGetStdString (imageDict, "image-path");
if (NormalizeDiskImagePath (currentImagePath) != normalizedImagePath)
continue;
}

size_t nextImageKeyPos = xml.find ("<key>image-path</key>", imageValueEnd);
if (NormalizeDiskImagePath (currentImagePath) == normalizedImagePath)
{
return ExtractDiskImageDeviceAndMountPoint (xml, imageValueEnd, nextImageKeyPos, device, mountPoint);
}
// Matching image found: extract from its system-entities (mirrors the
// previous behavior of returning the result for the first match).
CFTypeRef entities = CFDictionaryGetValue (imageDict, CFSTR ("system-entities")); // borrowed
if (!entities || CFGetTypeID (entities) != CFArrayGetTypeID())
return false;

p = imageValueEnd;
return ExtractDeviceAndMountPointFromEntities ((CFArrayRef) entities, device, mountPoint);
}

return false;
Expand Down Expand Up @@ -473,7 +485,7 @@ namespace VeraCrypt

DevicePath virtualDev;
DirectoryPath mountPoint;
if (!ExtractDiskImageDeviceAndMountPoint (xml, 0, string::npos, virtualDev, mountPoint)
if (!ExtractDiskImageDeviceAndMountPoint (xml, virtualDev, mountPoint)
|| virtualDev.IsEmpty())
throw ParameterIncorrect (SRC_POS);
(void) mountPoint;
Expand Down
21 changes: 19 additions & 2 deletions src/Main/Main.make
Original file line number Diff line number Diff line change
Expand Up @@ -569,9 +569,26 @@ endif

install: prepare
ifneq "$(DESTDIR)" ""
mkdir -p $(DESTDIR)
mkdir -p "$(DESTDIR)"
endif
cp -R $(BASE_DIR)/Setup/Linux/usr $(DESTDIR)/
# Merge directory contents so /usr/sbin -> bin layouts are not replaced.
@set -e; \
mkdir -p "$(DESTDIR)/usr"; \
for _src in "$(BASE_DIR)/Setup/Linux/usr"/*; do \
[ -e "$$_src" ] || continue; \
_name=$$(basename "$$_src"); \
_dst="$(DESTDIR)/usr/$$_name"; \
if [ -d "$$_src" ]; then \
if [ -e "$$_dst" ] || [ -L "$$_dst" ]; then \
mkdir -p "$$_dst"; \
cp -R "$$_src/." "$$_dst/"; \
else \
cp -R "$$_src" "$(DESTDIR)/usr/"; \
fi; \
else \
cp -P "$$_src" "$(DESTDIR)/usr/"; \
fi; \
done

ifeq "$(TC_BUILD_CONFIG)" "Release"
package: prepare
Expand Down
Loading