New storage encrypted file.

This commit is contained in:
John Preston 2018-07-26 23:36:28 +03:00
parent 51092fb6a9
commit 8a371b9c1b
17 changed files with 940 additions and 17 deletions

View File

@ -80,7 +80,7 @@ inline void move(span destination, const_span source) {
memmove(destination.data(), source.data(), source.size());
}
inline void set_with_const(span destination, gsl::byte value) {
inline void set_with_const(span destination, type value) {
memset(
destination.data(),
gsl::to_integer<unsigned char>(value),

View File

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "core/basic_types.h"
namespace Core {
class Launcher;
} // namespace Core

View File

@ -43,6 +43,11 @@ f_WindowsDeleteString WindowsDeleteString;
f_PropVariantToString PropVariantToString;
f_PSStringFromPropertyKey PSStringFromPropertyKey;
f_DwmIsCompositionEnabled DwmIsCompositionEnabled;
f_RmStartSession RmStartSession;
f_RmRegisterResources RmRegisterResources;
f_RmGetList RmGetList;
f_RmShutdown RmShutdown;
f_RmEndSession RmEndSession;
HINSTANCE LibUxTheme;
HINSTANCE LibShell32;
@ -50,6 +55,7 @@ HINSTANCE LibWtsApi32;
HINSTANCE LibPropSys;
HINSTANCE LibComBase;
HINSTANCE LibDwmApi;
HINSTANCE LibRstrtMgr;
void start() {
init();
@ -89,6 +95,13 @@ void start() {
LibDwmApi = LoadLibrary(L"DWMAPI.DLL");
load(LibDwmApi, "DwmIsCompositionEnabled", DwmIsCompositionEnabled);
LibRstrtMgr = LoadLibrary(L"RSTRTMGR.DLL");
load(LibRstrtMgr, "RmStartSession", RmStartSession);
load(LibRstrtMgr, "RmRegisterResources", RmRegisterResources);
load(LibRstrtMgr, "RmGetList", RmGetList);
load(LibRstrtMgr, "RmShutdown", RmShutdown);
load(LibRstrtMgr, "RmEndSession", RmEndSession);
}
}

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <shlobj.h>
#include <roapi.h>
#include <dwmapi.h>
#include <RestartManager.h>
namespace Platform {
namespace Dlls {
@ -32,62 +33,138 @@ bool load(HINSTANCE library, LPCSTR name, Function &func) {
}
// UXTHEME.DLL
using f_SetWindowTheme = HRESULT(FAR STDAPICALLTYPE*)(HWND hWnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
using f_SetWindowTheme = HRESULT(FAR STDAPICALLTYPE*)(
HWND hWnd,
LPCWSTR pszSubAppName,
LPCWSTR pszSubIdList);
extern f_SetWindowTheme SetWindowTheme;
// SHELL32.DLL
using f_SHAssocEnumHandlers = HRESULT(FAR STDAPICALLTYPE*)(PCWSTR pszExtra, ASSOC_FILTER afFilter, IEnumAssocHandlers **ppEnumHandler);
using f_SHAssocEnumHandlers = HRESULT(FAR STDAPICALLTYPE*)(
PCWSTR pszExtra,
ASSOC_FILTER afFilter,
IEnumAssocHandlers **ppEnumHandler);
extern f_SHAssocEnumHandlers SHAssocEnumHandlers;
using f_SHCreateItemFromParsingName = HRESULT(FAR STDAPICALLTYPE*)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv);
using f_SHCreateItemFromParsingName = HRESULT(FAR STDAPICALLTYPE*)(
PCWSTR pszPath,
IBindCtx *pbc,
REFIID riid,
void **ppv);
extern f_SHCreateItemFromParsingName SHCreateItemFromParsingName;
using f_SHOpenWithDialog = HRESULT(FAR STDAPICALLTYPE*)(HWND hwndParent, const OPENASINFO *poainfo);
using f_SHOpenWithDialog = HRESULT(FAR STDAPICALLTYPE*)(
HWND hwndParent,
const OPENASINFO *poainfo);
extern f_SHOpenWithDialog SHOpenWithDialog;
using f_OpenAs_RunDLL = HRESULT(FAR STDAPICALLTYPE*)(HWND hWnd, HINSTANCE hInstance, LPCWSTR lpszCmdLine, int nCmdShow);
using f_OpenAs_RunDLL = HRESULT(FAR STDAPICALLTYPE*)(
HWND hWnd,
HINSTANCE hInstance,
LPCWSTR lpszCmdLine,
int nCmdShow);
extern f_OpenAs_RunDLL OpenAs_RunDLL;
using f_SHQueryUserNotificationState = HRESULT(FAR STDAPICALLTYPE*)(QUERY_USER_NOTIFICATION_STATE *pquns);
using f_SHQueryUserNotificationState = HRESULT(FAR STDAPICALLTYPE*)(
QUERY_USER_NOTIFICATION_STATE *pquns);
extern f_SHQueryUserNotificationState SHQueryUserNotificationState;
using f_SHChangeNotify = void(FAR STDAPICALLTYPE*)(LONG wEventId, UINT uFlags, __in_opt LPCVOID dwItem1, __in_opt LPCVOID dwItem2);
using f_SHChangeNotify = void(FAR STDAPICALLTYPE*)(
LONG wEventId,
UINT uFlags,
__in_opt LPCVOID dwItem1,
__in_opt LPCVOID dwItem2);
extern f_SHChangeNotify SHChangeNotify;
using f_SetCurrentProcessExplicitAppUserModelID = HRESULT(FAR STDAPICALLTYPE*)(__in PCWSTR AppID);
using f_SetCurrentProcessExplicitAppUserModelID
= HRESULT(FAR STDAPICALLTYPE*)(__in PCWSTR AppID);
extern f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID;
// WTSAPI32.DLL
using f_WTSRegisterSessionNotification = BOOL(FAR STDAPICALLTYPE*)(HWND hWnd, DWORD dwFlags);
using f_WTSRegisterSessionNotification = BOOL(FAR STDAPICALLTYPE*)(
HWND hWnd,
DWORD dwFlags);
extern f_WTSRegisterSessionNotification WTSRegisterSessionNotification;
using f_WTSUnRegisterSessionNotification = BOOL(FAR STDAPICALLTYPE*)(HWND hWnd);
using f_WTSUnRegisterSessionNotification = BOOL(FAR STDAPICALLTYPE*)(
HWND hWnd);
extern f_WTSUnRegisterSessionNotification WTSUnRegisterSessionNotification;
// PROPSYS.DLL
using f_PropVariantToString = HRESULT(FAR STDAPICALLTYPE*)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch);
using f_PropVariantToString = HRESULT(FAR STDAPICALLTYPE*)(
_In_ REFPROPVARIANT propvar,
_Out_writes_(cch) PWSTR psz,
_In_ UINT cch);
extern f_PropVariantToString PropVariantToString;
using f_PSStringFromPropertyKey = HRESULT(FAR STDAPICALLTYPE*)(_In_ REFPROPERTYKEY pkey, _Out_writes_(cch) LPWSTR psz, _In_ UINT cch);
using f_PSStringFromPropertyKey = HRESULT(FAR STDAPICALLTYPE*)(
_In_ REFPROPERTYKEY pkey,
_Out_writes_(cch) LPWSTR psz,
_In_ UINT cch);
extern f_PSStringFromPropertyKey PSStringFromPropertyKey;
// COMBASE.DLL
using f_RoGetActivationFactory = HRESULT(FAR STDAPICALLTYPE*)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory);
using f_RoGetActivationFactory = HRESULT(FAR STDAPICALLTYPE*)(
_In_ HSTRING activatableClassId,
_In_ REFIID iid,
_COM_Outptr_ void ** factory);
extern f_RoGetActivationFactory RoGetActivationFactory;
using f_WindowsCreateStringReference = HRESULT(FAR STDAPICALLTYPE*)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string);
using f_WindowsCreateStringReference = HRESULT(FAR STDAPICALLTYPE*)(
_In_reads_opt_(length + 1) PCWSTR sourceString,
UINT32 length,
_Out_ HSTRING_HEADER * hstringHeader,
_Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string);
extern f_WindowsCreateStringReference WindowsCreateStringReference;
using f_WindowsDeleteString = HRESULT(FAR STDAPICALLTYPE*)(_In_opt_ HSTRING string);
using f_WindowsDeleteString = HRESULT(FAR STDAPICALLTYPE*)(
_In_opt_ HSTRING string);
extern f_WindowsDeleteString WindowsDeleteString;
// DWMAPI.DLL
using f_DwmIsCompositionEnabled = HRESULT(FAR STDAPICALLTYPE*)(_Out_ BOOL* pfEnabled);
using f_DwmIsCompositionEnabled = HRESULT(FAR STDAPICALLTYPE*)(
_Out_ BOOL* pfEnabled);
extern f_DwmIsCompositionEnabled DwmIsCompositionEnabled;
// RSTRTMGR.DLL
using f_RmStartSession = DWORD(FAR STDAPICALLTYPE*)(
_Out_ DWORD *pSessionHandle,
_Reserved_ DWORD dwSessionFlags,
_Out_writes_(CCH_RM_SESSION_KEY + 1) WCHAR strSessionKey[]);
extern f_RmStartSession RmStartSession;
using f_RmRegisterResources = DWORD(FAR STDAPICALLTYPE*)(
_In_ DWORD dwSessionHandle,
_In_ UINT nFiles,
_In_reads_opt_(nFiles) LPCWSTR rgsFileNames[],
_In_ UINT nApplications,
_In_reads_opt_(nApplications) RM_UNIQUE_PROCESS rgApplications[],
_In_ UINT nServices,
_In_reads_opt_(nServices) LPCWSTR rgsServiceNames[]);
extern f_RmRegisterResources RmRegisterResources;
using f_RmGetList = DWORD(FAR STDAPICALLTYPE*)(
_In_ DWORD dwSessionHandle,
_Out_ UINT *pnProcInfoNeeded,
_Inout_ UINT *pnProcInfo,
_Inout_updates_opt_(*pnProcInfo) RM_PROCESS_INFO rgAffectedApps[],
_Out_ LPDWORD lpdwRebootReasons);
extern f_RmGetList RmGetList;
using f_RmShutdown = DWORD(FAR STDAPICALLTYPE*)(
_In_ DWORD dwSessionHandle,
_In_ ULONG lActionFlags,
_In_opt_ RM_WRITE_STATUS_CALLBACK fnStatus);
extern f_RmShutdown RmShutdown;
using f_RmEndSession = DWORD(FAR STDAPICALLTYPE*)(
_In_ DWORD dwSessionHandle);
extern f_RmEndSession RmEndSession;
} // namespace Dlls
} // namespace Platform

