storage_file_lock_win.cpp 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "storage/storage_file_lock.h"
  8. #include "platform/win/windows_dlls.h"
  9. #include "base/platform/win/base_windows_h.h"
  10. #include <io.h>
  11. #include <fileapi.h>
  12. #include <RestartManager.h>
  13. namespace Storage {
  14. namespace {
  15. bool CloseProcesses(const QString &filename) {
  16. using namespace Platform;
  17. if (!Dlls::RmStartSession
  18. || !Dlls::RmRegisterResources
  19. || !Dlls::RmGetList
  20. || !Dlls::RmShutdown
  21. || !Dlls::RmEndSession) {
  22. return false;
  23. }
  24. auto result = BOOL(FALSE);
  25. auto session = DWORD();
  26. auto sessionKey = std::wstring(CCH_RM_SESSION_KEY + 1, wchar_t(0));
  27. auto error = Dlls::RmStartSession(&session, 0, sessionKey.data());
  28. if (error != ERROR_SUCCESS) {
  29. return false;
  30. }
  31. const auto guard = gsl::finally([&] { Dlls::RmEndSession(session); });
  32. const auto path = QDir::toNativeSeparators(filename).toStdWString();
  33. auto nullterm = path.c_str();
  34. error = Dlls::RmRegisterResources(
  35. session,
  36. 1,
  37. &nullterm,
  38. 0,
  39. nullptr,
  40. 0,
  41. nullptr);
  42. if (error != ERROR_SUCCESS) {
  43. return false;
  44. }
  45. auto processInfoNeeded = UINT(0);
  46. auto processInfoCount = UINT(0);
  47. auto reason = DWORD();
  48. error = Dlls::RmGetList(
  49. session,
  50. &processInfoNeeded,
  51. &processInfoCount,
  52. nullptr,
  53. &reason);
  54. if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) {
  55. return false;
  56. } else if (processInfoNeeded <= 0) {
  57. return true;
  58. }
  59. error = Dlls::RmShutdown(session, RmForceShutdown, NULL);
  60. if (error != ERROR_SUCCESS) {
  61. return false;
  62. }
  63. return true;
  64. }
  65. } // namespace
  66. class FileLock::Lock {
  67. public:
  68. static int Acquire(const QFile &file);
  69. explicit Lock(int descriptor);
  70. ~Lock();
  71. private:
  72. static constexpr auto offsetLow = DWORD(kLockOffset);
  73. static constexpr auto offsetHigh = DWORD(0);
  74. static constexpr auto limitLow = DWORD(kLockLimit);
  75. static constexpr auto limitHigh = DWORD(0);
  76. int _descriptor = 0;
  77. };
  78. int FileLock::Lock::Acquire(const QFile &file) {
  79. const auto descriptor = file.handle();
  80. if (!descriptor || !file.isOpen()) {
  81. return false;
  82. }
  83. const auto handle = HANDLE(_get_osfhandle(descriptor));
  84. if (!handle) {
  85. return false;
  86. }
  87. return LockFile(handle, offsetLow, offsetHigh, limitLow, limitHigh)
  88. ? descriptor
  89. : 0;
  90. }
  91. FileLock::Lock::Lock(int descriptor) : _descriptor(descriptor) {
  92. }
  93. FileLock::Lock::~Lock() {
  94. if (const auto handle = HANDLE(_get_osfhandle(_descriptor))) {
  95. UnlockFile(handle, offsetLow, offsetHigh, limitLow, limitHigh);
  96. }
  97. }
  98. FileLock::FileLock() = default;
  99. bool FileLock::lock(QFile &file, QIODevice::OpenMode mode) {
  100. Expects(_lock == nullptr || file.isOpen());
  101. unlock();
  102. file.close();
  103. do {
  104. if (!file.open(mode)) {
  105. return false;
  106. } else if (const auto descriptor = Lock::Acquire(file)) {
  107. _lock = std::make_unique<Lock>(descriptor);
  108. return true;
  109. }
  110. file.close();
  111. } while (CloseProcesses(file.fileName()));
  112. return false;
  113. }
  114. void FileLock::unlock() {
  115. _lock = nullptr;
  116. }
  117. FileLock::~FileLock() = default;
  118. } // namespace Storage