macOS: prepare APFS formatter device aliases

When creating an APFS filesystem inside a newly created device-hosted volume, VeraCrypt prepared only the raw hdiutil device path before invoking newfs_apfs. On macOS, newfs_apfs may resolve or reopen the corresponding block device path, which can fail with Permission denied for non-root GUI runs.

Prepare both raw and block aliases for the temporary formatter device, restore changed owners afterward, and share the helper between GUI and text-mode volume creation. Restore each changed alias independently so one restore failure does not skip the rest.
This commit is contained in:
Mounir IDRASSI
2026-05-13 12:41:09 +09:00
parent 46744db44f
commit 960f5993b2
3 changed files with 161 additions and 24 deletions

View File

@@ -22,6 +22,9 @@
#include "Core/VolumeCreator.h"
#include "Main/Application.h"
#include "Main/GraphicUserInterface.h"
#ifdef TC_MACOSX
#include "Main/MacOSXFormatterDevice.h"
#endif
#include "Main/Resources.h"
#include "VolumeCreationWizard.h"
#include "EncryptionOptionsWizardPage.h"
@@ -194,20 +197,6 @@ namespace VeraCrypt
return path;
}
static string GetMacOSXRawDevicePath (const string &deviceIdentifier)
{
if (deviceIdentifier.find ("/dev/rdisk") == 0)
return deviceIdentifier;
if (deviceIdentifier.find ("/dev/disk") == 0)
return string ("/dev/r") + deviceIdentifier.substr (5);
if (deviceIdentifier.find ("disk") == 0)
return string ("/dev/r") + deviceIdentifier;
return deviceIdentifier;
}
static string GetMacOSXDiskutilInfo (const VolumePath &devicePath)
{
list <string> args;
@@ -844,14 +833,20 @@ namespace VeraCrypt
Thread::Sleep (2000); // Try to prevent race conditions caused by OS
// Temporarily take ownership of the device if the user is not an administrator
UserId origDeviceOwner ((uid_t) -1);
DevicePath virtualDevice = volume->VirtualDevice;
#ifdef TC_MACOSX
string virtualDeviceStr = virtualDevice;
if (virtualDeviceStr.find ("/dev/rdisk") != 0)
virtualDevice = "/dev/r" + virtualDeviceStr.substr (5);
#endif
virtualDevice = GetMacOSXRawDevicePath (virtualDeviceStr);
MacOSXFormatterDeviceOwnerRestoreList changedDeviceOwners;
finally_do_arg (MacOSXFormatterDeviceOwnerRestoreList *, &changedDeviceOwners,
{
RestoreMacOSXFormatterDeviceOwners (*finally_arg);
});
PrepareMacOSXFormatterDevice (virtualDevice, changedDeviceOwners);
#else
UserId origDeviceOwner ((uid_t) -1);
try
{
File file;
@@ -871,6 +866,7 @@ namespace VeraCrypt
if (finally_arg2.SystemId != (uid_t) -1)
Core->SetFileOwner (finally_arg, finally_arg2);
});
#endif
// Create filesystem
list <string> args;

View File