View File

@ -0,0 +1,9 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/cache/storage_cache_database.h"

View File

@ -0,0 +1,34 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "core/basic_types.h"
#include <crl/crl_object_on_queue.h>
#include <QtCore/QString>
namespace Storage {
class EncryptionKey;
namespace Cache {
namespace details {
class Database;
} // namespace details
class Database {
public:
Database(const QString &path, EncryptionKey key);
private:
using Implementation = details::Database;
crl::object_on_queue<Implementation> _wrapped;
};
} // namespace Cache
} // namespace Storage

View File

@ -0,0 +1,201 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_encrypted_file.h"
#include "base/openssl_help.h"
namespace Storage {
namespace {
constexpr auto kBlockSize = CtrState::kBlockSize;
} // namespace
struct File::BasicHeader {
bytes::array<kSaltSize> salt = { { bytes::type() } };
Format format = Format::Format_0;
uint32 reserved = 0;
uint64 applicationVersion = 0;
bytes::array<openssl::kSha256Size> checksum = { { bytes::type() } };
};
File::Result File::open(
const QString &path,
Mode mode,
const EncryptionKey &key) {
_data.setFileName(QFileInfo(path).absoluteFilePath());
const auto result = attemptOpen(mode, key);
if (result != Result::Success) {
_state = base::none;
_data.close();
_lock.unlock();
}
return result;
static_assert(sizeof(BasicHeader) == kSaltSize
+ sizeof(uint64) * 2
+ openssl::kSha256Size, "Unexpected paddings in the header.");
static_assert(
(sizeof(BasicHeader) - kSaltSize) % kBlockSize == 0,
"Not way to encrypt the header.");
}
File::Result File::attemptOpen(Mode mode, const EncryptionKey &key) {
switch (mode) {
case Mode::Read: return attemptOpenForRead(key);
case Mode::ReadAppend: return attemptOpenForReadAppend(key);
case Mode::Write: return attemptOpenForWrite(key);
}
Unexpected("Mode in Storage::File::attemptOpen.");
}
File::Result File::attemptOpenForRead(const EncryptionKey &key) {
if (!_data.open(QIODevice::ReadOnly)) {
return Result::Failed;
}
return readHeader(key);
}
File::Result File::attemptOpenForReadAppend(const EncryptionKey &key) {
if (!_data.open(QIODevice::ReadWrite)) {
return Result::Failed;
} else if (!_lock.lock(_data)) {
return Result::LockFailed;
}
const auto size = _data.size();
if (!size) {
return writeHeader(key) ? Result::Success : Result::Failed;
}
return readHeader(key);
}
File::Result File::attemptOpenForWrite(const EncryptionKey &key) {
if (!_data.open(QIODevice::WriteOnly)) {
return Result::Failed;
} else if (!_lock.lock(_data)) {
return Result::LockFailed;
}
return writeHeader(key) ? Result::Success : Result::Failed;
}
bool File::writeHeader(const EncryptionKey &key) {
Expects(!_state.has_value());
Expects(_data.pos() == 0);
const auto magic = bytes::make_span("TDEF");
if (!writePlain(magic.subspan(0, FileLock::kSkipBytes))) {
return false;
}
auto header = BasicHeader();
bytes::set_random(header.salt);
_state = key.prepareCtrState(header.salt);
const auto headerBytes = bytes::object_as_span(&header);
const auto checkSize = headerBytes.size() - header.checksum.size();
bytes::copy(
header.checksum,
openssl::Sha256(
key.data(),
headerBytes.subspan(0, checkSize)));
if (writePlain(header.salt) != header.salt.size()) {
return false;
} else if (!write(headerBytes.subspan(header.salt.size()))) {
return false;
}
return true;
}
File::Result File::readHeader(const EncryptionKey &key) {
Expects(!_state.has_value());
Expects(_data.pos() == 0);
if (!_data.seek(FileLock::kSkipBytes)) {
return Result::Failed;
}
auto header = BasicHeader();
const auto headerBytes = bytes::object_as_span(&header);
if (readPlain(headerBytes) != headerBytes.size()) {
return Result::Failed;
}
_state = key.prepareCtrState(header.salt);
decrypt(headerBytes.subspan(header.salt.size()));
const auto checkSize = headerBytes.size() - header.checksum.size();
const auto checksum = openssl::Sha256(
key.data(),
headerBytes.subspan(0, checkSize));
if (bytes::compare(header.checksum, checksum) != 0) {
return Result::WrongKey;
} else if (header.format != Format::Format_0) {
return Result::Failed;
}
return Result::Success;
}
size_type File::readPlain(bytes::span bytes) {
return _data.read(reinterpret_cast<char*>(bytes.data()), bytes.size());
}
size_type File::writePlain(bytes::const_span bytes) {
return _data.write(
reinterpret_cast<const char*>(bytes.data()),
bytes.size());
}
void File::decrypt(bytes::span bytes) {
Expects(_state.has_value());
_state->decrypt(bytes, _offset);
_offset += bytes.size();
}
void File::encrypt(bytes::span bytes) {
Expects(_state.has_value());
_state->encrypt(bytes, _offset);
_offset += bytes.size();
}
size_type File::read(bytes::span bytes) {
Expects(bytes.size() % kBlockSize == 0);
auto count = readPlain(bytes);
if (const auto back = -(count % kBlockSize)) {
if (!_data.seek(_data.pos() + back)) {
return 0;
}
count += back;
}
if (count) {
decrypt(bytes.subspan(0, count));
}
return count;
}
size_type File::write(bytes::span bytes) {
Expects(bytes.size() % kBlockSize == 0);
encrypt(bytes);
auto count = writePlain(bytes);
if (const auto back = (count % kBlockSize)) {
if (!_data.seek(_data.pos() - back)) {
return 0;
}
count -= back;
}
if (const auto back = (bytes.size() - count)) {
_offset -= back;
decrypt(bytes.subspan(count));
}
_data.flush();
return count;
}
} // namespace Storage

