PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/storage/Integrity.cpp

https://gitlab.com/github-cloud-corporation/cynara
C++ | 260 lines | 183 code | 47 blank | 30 comment | 37 complexity | e88a1372b71c6d26185c3acd71d3846f MD5 | raw file
  1. /*
  2. * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /**
  17. * @file src/storage/Integrity.cpp
  18. * @author Pawel Wieczorek <p.wieczorek2@samsung.com>
  19. * @version 0.1
  20. * @brief Implementation of Cynara::Integrity
  21. */
  22. #include <dirent.h>
  23. #include <errno.h>
  24. #include <fstream>
  25. #include <functional>
  26. #include <memory>
  27. #include <string.h>
  28. #include <sys/stat.h>
  29. #include <sys/types.h>
  30. #include <unistd.h>
  31. #include <config/PathConfig.h>
  32. #include <exceptions/CannotCreateFileException.h>
  33. #include <exceptions/UnexpectedErrorException.h>
  34. #include <log/log.h>
  35. #include "Integrity.h"
  36. namespace Cynara {
  37. namespace StorageConfig = PathConfig::StoragePath;
  38. bool Integrity::backupGuardExists(void) const {
  39. struct stat buffer;
  40. std::string guardFilename = m_dbPath + StorageConfig::guardFilename;
  41. int ret = stat(guardFilename.c_str(), &buffer);
  42. if (ret == 0) {
  43. return true;
  44. } else {
  45. int err = errno;
  46. if (err != ENOENT) {
  47. LOGE("'stat' function error [%d] : <%s>", err, strerror(err));
  48. throw UnexpectedErrorException(err, strerror(err));
  49. }
  50. return false;
  51. }
  52. }
  53. void Integrity::createBackupGuard(void) const {
  54. syncElement(m_dbPath + StorageConfig::guardFilename, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC);
  55. syncDirectory(m_dbPath);
  56. }
  57. void Integrity::syncDatabase(const Buckets &buckets, bool syncBackup) {
  58. std::string suffix = "";
  59. if (syncBackup) {
  60. suffix += StorageConfig::backupFilenameSuffix;
  61. }
  62. for (const auto &bucketIter : buckets) {
  63. const auto &bucketId = bucketIter.first;
  64. const auto &bucketFilename = m_dbPath + StorageConfig::bucketFilenamePrefix +
  65. bucketId + suffix;
  66. syncElement(bucketFilename);
  67. }
  68. syncElement(m_dbPath + StorageConfig::indexFilename + suffix);
  69. syncElement(m_dbPath + PathConfig::StoragePath::checksumFilename + suffix);
  70. syncDirectory(m_dbPath);
  71. }
  72. void Integrity::revalidatePrimaryDatabase(const Buckets &buckets) {
  73. createPrimaryHardLinks(buckets);
  74. syncDatabase(buckets, false);
  75. deleteHardLink(m_dbPath + StorageConfig::guardFilename);
  76. syncDirectory(m_dbPath);
  77. deleteBackupHardLinks(buckets);
  78. }
  79. void Integrity::deleteNonIndexedFiles(BucketPresenceTester tester) {
  80. DIR *dirPtr = nullptr;
  81. struct dirent *direntPtr;
  82. if ((dirPtr = opendir(m_dbPath.c_str())) == nullptr) {
  83. int err = errno;
  84. LOGE("'opendir' function error [%d] : <%s>", err, strerror(err));
  85. throw UnexpectedErrorException(err, strerror(err));
  86. return;
  87. }
  88. std::unique_ptr<DIR, std::function<void(DIR*)>> dirStream(dirPtr,
  89. [](DIR *dir) {
  90. if (closedir(dir) < 0) {
  91. int err = errno;
  92. (void) err;
  93. LOGE("'closedir' function error [%d] : <%s>", err, strerror(err));
  94. }
  95. });
  96. while (errno = 0, (direntPtr = readdir(dirPtr)) != nullptr) {
  97. std::string filename = direntPtr->d_name;
  98. //ignore all special files (working dir, parent dir, index, checksums)
  99. if (isSpecialDirectory(filename) || isSpecialDatabaseEntry(filename)) {
  100. continue;
  101. }
  102. std::string bucketId;
  103. auto nameLength = filename.length();
  104. auto prefixLength = StorageConfig::bucketFilenamePrefix.length();
  105. //remove if it is impossible that it is a bucket file
  106. if (nameLength < prefixLength) {
  107. deleteHardLink(m_dbPath + filename);
  108. continue;
  109. }
  110. //remove if there is no bucket filename prefix
  111. //0 is returned from string::compare() if strings are equal
  112. if (0 != filename.compare(0, prefixLength, StorageConfig::bucketFilenamePrefix)) {
  113. deleteHardLink(m_dbPath + filename);
  114. continue;
  115. }
  116. //remove if bucket is not in index
  117. bucketId = filename.substr(prefixLength);
  118. if (!tester(bucketId)) {
  119. deleteHardLink(m_dbPath + filename);
  120. }
  121. }
  122. if (errno) {
  123. int err = errno;
  124. LOGE("'readdir' function error [%d] : <%s>", err, strerror(err));
  125. throw UnexpectedErrorException(err, strerror(err));
  126. return;
  127. }
  128. }
  129. void Integrity::syncElement(const std::string &filename, int flags, mode_t mode) {
  130. int fileFd = TEMP_FAILURE_RETRY(open(filename.c_str(), flags, mode));
  131. if (fileFd < 0) {
  132. int err = errno;
  133. if (err != EEXIST) {
  134. LOGE("File <%s> : 'open' function error [%d] : <%s>", filename.c_str(), err,
  135. strerror(err));
  136. throw UnexpectedErrorException(err, strerror(err));
  137. } else {
  138. throw CannotCreateFileException(filename);
  139. }
  140. }
  141. int ret = fsync(fileFd);
  142. if (ret < 0) {
  143. int err = errno;
  144. LOGE("'fsync' function error [%d] : <%s>", err, strerror(err));
  145. throw UnexpectedErrorException(err, strerror(err));
  146. }
  147. ret = close(fileFd);
  148. if (ret < 0) {
  149. int err = errno;
  150. LOGE("'close' function error [%d] : <%s>", err, strerror(err));
  151. throw UnexpectedErrorException(err, strerror(err));
  152. }
  153. }
  154. // from: man 2 fsync
  155. // Calling fsync() does not necessarily ensure that the entry in the directory containing
  156. // the file has also reached disk. For that an explicit fsync() on a file descriptor for
  157. // the directory is also needed.
  158. void Integrity::syncDirectory(const std::string &dirname, mode_t mode) {
  159. syncElement(dirname, O_DIRECTORY, mode);
  160. }
  161. void Integrity::createPrimaryHardLinks(const Buckets &buckets) {
  162. for (const auto &bucketIter : buckets) {
  163. const auto &bucketId = bucketIter.first;
  164. const auto &bucketFilename = m_dbPath + StorageConfig::bucketFilenamePrefix + bucketId;
  165. deleteHardLink(bucketFilename);
  166. createHardLink(bucketFilename + StorageConfig::backupFilenameSuffix, bucketFilename);
  167. }
  168. const auto &indexFilename = m_dbPath + StorageConfig::indexFilename;
  169. const auto &checksumFilename = m_dbPath + PathConfig::StoragePath::checksumFilename;
  170. deleteHardLink(indexFilename);
  171. createHardLink(indexFilename + StorageConfig::backupFilenameSuffix, indexFilename);
  172. deleteHardLink(checksumFilename);
  173. createHardLink(checksumFilename + StorageConfig::backupFilenameSuffix, checksumFilename);
  174. }
  175. void Integrity::deleteBackupHardLinks(const Buckets &buckets) {
  176. for (const auto &bucketIter : buckets) {
  177. const auto &bucketId = bucketIter.first;
  178. const auto &bucketFilename = m_dbPath + StorageConfig::bucketFilenamePrefix +
  179. bucketId + StorageConfig::backupFilenameSuffix;
  180. deleteHardLink(bucketFilename);
  181. }
  182. deleteHardLink(m_dbPath + StorageConfig::indexFilename + StorageConfig::backupFilenameSuffix);
  183. deleteHardLink(m_dbPath + StorageConfig::checksumFilename +
  184. StorageConfig::backupFilenameSuffix);
  185. }
  186. void Integrity::createHardLink(const std::string &oldName, const std::string &newName) {
  187. int ret = link(oldName.c_str(), newName.c_str());
  188. if (ret < 0) {
  189. int err = errno;
  190. throw UnexpectedErrorException(err, strerror(err));
  191. LOGN("Trying to link to non-existent file: <%s>", oldName.c_str());
  192. }
  193. }
  194. void Integrity::deleteHardLink(const std::string &filename) {
  195. int ret = unlink(filename.c_str());
  196. if (ret < 0) {
  197. int err = errno;
  198. if (err != ENOENT) {
  199. LOGE("'unlink' function error [%d] : <%s>", err, strerror(err));
  200. throw UnexpectedErrorException(err, strerror(err));
  201. } else {
  202. LOGN("Trying to unlink non-existent file: <%s>", filename.c_str());
  203. }
  204. }
  205. }
  206. bool Integrity::isSpecialDirectory(const std::string &filename) {
  207. return "." == filename || ".." == filename;
  208. }
  209. bool Integrity::isSpecialDatabaseEntry(const std::string &filename) {
  210. return PathConfig::StoragePath::indexFilename == filename ||
  211. PathConfig::StoragePath::checksumFilename == filename;
  212. }
  213. } /* namespace Cynara */