OpenBSD: add FFS volume formatting support

Expose FFS as the native OpenBSD filesystem option for volume creation and accept FFS/UFS on the command line, mapping mounts to the OpenBSD ffs filesystem type.

Run newfs through the elevated core service on vnd raw devices, then temporarily mount the new filesystem to transfer root directory ownership back to the invoking user.

Keep the non-interactive creation default as FAT on OpenBSD so existing unattended scripts do not start requiring elevation.
This commit is contained in:
Mounir IDRASSI committed 2026-07-06 17:11:24 +09:00
1 parent ede12bf81c
commit 48f8d87418
13 files changed
+471 -25

No files matched your search

+187
View File
@@ -914,6 +914,168 @@ namespace VeraCrypt
}
#endif
#ifdef TC_OPENBSD
static bool ParseOpenBSDVndDevicePath (const string &path, bool rawDevice, string &deviceNumber)
{
const string prefix = rawDevice ? "/dev/rvnd" : "/dev/vnd";
if (path.find (prefix) != 0)
return false;
size_t numberStart = prefix.size();
size_t numberEnd = numberStart;
while (numberEnd < path.size() && path[numberEnd] >= '0' && path[numberEnd] <= '9')
++numberEnd;
if (numberEnd == numberStart)
return false;
if (numberEnd + 1 != path.size() || path[numberEnd] != 'c')
return false;
deviceNumber = path.substr (numberStart, numberEnd - numberStart);
return true;
}
static string GetOpenBSDVndBlockDevicePath (const DevicePath &rawDevice)
{
string deviceNumber;
if (!ParseOpenBSDVndDevicePath (string (rawDevice), true, deviceNumber))
throw ParameterIncorrect (SRC_POS);
return string ("/dev/vnd") + deviceNumber + "c";
}
static void ValidateOpenBSDVndDeviceNode (const string &path, bool rawDevice)
{
string deviceNumber;
if (!ParseOpenBSDVndDevicePath (path, rawDevice, deviceNumber))
throw ParameterIncorrect (SRC_POS);
struct stat sb;
if (lstat (path.c_str(), &sb) != 0)
throw ParameterIncorrect (SRC_POS);
if (rawDevice)
{
if (!S_ISCHR (sb.st_mode))
throw ParameterIncorrect (SRC_POS);
}
else if (!S_ISBLK (sb.st_mode))
throw ParameterIncorrect (SRC_POS);
}
static void ValidateOpenBSDFFSFormatterRequest (const ExecuteOpenBSDFFSFormatterRequest &request)
{
ValidateOpenBSDVndDeviceNode (string (request.Device), true);
ValidateOpenBSDVndDeviceNode (GetOpenBSDVndBlockDevicePath (request.Device), false);
if (request.OwnerUserId > static_cast <uint64> ((uid_t) -1)
|| request.OwnerGroupId > static_cast <uint64> ((gid_t) -1))
{
throw ParameterIncorrect (SRC_POS);
}
}
static list <string> BuildOpenBSDFFSFormatterArguments (const ExecuteOpenBSDFFSFormatterRequest &request)
{
ValidateOpenBSDFFSFormatterRequest (request);
list <string> arguments;
arguments.push_back (string (request.Device));
return arguments;
}
static DirectoryPath CreateOpenBSDFFSTemporaryMountPoint ()
{
string mountPointTemplate = "/tmp/veracrypt-ffs.XXXXXXXXXX";
vector <char> mountPoint (mountPointTemplate.begin(), mountPointTemplate.end());
mountPoint.push_back ('\0');
char *createdPath = mkdtemp (&mountPoint.front());
throw_sys_sub_if (!createdPath, mountPointTemplate);
return DirectoryPath (createdPath);
}
static void RemoveOpenBSDFFSTemporaryMountPoint (const DirectoryPath &mountPoint)
{
if (!mountPoint.IsEmpty())
rmdir (string (mountPoint).c_str());
}
static void MountOpenBSDFFSTemporaryFilesystem (const DevicePath &devicePath, const DirectoryPath &mountPoint)
{
list <string> args;
args.push_back ("-t");
args.push_back ("ffs");
args.push_back ("-o");
args.push_back ("nodev,nosuid,noexec");
args.push_back ("--");
args.push_back (devicePath);
args.push_back (mountPoint);
Process::Execute ("mount", args);
}
static void DismountOpenBSDFFSTemporaryFilesystem (const DirectoryPath &mountPoint)
{
list <string> args;
args.push_back ("--");
args.push_back (mountPoint);
for (int attempt = 0; true; ++attempt)
{
try
{
Process::Execute ("umount", args);
return;
}
catch (ExecutedProcessFailed&)
{
if (attempt >= 5)
throw;
Thread::Sleep (200);
}
}
}
static void SetOpenBSDFFSRootOwner (const ExecuteOpenBSDFFSFormatterRequest &request)
{
ValidateOpenBSDFFSFormatterRequest (request);
DirectoryPath mountPoint = CreateOpenBSDFFSTemporaryMountPoint();
bool mounted = false;
try
{
MountOpenBSDFFSTemporaryFilesystem (DevicePath (GetOpenBSDVndBlockDevicePath (request.Device)), mountPoint);
mounted = true;
string mountPointStr = mountPoint;
throw_sys_sub_if (chown (mountPointStr.c_str(), static_cast <uid_t> (request.OwnerUserId), static_cast <gid_t> (request.OwnerGroupId)) == -1, mountPointStr);
DismountOpenBSDFFSTemporaryFilesystem (mountPoint);
mounted = false;
throw_sys_sub_if (rmdir (mountPointStr.c_str()) == -1, mountPointStr);
}
catch (...)
{
if (mounted)
{
try
{
DismountOpenBSDFFSTemporaryFilesystem (mountPoint);
}
catch (...) { }
}
RemoveOpenBSDFFSTemporaryMountPoint (mountPoint);
throw;
}
}
#endif
unique_ptr <Serializable> CoreService::GetResponseObject ()
{
unique_ptr <Serializable> deserializedObject (Serializable::DeserializeNew (ServiceOutputStream));
@@ -1131,6 +1293,18 @@ namespace VeraCrypt
}
#endif
#ifdef TC_OPENBSD
// ExecuteOpenBSDFFSFormatterRequest
ExecuteOpenBSDFFSFormatterRequest *executeFFSFormatterRequest = dynamic_cast <ExecuteOpenBSDFFSFormatterRequest*> (request.get());
if (executeFFSFormatterRequest)
{
Process::Execute (CoreService::GetOpenBSDFFSFormatterPath(), BuildOpenBSDFFSFormatterArguments (*executeFFSFormatterRequest));
SetOpenBSDFFSRootOwner (*executeFFSFormatterRequest);
ExecuteOpenBSDFFSFormatterResponse().Serialize (outputStream);
continue;
}
#endif
// MountVolumeRequest
MountVolumeRequest *mountRequest = dynamic_cast <MountVolumeRequest*> (request.get());
if (mountRequest)
@@ -1237,6 +1411,19 @@ namespace VeraCrypt
}
#endif
#ifdef TC_OPENBSD
const char *CoreService::GetOpenBSDFFSFormatterPath ()
{
return "/sbin/newfs";
}
void CoreService::RequestExecuteOpenBSDFFSFormatter (const DevicePath &devicePath, uint64 userId, uint64 groupId)
{
ExecuteOpenBSDFFSFormatterRequest request (devicePath, userId, groupId);
SendRequest <ExecuteOpenBSDFFSFormatterResponse> (request);
}
#endif
shared_ptr <VolumeInfo> CoreService::RequestMountVolume (MountOptions &options)
{
MountVolumeRequest request (&options);
+4
View File
@@ -38,6 +38,10 @@ namespace VeraCrypt
#ifdef TC_MACOSX
static const char *GetMacOSXAPFSFormatterPath ();
static void RequestExecuteMacOSXAPFSFormatter (const DevicePath &devicePath, uint64 userId, uint64 groupId);
#endif
#ifdef TC_OPENBSD
static const char *GetOpenBSDFFSFormatterPath ();
static void RequestExecuteOpenBSDFFSFormatter (const DevicePath &devicePath, uint64 userId, uint64 groupId);
#endif
static shared_ptr <VolumeInfo> RequestMountVolume (MountOptions &options);
static void RequestSetFileOwner (const FilesystemPath &path, const UserId &owner);
+29
View File
@@ -245,6 +245,32 @@ namespace VeraCrypt
}
#endif
#ifdef TC_OPENBSD
// ExecuteOpenBSDFFSFormatterRequest
void ExecuteOpenBSDFFSFormatterRequest::Deserialize (shared_ptr <Stream> stream)
{
CoreServiceRequest::Deserialize (stream);
Serializer sr (stream);
Device = sr.DeserializeWString ("Device");
sr.Deserialize ("OwnerGroupId", OwnerGroupId);
sr.Deserialize ("OwnerUserId", OwnerUserId);
}
bool ExecuteOpenBSDFFSFormatterRequest::RequiresElevation () const
{
return !Core->HasAdminPrivileges();
}
void ExecuteOpenBSDFFSFormatterRequest::Serialize (shared_ptr <Stream> stream) const
{
CoreServiceRequest::Serialize (stream);
Serializer sr (stream);
sr.Serialize ("Device", wstring (Device));
sr.Serialize ("OwnerGroupId", OwnerGroupId);
sr.Serialize ("OwnerUserId", OwnerUserId);
}
#endif
// MountVolumeRequest
void MountVolumeRequest::Deserialize (shared_ptr <Stream> stream)
{
@@ -322,6 +348,9 @@ namespace VeraCrypt
TC_SERIALIZER_FACTORY_ADD_CLASS (ExitRequest);
#ifdef TC_MACOSX
TC_SERIALIZER_FACTORY_ADD_CLASS (ExecuteMacOSXAPFSFormatterRequest);
#endif
#ifdef TC_OPENBSD
TC_SERIALIZER_FACTORY_ADD_CLASS (ExecuteOpenBSDFFSFormatterRequest);
#endif
TC_SERIALIZER_FACTORY_ADD_CLASS (GetDeviceSectorSizeRequest);
TC_SERIALIZER_FACTORY_ADD_CLASS (GetDeviceSizeRequest);
+16
View File
@@ -142,6 +142,22 @@ namespace VeraCrypt
};
#endif
#ifdef TC_OPENBSD
struct ExecuteOpenBSDFFSFormatterRequest : CoreServiceRequest
{
ExecuteOpenBSDFFSFormatterRequest () { }
ExecuteOpenBSDFFSFormatterRequest (const DevicePath &devicePath, uint64 userId, uint64 groupId)
: Device (devicePath), OwnerGroupId (groupId), OwnerUserId (userId) { }
TC_SERIALIZABLE (ExecuteOpenBSDFFSFormatterRequest);
virtual bool RequiresElevation () const;
DevicePath Device;
uint64 OwnerGroupId;
uint64 OwnerUserId;
};
#endif
struct MountVolumeRequest : CoreServiceRequest
{
MountVolumeRequest () { }
+15
View File
@@ -110,6 +110,18 @@ namespace VeraCrypt
}
#endif
#ifdef TC_OPENBSD
// ExecuteOpenBSDFFSFormatterResponse
void ExecuteOpenBSDFFSFormatterResponse::Deserialize (shared_ptr <Stream> stream)
{
}
void ExecuteOpenBSDFFSFormatterResponse::Serialize (shared_ptr <Stream> stream) const
{
Serializable::Serialize (stream);
}
#endif
// MountVolumeResponse
void MountVolumeResponse::Deserialize (shared_ptr <Stream> stream)
{
@@ -143,6 +155,9 @@ namespace VeraCrypt
TC_SERIALIZER_FACTORY_ADD_CLASS (GetHostDevicesResponse);
#ifdef TC_MACOSX
TC_SERIALIZER_FACTORY_ADD_CLASS (ExecuteMacOSXAPFSFormatterResponse);
#endif
#ifdef TC_OPENBSD
TC_SERIALIZER_FACTORY_ADD_CLASS (ExecuteOpenBSDFFSFormatterResponse);
#endif
TC_SERIALIZER_FACTORY_ADD_CLASS (MountVolumeResponse);
TC_SERIALIZER_FACTORY_ADD_CLASS (SetFileOwnerResponse);
+8
View File
@@ -83,6 +83,14 @@ namespace VeraCrypt
};
#endif
#ifdef TC_OPENBSD
struct ExecuteOpenBSDFFSFormatterResponse : CoreServiceResponse
{
ExecuteOpenBSDFFSFormatterResponse () { }
TC_SERIALIZABLE (ExecuteOpenBSDFFSFormatterResponse);
};
#endif
struct MountVolumeResponse : CoreServiceResponse
{
MountVolumeResponse () { }
+6 -1
View File
@@ -55,7 +55,8 @@ namespace VeraCrypt
Btrfs,
MacOsExt,
APFS,
UFS
UFS,
FFS
};
static Enum GetPlatformNative ()
@@ -68,6 +69,8 @@ namespace VeraCrypt
return VolumeCreationOptions::FilesystemType::MacOsExt;
#elif defined (TC_FREEBSD) || defined (TC_SOLARIS)
return VolumeCreationOptions::FilesystemType::UFS;
#elif defined (TC_OPENBSD)
return VolumeCreationOptions::FilesystemType::FFS;
#else
return VolumeCreationOptions::FilesystemType::FAT;
#endif
@@ -90,6 +93,8 @@ namespace VeraCrypt
case VolumeCreationOptions::FilesystemType::APFS: return "newfs_apfs";
#elif defined (TC_FREEBSD) || defined (TC_SOLARIS)
case VolumeCreationOptions::FilesystemType::UFS: return "newfs" ;
#elif defined (TC_OPENBSD)
case VolumeCreationOptions::FilesystemType::FFS: return "newfs" ;
#endif
default: return NULL;
}