View File

@ -0,0 +1,61 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "storage/storage_file_lock.h"
#include "storage/storage_encryption.h"
#include "base/bytes.h"
namespace Storage {
class File {
public:
enum class Mode {
Read,
ReadAppend,
Write,
};
enum class Result {
Failed,
LockFailed,
WrongKey,
Success,
};
Result open(const QString &path, Mode mode, const EncryptionKey &key);
size_type read(bytes::span bytes);
size_type write(bytes::span bytes);
private:
enum class Format : uint32 {
Format_0,
};
struct BasicHeader;
Result attemptOpen(Mode mode, const EncryptionKey &key);
Result attemptOpenForRead(const EncryptionKey &key);
Result attemptOpenForReadAppend(const EncryptionKey &key);
Result attemptOpenForWrite(const EncryptionKey &key);
bool writeHeader(const EncryptionKey &key);
Result readHeader(const EncryptionKey &key);
size_type readPlain(bytes::span bytes);
size_type writePlain(bytes::const_span bytes);
void decrypt(bytes::span bytes);
void encrypt(bytes::span bytes);
QFile _data;
FileLock _lock;
index_type _offset = 0;
base::optional<CtrState> _state;
};
} // namespace Storage

View File

@ -0,0 +1,79 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_encryption.h"
#include "base/openssl_help.h"
namespace Storage {
CtrState::CtrState(bytes::const_span key, bytes::const_span iv) {
Expects(key.size() == _key.size());
Expects(iv.size() == _iv.size());
bytes::copy(_key, key);
bytes::copy(_iv, iv);
}
template <typename Method>
void CtrState::process(bytes::span data, index_type offset, Method method) {
Expects((data.size() % kBlockSize) == 0);
Expects((offset % kBlockSize) == 0);
AES_KEY aes;
AES_set_encrypt_key(
reinterpret_cast<const uchar*>(_key.data()),
_key.size() * CHAR_BIT,
&aes);
unsigned char ecountBuf[kBlockSize];
unsigned int blockNumber = offset / kBlockSize;
CRYPTO_ctr128_encrypt(
reinterpret_cast<const uchar*>(data.data()),
reinterpret_cast<uchar*>(data.data()),
data.size(),
&aes,
reinterpret_cast<unsigned char*>(_iv.data()),
ecountBuf,
&blockNumber,
(block128_f)method);
}
void CtrState::encrypt(bytes::span data, index_type offset) {
return process(data, offset, AES_encrypt);
}
void CtrState::decrypt(bytes::span data, index_type offset) {
return process(data, offset, AES_decrypt);
}
EncryptionKey::EncryptionKey(bytes::vector &&data)
: _data(std::move(data)) {
Expects(_data.size() == kSize);
}
const bytes::vector &EncryptionKey::data() const {
return _data;
}
CtrState EncryptionKey::prepareCtrState(bytes::const_span salt) const {
Expects(salt.size() == kSaltSize);
const auto data = bytes::make_span(_data);
const auto key = openssl::Sha256(
data.subspan(0, kSize / 2),
salt.subspan(0, kSaltSize / 2));
const auto iv = openssl::Sha256(
data.subspan(kSize / 2),
salt.subspan(kSaltSize / 2));
return CtrState(
key,
bytes::make_span(iv).subspan(0, CtrState::kIvSize));
}
} // namespace Storage

