Files
VeraCrypt/src/Platform/Unix/File.cpp
T
Mounir IDRASSI 5d7a2a78b8 OpenBSD: fix device-hosted volume sizing
OpenBSD device length detection was returning the raw disk sector count from DIOCGPDINFO directly. That value is not bytes and it describes the physical/default disk label, which caused VeraCrypt to expose an incorrectly sized FUSE backing image through vnd for device-hosted volumes.

Use the current disklabel from DIOCGDINFO, derive the opened partition from the device minor number, and return the selected partition size in bytes. Keep the raw c partition on the whole-disk path by using DL_GETDSIZE there.

Also reject sector-misaligned device-hosted sizes during volume creation so new malformed OpenBSD device-hosted volumes are not created. Do not reject existing malformed headers at mount time, so users can still mount old OpenBSD-created volumes for recovery.

Refs #1589.

Refs #1593.
2026-05-26 11:04:54 +09:00

441 lines
11 KiB
C++

/*
Derived from source code of TrueCrypt 7.1a, which is
Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed
by the TrueCrypt License 3.0.
Modifications and additions to the original source code (contained in this file)
and all other portions of this file are Copyright (c) 2013-2025 AM Crypto
and are 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.
*/
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <utime.h>
#ifdef TC_LINUX
#include <sys/mount.h>
#endif
#ifdef TC_BSD
#include <sys/disk.h>
#endif
#ifdef TC_OPENBSD
#include <sys/ioctl.h>
#include <sys/dkio.h>
#include <sys/disklabel.h>
#endif
#ifdef TC_SOLARIS
#include <stropts.h>
#include <sys/dkio.h>
#endif
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef TC_FREEBSD
#include <sys/sysctl.h>
#endif
#include "Platform/File.h"
#include "Platform/TextReader.h"
namespace VeraCrypt
{
#if 0
# define TC_TRACE_FILE_OPERATIONS
static void TraceFileOperation (int fileHandle, FilePath filePath, bool write, uint64 length, int64 position = -1)
{
string path = filePath;
if (path.empty() || path.find ("veracrypt_aux_mnt") != string::npos)
return;
stringstream s;
s << path << ": " << (write ? "W " : "R ") << (position == -1 ? lseek (fileHandle, 0, SEEK_CUR) : position) << " (" << length << ")";
SystemLog::WriteError (s.str());
}
#endif
void File::Close ()
{
if_debug (ValidateState());
if (!SharedHandle)
{
close (FileHandle);
FileIsOpen = false;
if ((mFileOpenFlags & File::PreserveTimestamps) && Path.IsFile())
{
struct utimbuf u;
u.actime = AccTime;
u.modtime = ModTime;
try
{
throw_sys_sub_if (utime (string (Path).c_str(), &u) == -1, wstring (Path));
}
catch (...) // Suppress errors to allow using read-only files
{
#ifdef DEBUG
throw;
#endif
}
}
}
}
void File::Delete ()
{
Close();
Path.Delete();
}
void File::Flush () const
{
if_debug (ValidateState());
throw_sys_sub_if (fsync (FileHandle) != 0, wstring (Path));
}
uint32 File::GetDeviceSectorSize () const
{
if (Path.IsDevice())
{
#ifdef TC_LINUX
int blockSize;
throw_sys_sub_if (ioctl (FileHandle, BLKSSZGET, &blockSize) == -1, wstring (Path));
return blockSize;
#elif defined (TC_MACOSX)
uint32 blockSize;
throw_sys_sub_if (ioctl (FileHandle, DKIOCGETBLOCKSIZE, &blockSize) == -1, wstring (Path));
return blockSize;
#elif defined (TC_FREEBSD)
u_int sectorSize;
throw_sys_sub_if (ioctl (FileHandle, DIOCGSECTORSIZE, &sectorSize) == -1, wstring (Path));
return (uint32) sectorSize;
#elif defined (TC_OPENBSD)
struct disklabel dl;
throw_sys_sub_if (ioctl (FileHandle, DIOCGDINFO, &dl) == -1, wstring (Path));
if (dl.d_secsize == 0)
throw ParameterIncorrect (SRC_POS);
return (uint32) dl.d_secsize;
#elif defined (TC_SOLARIS)
struct dk_minfo mediaInfo;
throw_sys_sub_if (ioctl (FileHandle, DKIOCGMEDIAINFO, &mediaInfo) == -1, wstring (Path));
return mediaInfo.dki_lbsize;
#else
# error GetDeviceSectorSize()
#endif
}
else
throw ParameterIncorrect (SRC_POS);
}
uint64 File::GetPartitionDeviceStartOffset () const
{
#ifdef TC_LINUX
// HDIO_GETGEO ioctl is limited by the size of long
TextReader tr ("/sys/block/" + string (Path.ToHostDriveOfPartition().ToBaseName()) + "/" + string (Path.ToBaseName()) + "/start");
string line;
tr.ReadLine (line);
return StringConverter::ToUInt64 (line) * GetDeviceSectorSize();
#elif defined (TC_MACOSX)
#ifndef DKIOCGETBASE
# define DKIOCGETBASE _IOR('d', 73, uint64)
#endif
uint64 offset;
throw_sys_sub_if (ioctl (FileHandle, DKIOCGETBASE, &offset) == -1, wstring (Path));
return offset;
#elif defined (TC_FREEBSD)
// Get the kernel GEOM configuration
size_t sysctlDataLen, mibLen;
int mib[4];
mibLen = 4;
throw_sys_sub_if (sysctlnametomib ("kern.geom.conftxt", mib, &mibLen), wstring (Path));
throw_sys_sub_if (sysctl (mib, mibLen, NULL, &sysctlDataLen, NULL, 0), wstring (Path));
vector<char> conftxt(sysctlDataLen);
throw_sys_sub_if (sysctl (mib, mibLen, (void *)conftxt.data(), &sysctlDataLen, NULL, 0), wstring (Path));
// Find the slice/partition data
string conftxtStr (conftxt.begin(), conftxt.end());
size_t confLoc = conftxtStr.find (Path.ToBaseName());
throw_sys_sub_if (confLoc == string::npos, wstring (Path));
// Skip to the ninth column
for (int i = 0; i < 6;i++) {
confLoc = conftxtStr.find (" ", confLoc + 1);
throw_sys_sub_if (confLoc == string::npos, wstring (Path));
}
confLoc++;
size_t end = conftxtStr.find (" ", confLoc);
throw_sys_sub_if (end == string::npos, wstring (Path));
return StringConverter::ToUInt64 (conftxtStr.substr (confLoc, end - confLoc));
#elif defined (TC_SOLARIS)
struct extpart_info partInfo;
throw_sys_sub_if (ioctl (FileHandle, DKIOCEXTPARTINFO, &partInfo) == -1, wstring (Path));
return partInfo.p_start * GetDeviceSectorSize();
#else
throw NotImplemented (SRC_POS);
#endif
}
uint64 File::Length () const
{
if_debug (ValidateState());
// BSD does not support seeking to the end of a device
#ifdef TC_BSD
if (Path.IsBlockDevice() || Path.IsCharacterDevice())
{
# ifdef TC_MACOSX
uint32 blockSize;
uint64 blockCount;
throw_sys_sub_if (ioctl (FileHandle, DKIOCGETBLOCKSIZE, &blockSize) == -1, wstring (Path));
throw_sys_sub_if (ioctl (FileHandle, DKIOCGETBLOCKCOUNT, &blockCount) == -1, wstring (Path));
return blockCount * blockSize;
# elif TC_OPENBSD
struct disklabel dl;
struct stat statData;
throw_sys_sub_if (ioctl (FileHandle, DIOCGDINFO, &dl) == -1, wstring (Path));
throw_sys_sub_if (fstat (FileHandle, &statData) == -1, wstring (Path));
if (dl.d_secsize == 0)
throw ParameterIncorrect (SRC_POS);
uint64 sectors;
int partition = DISKPART (statData.st_rdev);
if (partition == RAW_PART)
{
sectors = DL_GETDSIZE (&dl);
}
else
{
if (partition < 0 || partition >= dl.d_npartitions)
throw ParameterIncorrect (SRC_POS);
sectors = DL_GETPSIZE (&dl.d_partitions[partition]);
}
if (sectors > ((uint64) -1) / dl.d_secsize)
throw ParameterIncorrect (SRC_POS);
return sectors * dl.d_secsize;
# else
uint64 mediaSize;
throw_sys_sub_if (ioctl (FileHandle, DIOCGMEDIASIZE, &mediaSize) == -1, wstring (Path));
return mediaSize;
# endif
}
#endif
#ifdef TC_LINUX
// On Linux, try to use BLKGETSIZE64 for devices
if (Path.IsDevice())
{
uint64 mediaSize;
if (ioctl (FileHandle, BLKGETSIZE64, &mediaSize) != -1)
{
return mediaSize;
}
}
#endif
off_t current = lseek (FileHandle, 0, SEEK_CUR);
throw_sys_sub_if (current == -1, wstring (Path));
SeekEnd (0);
uint64 length = lseek (FileHandle, 0, SEEK_CUR);
SeekAt (current);
return length;
}
void File::Open (const FilePath &path, FileOpenMode mode, FileShareMode shareMode, FileOpenFlags flags)
{
#ifdef TC_LINUX
int sysFlags = O_LARGEFILE;
#else
int sysFlags = 0;
#endif
switch (mode)
{
case CreateReadWrite:
sysFlags |= O_CREAT | O_TRUNC | O_RDWR;
break;
case CreateWrite:
sysFlags |= O_CREAT | O_TRUNC | O_WRONLY;
break;
case OpenRead:
sysFlags |= O_RDONLY;
break;
case OpenWrite:
sysFlags |= O_WRONLY;
break;
case OpenReadWrite:
sysFlags |= O_RDWR;
break;
default:
throw ParameterIncorrect (SRC_POS);
}
if ((flags & File::PreserveTimestamps) && path.IsFile())
{
struct stat statData;
throw_sys_sub_if (stat (string (path).c_str(), &statData) == -1, wstring (path));
AccTime = statData.st_atime;
ModTime = statData.st_mtime;
}
FileHandle = open (string (path).c_str(), sysFlags, S_IRUSR | S_IWUSR);
throw_sys_sub_if (FileHandle == -1, wstring (path));
#if 0 // File locking is disabled to avoid remote filesystem locking issues
try
{
struct flock fl;
memset (&fl, 0, sizeof (fl));
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
switch (shareMode)
{
case ShareNone:
fl.l_type = F_WRLCK;
if (fcntl (FileHandle, F_SETLK, &fl) == -1)
throw_sys_sub_if (errno == EAGAIN || errno == EACCES, wstring (path));
break;
case ShareRead:
fl.l_type = F_RDLCK;
if (fcntl (FileHandle, F_SETLK, &fl) == -1)
throw_sys_sub_if (errno == EAGAIN || errno == EACCES, wstring (path));
break;
case ShareReadWrite:
fl.l_type = (mode == OpenRead ? F_RDLCK : F_WRLCK);
if (fcntl (FileHandle, F_GETLK, &fl) != -1 && fl.l_type != F_UNLCK)
{
errno = EAGAIN;
throw SystemException (SRC_POS, wstring (path));
}
break;
case ShareReadWriteIgnoreLock:
break;
default:
throw ParameterIncorrect (SRC_POS);
}
}
catch (...)
{
close (FileHandle);
throw;
}
#endif // 0
Path = path;
mFileOpenFlags = flags;
FileIsOpen = true;
}
uint64 File::Read (const BufferPtr &buffer) const
{
if_debug (ValidateState());
#ifdef TC_TRACE_FILE_OPERATIONS
TraceFileOperation (FileHandle, Path, false, buffer.Size());
#endif
ssize_t bytesRead = read (FileHandle, buffer, buffer.Size());
throw_sys_sub_if (bytesRead == -1, wstring (Path));
return bytesRead;
}
uint64 File::ReadAt (const BufferPtr &buffer, uint64 position) const
{
if_debug (ValidateState());
#ifdef TC_TRACE_FILE_OPERATIONS
TraceFileOperation (FileHandle, Path, false, buffer.Size(), position);
#endif
ssize_t bytesRead = pread (FileHandle, buffer, buffer.Size(), position);
throw_sys_sub_if (bytesRead == -1, wstring (Path));
return bytesRead;
}
void File::SeekAt (uint64 position) const
{
if_debug (ValidateState());
throw_sys_sub_if (lseek (FileHandle, position, SEEK_SET) == -1, wstring (Path));
}
void File::SeekEnd (int offset) const
{
if_debug (ValidateState());
// BSD does not support seeking to the end of a device
#ifdef TC_BSD
if (Path.IsBlockDevice() || Path.IsCharacterDevice())
{
SeekAt (Length() + offset);
return;
}
#endif
throw_sys_sub_if (lseek (FileHandle, offset, SEEK_END) == -1, wstring (Path));
}
void File::SetLength (uint64 length) const
{
if_debug (ValidateState());
throw_sys_sub_if (ftruncate (FileHandle, length) == -1, wstring (Path));
}
void File::Write (const ConstBufferPtr &buffer) const
{
if_debug (ValidateState());
#ifdef TC_TRACE_FILE_OPERATIONS
TraceFileOperation (FileHandle, Path, true, buffer.Size());
#endif
throw_sys_sub_if (write (FileHandle, buffer, buffer.Size()) != (ssize_t) buffer.Size(), wstring (Path));
}
void File::WriteAt (const ConstBufferPtr &buffer, uint64 position) const
{
if_debug (ValidateState());
#ifdef TC_TRACE_FILE_OPERATIONS
TraceFileOperation (FileHandle, Path, true, buffer.Size(), position);
#endif
throw_sys_sub_if (pwrite (FileHandle, buffer, buffer.Size(), position) != (ssize_t) buffer.Size(), wstring (Path));
}
}