@@ -0,0 +1,131 @@
/*
VeraCrypt source code
Copyright (c) 2026 AM Crypto
This file is part of VeraCrypt and is governed by the Apache License 2.0
the full text of which is contained in the file License.txt included in
VeraCrypt binary and source code distribution packages.
*/
#ifndef VC_HEADER_Main_MacOSXFormatterDevice
#define VC_HEADER_Main_MacOSXFormatterDevice
#include "Main/Main.h"
#ifdef TC_MACOSX
#include <unistd.h>
namespace VeraCrypt
{
inline bool IsMacOSXDiskDevicePath (const string &deviceIdentifier, const string &prefix)
{
return deviceIdentifier.find (prefix) == 0
&& deviceIdentifier.size() > prefix.size()
&& deviceIdentifier[prefix.size()] >= '0'
&& deviceIdentifier[prefix.size()] <= '9';
}
inline string GetMacOSXRawDevicePath (const string &deviceIdentifier)
{
if (IsMacOSXDiskDevicePath (deviceIdentifier, "/dev/rdisk"))
return deviceIdentifier;
if (IsMacOSXDiskDevicePath (deviceIdentifier, "/dev/disk"))
return string ("/dev/rdisk") + deviceIdentifier.substr (9);
if (IsMacOSXDiskDevicePath (deviceIdentifier, "rdisk"))
return string ("/dev/") + deviceIdentifier;
if (IsMacOSXDiskDevicePath (deviceIdentifier, "disk"))
return string ("/dev/r") + deviceIdentifier;
return deviceIdentifier;
}
inline string GetMacOSXBlockDevicePath (const string &deviceIdentifier)
{
if (IsMacOSXDiskDevicePath (deviceIdentifier, "/dev/disk"))
return deviceIdentifier;
if (IsMacOSXDiskDevicePath (deviceIdentifier, "/dev/rdisk"))
return string ("/dev/disk") + deviceIdentifier.substr (10);
if (IsMacOSXDiskDevicePath (deviceIdentifier, "disk"))
return string ("/dev/") + deviceIdentifier;
if (IsMacOSXDiskDevicePath (deviceIdentifier, "rdisk"))
return string ("/dev/disk") + deviceIdentifier.substr (5);
return deviceIdentifier;
}
struct MacOSXFormatterDeviceOwnerRestore
{
MacOSXFormatterDeviceOwnerRestore (const FilesystemPath &path, const UserId &owner)
: Path (path), Owner (owner) { }
FilesystemPath Path;
UserId Owner;
};
typedef list <MacOSXFormatterDeviceOwnerRestore> MacOSXFormatterDeviceOwnerRestoreList;
inline void AddUniqueMacOSXDevicePath (list <FilesystemPath> &paths, const string &path)
{
if (path.empty())
return;
foreach (const FilesystemPath &existingPath, paths)
{
if (string (existingPath) == path)
return;
}
paths.push_back (FilesystemPath (path));
}
inline void PrepareMacOSXFormatterDevice (const DevicePath &devicePath, MacOSXFormatterDeviceOwnerRestoreList &changedDeviceOwners)
{
if (Core->HasAdminPrivileges())
return;
const string devicePathStr = devicePath;
list <FilesystemPath> paths;
// APFS formatters may resolve /dev/rdiskN back to /dev/diskN internally.
AddUniqueMacOSXDevicePath (paths, devicePathStr);
AddUniqueMacOSXDevicePath (paths, GetMacOSXRawDevicePath (devicePathStr));
AddUniqueMacOSXDevicePath (paths, GetMacOSXBlockDevicePath (devicePathStr));
foreach (const FilesystemPath &path, paths)
{
try
{
File file;
file.Open (path, File::OpenReadWrite);
}
catch (...)
{
UserId origDeviceOwner = path.GetOwner();
// Register before chown so service-side success followed by
// an IPC failure can still be restored.
changedDeviceOwners.push_back (MacOSXFormatterDeviceOwnerRestore (path, origDeviceOwner));
Core->SetFileOwner (path, UserId (getuid()));
}
}
}
inline void RestoreMacOSXFormatterDeviceOwners (const MacOSXFormatterDeviceOwnerRestoreList &changedDeviceOwners)
{
foreach (const MacOSXFormatterDeviceOwnerRestore &restore, changedDeviceOwners)
{
try
{
Core->SetFileOwner (restore.Path, restore.Owner);
}
catch (...) { }
}
}
}
#endif // TC_MACOSX
#endif // VC_HEADER_Main_MacOSXFormatterDevice

View File

@@ -26,6 +26,9 @@
#include "Common/EMVToken.h"
#include "Core/RandomNumberGenerator.h"
#include "Application.h"
#ifdef TC_MACOSX
#include "Main/MacOSXFormatterDevice.h"
#endif
#include "TextUserInterface.h"
namespace VeraCrypt
@@ -1065,14 +1068,20 @@ namespace VeraCrypt
Thread::Sleep (2000); // Try to prevent race conditions caused by OS
// Temporarily take ownership of the device if the user is not an administrator
UserId origDeviceOwner ((uid_t) -1);
DevicePath virtualDevice = volume->VirtualDevice;
#ifdef TC_MACOSX
string virtualDeviceStr = virtualDevice;
if (virtualDeviceStr.find ("/dev/rdisk") != 0)
virtualDevice = "/dev/r" + virtualDeviceStr.substr (5);
#endif
virtualDevice = GetMacOSXRawDevicePath (virtualDeviceStr);
MacOSXFormatterDeviceOwnerRestoreList changedDeviceOwners;
finally_do_arg (MacOSXFormatterDeviceOwnerRestoreList *, &changedDeviceOwners,
{
RestoreMacOSXFormatterDeviceOwners (*finally_arg);
});
PrepareMacOSXFormatterDevice (virtualDevice, changedDeviceOwners);
#else
UserId origDeviceOwner ((uid_t) -1);
try
{
File file;
@@ -1092,6 +1101,7 @@ namespace VeraCrypt
if (finally_arg2.SystemId != (uid_t) -1)
Core->SetFileOwner (finally_arg, finally_arg2);
});
#endif
// Create filesystem
list <string> args;