View File

@ -0,0 +1,53 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/bytes.h"
namespace Storage {
constexpr auto kSaltSize = size_type(64);
class CtrState {
public:
static constexpr auto kBlockSize = size_type(16);
static constexpr auto kKeySize = size_type(32);
static constexpr auto kIvSize = kBlockSize;
CtrState(bytes::const_span key, bytes::const_span iv);
void encrypt(bytes::span data, index_type offset);
void decrypt(bytes::span data, index_type offset);
private:
template <typename Method>
void process(bytes::span data, index_type offset, Method method);
static constexpr auto EcountSize = kBlockSize;
bytes::array<kKeySize> _key;
bytes::array<kIvSize> _iv;
};
class EncryptionKey {
public:
static constexpr auto kSize = size_type(256);
EncryptionKey() = default;
explicit EncryptionKey(bytes::vector &&data);
const bytes::vector &data() const;
CtrState prepareCtrState(bytes::const_span salt) const;
private:
bytes::vector _data;
};
} // namespace Storage

View File

@ -0,0 +1,35 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Storage {
class FileLock {
public:
FileLock();
bool lock(const QFile &file);
void unlock();
static constexpr auto kSkipBytes = size_type(4);
~FileLock();
private:
class Lock;
struct Descriptor;
struct LockingPid;
static constexpr auto kLockOffset = index_type(0);
static constexpr auto kLockLimit = kSkipBytes;
std::unique_ptr<Lock> _lock;
};
} // namespace Storage

View File

@ -0,0 +1,110 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_file_lock.h"
#include "base/variant.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>
namespace Storage {
namespace {
bool KillProcess(pid_t pid) {
while (true) {
const auto result = kill(pid, SIGTERM);
if (result < 0) {
return (errno == ESRCH);
}
usleep(10000);
}
}
} // namespace
struct FileLock::Descriptor {
int value;
};
struct FileLock::LockingPid {
pid_t value;
};
class FileLock::Lock {
public:
using Result = base::variant<Descriptor, LockingPid>;
static Result Acquire(const QFile &file);
explicit Lock(int descriptor);
~Lock();
private:
int _descriptor = 0;
};
FileLock::Lock::Result FileLock::Lock::Acquire(const QFile &file) {
const auto descriptor = file.handle();
if (!descriptor || !file.isOpen()) {
return Descriptor{ 0 };
}
while (true) {
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = kLockOffset;
lock.l_len = kLockLimit;
if (fcntl(descriptor, F_SETLK, &lock) == 0) {
return Descriptor{ descriptor };
} else if (fcntl(descriptor, F_GETLK, &lock) < 0) {
return LockingPid{ 0 };
} else if (lock.l_type != F_UNLCK) {
return LockingPid{ lock.l_pid };
}
}
}
FileLock::Lock::~Lock() {
struct flock unlock;
unlock.l_type = F_UNLCK;
unlock.l_whence = SEEK_SET;
unlock.l_start = kLockOffset;
unlock.l_len = kLockLimit;
fcntl(_descriptor, F_SETLK, &unlock);
}
FileLock::FileLock() = default;
bool FileLock::lock(const QFile &file) {
unlock();
while (true) {
const auto result = Lock::Acquire(file);
if (const auto descriptor = base::get_if<Descriptor>(&result)) {
if (descriptor->value > 0) {
_lock = std::make_unique<Lock>(descriptor->value);
return true;
}
return false;
} else if (const auto pid = base::get_if<LockingPid>(&result)) {
if (pid->value <= 0 || !KillProcess(pid->value)) {
return false;
}
}
}
return false;
}
void FileLock::unlock() {
_lock = nullptr;
}
FileLock::~FileLock() = default;
} // namespace Storage

View File

@ -0,0 +1,137 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_file_lock.h"
#include "platform/win/windows_dlls.h"
#include <io.h>
#include <windows.h>
#include <fileapi.h>
#include <RestartManager.h>
namespace Storage {
namespace {
bool CloseProcesses(const QString &filename) {
using namespace Platform;
if (!Dlls::RmStartSession
|| !Dlls::RmRegisterResources
|| !Dlls::RmGetList
|| !Dlls::RmShutdown
|| !Dlls::RmEndSession) {
return false;
}
auto result = BOOL(FALSE);
auto session = DWORD();
auto sessionKey = std::wstring(CCH_RM_SESSION_KEY + 1, wchar_t(0));
auto error = Dlls::RmStartSession(&session, 0, sessionKey.data());
if (error != ERROR_SUCCESS) {
return false;
}
const auto guard = gsl::finally([&] { Dlls::RmEndSession(session); });
const auto path = QDir::toNativeSeparators(filename).toStdWString();
auto nullterm = path.c_str();
error = Dlls::RmRegisterResources(
session,
1,
&nullterm,
0,
nullptr,
0,
nullptr);
if (error != ERROR_SUCCESS) {
return false;
}
auto processInfoNeeded = UINT(0);
auto processInfoCount = UINT(0);
auto reason = DWORD();
error = Dlls::RmGetList(
session,
&processInfoNeeded,
&processInfoCount,
nullptr,
&reason);
if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) {
return false;
} else if (processInfoNeeded <= 0) {
return true;
}
error = Dlls::RmShutdown(session, RmForceShutdown, NULL);
if (error != ERROR_SUCCESS) {
return false;
}
return true;
}
} // namespace
class FileLock::Lock {
public:
static int Acquire(const QFile &file);
explicit Lock(int descriptor);
~Lock();
private:
static constexpr auto offsetLow = DWORD(kLockOffset);
static constexpr auto offsetHigh = DWORD(0);
static constexpr auto limitLow = DWORD(kLockLimit);
static constexpr auto limitHigh = DWORD(0);
int _descriptor = 0;
};
int FileLock::Lock::Acquire(const QFile &file) {
const auto descriptor = file.handle();
if (!descriptor || !file.isOpen()) {
return false;
}
const auto handle = HANDLE(_get_osfhandle(descriptor));
if (!handle) {
return false;
}
return LockFile(handle, offsetLow, offsetHigh, limitLow, limitHigh)
? descriptor
: 0;
}
FileLock::Lock::Lock(int descriptor) : _descriptor(descriptor) {
}
FileLock::Lock::~Lock() {
if (const auto handle = HANDLE(_get_osfhandle(_descriptor))) {
UnlockFile(handle, offsetLow, offsetHigh, limitLow, limitHigh);
}
}
FileLock::FileLock() = default;
bool FileLock::lock(const QFile &file) {
do {
unlock();
if (const auto descriptor = Lock::Acquire(file)) {
_lock = std::make_unique<Lock>(descriptor);
return true;
}
} while (CloseProcesses(file.fileName()));
return false;
}
void FileLock::unlock() {
_lock = nullptr;
}
FileLock::~FileLock() = default;
} // namespace Storage

View File

@ -0,0 +1,9 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_pch.h"

View File

@ -0,0 +1,30 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QByteArray>
#include <QtCore/QString>
#include <crl/crl.h>
#include <rpl/rpl.h>
#include <vector>
#include <range/v3/all.hpp>
#ifdef Q_OS_WIN
#include "platform/win/windows_range_v3_helpers.h"
#endif // Q_OS_WIN
#include "base/flat_map.h"
#include "base/flat_set.h"
#include "base/optional.h"
#include "base/openssl_help.h"
#include "logs.h"

View File

@ -78,6 +78,7 @@
'../ThirdParty/libtgvoip/libtgvoip.gyp:libtgvoip',
'crl.gyp:crl',
'lib_export.gyp:lib_export',
'lib_storage.gyp:lib_storage',
],
'defines': [

View File

@ -0,0 +1,72 @@
# This file is part of Telegram Desktop,
# the official desktop application for the Telegram messaging service.
#
# For license and copyright information please follow this link:
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
{
'includes': [
'common.gypi',
],
'targets': [{
'target_name': 'lib_storage',
'type': 'static_library',
'includes': [
'common.gypi',
'qt.gypi',
'telegram_win.gypi',
'telegram_mac.gypi',
'telegram_linux.gypi',
'pch.gypi',
],
'variables': {
'src_loc': '../SourceFiles',
'res_loc': '../Resources',
'libs_loc': '../../../Libraries',
'official_build_target%': '',
'submodules_loc': '../ThirdParty',
'pch_source': '<(src_loc)/storage/storage_pch.cpp',
'pch_header': '<(src_loc)/storage/storage_pch.h',
},
'defines': [
],
'dependencies': [
'crl.gyp:crl',
],
'include_dirs': [
'<(src_loc)',
'<(SHARED_INTERMEDIATE_DIR)',
'<(libs_loc)/range-v3/include',
'<(submodules_loc)/GSL/include',
'<(submodules_loc)/variant/include',
'<(submodules_loc)/crl/src',
],
'sources': [
'<(src_loc)/storage/storage_encryption.cpp',
'<(src_loc)/storage/storage_encryption.h',
'<(src_loc)/storage/storage_encrypted_file.cpp',
'<(src_loc)/storage/storage_encrypted_file.h',
'<(src_loc)/storage/storage_file_lock_posix.cpp',
'<(src_loc)/storage/storage_file_lock_win.cpp',
'<(src_loc)/storage/storage_file_lock.h',
'<(src_loc)/storage/cache/storage_cache_database.cpp',
'<(src_loc)/storage/cache/storage_cache_database.h',
],
'conditions': [[ 'build_macold', {
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ],
},
'include_dirs': [
'/usr/local/macold/include/c++/v1',
],
}], [ 'build_win', {
'sources!': [
'<(src_loc)/storage/storage_file_lock_posix.cpp',
],
}, {
'sources!': [
'<(src_loc)/storage/storage_file_lock_win.cpp',
],
}]],
}],
}