0b8e58351e4b3a1327f0e9df0e57d1884132f0a6
[platform/core/security/cynara.git] / src / storage / Integrity.cpp
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
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fstream>
26 #include <functional>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #include <config/PathConfig.h>
33 #include <exceptions/CannotCreateFileException.h>
34 #include <exceptions/UnexpectedErrorException.h>
35 #include <log/log.h>
36
37 #include "Integrity.h"
38
39 namespace Cynara {
40
41 namespace StorageConfig = PathConfig::StoragePath;
42
43 const std::string Integrity::m_guardFilename(StorageConfig::guardFilename);
44 const std::string Integrity::m_indexFilename(StorageConfig::indexFilename);
45 const std::string Integrity::m_backupFilenameSuffix(StorageConfig::backupFilenameSuffix);
46 const std::string Integrity::m_bucketFilenamePrefix(StorageConfig::bucketFilenamePrefix);
47
48 bool Integrity::backupGuardExists(void) const {
49     struct stat buffer;
50     std::string guardFilename = m_dbPath + m_guardFilename;
51
52     int ret = stat(guardFilename.c_str(), &buffer);
53
54     if (ret == 0) {
55         return true;
56     } else {
57         int err = errno;
58         if (err != ENOENT) {
59             LOGE("'stat' function error [%d] : <%s>", err, strerror(err));
60             throw UnexpectedErrorException(err, strerror(err));
61         }
62         return false;
63     }
64 }
65
66 void Integrity::createBackupGuard(void) const {
67     syncElement(m_dbPath + m_guardFilename, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC);
68     syncDirectory(m_dbPath);
69 }
70
71 void Integrity::syncDatabase(const Buckets &buckets, bool syncBackup) {
72     std::string suffix = "";
73
74     if (syncBackup) {
75         suffix += m_backupFilenameSuffix;
76     }
77
78     for (const auto &bucketIter : buckets) {
79         const auto &bucketId = bucketIter.first;
80         const auto &bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId + suffix;
81
82         syncElement(bucketFilename);
83     }
84
85     syncElement(m_dbPath + m_indexFilename + suffix);
86     syncElement(m_dbPath + PathConfig::StoragePath::checksumFilename + suffix);
87     syncDirectory(m_dbPath);
88 }
89
90 void Integrity::revalidatePrimaryDatabase(const Buckets &buckets) {
91     createPrimaryHardLinks(buckets);
92     syncDatabase(buckets, false);
93
94     deleteHardLink(m_dbPath + m_guardFilename);
95     syncDirectory(m_dbPath);
96
97     deleteBackupHardLinks(buckets);
98 }
99
100 void Integrity::deleteNonIndexedFiles(BucketPresenceTester tester) {
101     DIR *dirPtr = nullptr;
102     struct dirent *direntPtr;
103
104     if ((dirPtr = opendir(m_dbPath.c_str())) == nullptr) {
105         int err = errno;
106         LOGE("'opendir' function error [%d] : <%s>", err, strerror(err));
107         throw UnexpectedErrorException(err, strerror(err));
108         return;
109     }
110
111     while (errno = 0, (direntPtr = readdir(dirPtr)) != nullptr) {
112         std::string filename = direntPtr->d_name;
113         //ignore all special files (working dir, parent dir, index, checksums)
114         if (isSpecialDirectory(filename) || isSpecialDatabaseEntry(filename)) {
115             continue;
116         }
117
118         std::string bucketId;
119         auto nameLength = filename.length();
120         auto prefixLength = m_bucketFilenamePrefix.length();
121
122         //remove if it is impossible that it is a bucket file
123         if (nameLength < prefixLength) {
124             deleteHardLink(m_dbPath + filename);
125             continue;
126         }
127
128         //remove if there is no bucket filename prefix
129         //0 is returned from string::compare() if strings are equal
130         if (0 != filename.compare(0, prefixLength, m_bucketFilenamePrefix)) {
131             deleteHardLink(m_dbPath + filename);
132             continue;
133         }
134
135         //remove if bucket is not in index
136         bucketId = filename.substr(prefixLength);
137         if (!tester(bucketId)) {
138             deleteHardLink(m_dbPath + filename);
139         }
140     }
141
142     if (errno) {
143         int err = errno;
144         LOGE("'readdir' function error [%d] : <%s>", err, strerror(err));
145         throw UnexpectedErrorException(err, strerror(err));
146         return;
147     }
148 }
149
150 void Integrity::syncElement(const std::string &filename, int flags, mode_t mode) {
151     int fileFd = TEMP_FAILURE_RETRY(open(filename.c_str(), flags, mode));
152
153     if (fileFd < 0) {
154         int err = errno;
155         if (err != EEXIST) {
156             LOGE("File <%s> : 'open' function error [%d] : <%s>", filename.c_str(), err,
157                  strerror(err));
158             throw UnexpectedErrorException(err, strerror(err));
159         } else {
160             throw CannotCreateFileException(filename);
161         }
162     }
163
164     int ret = fsync(fileFd);
165
166     if (ret < 0) {
167         int err = errno;
168         LOGE("'fsync' function error [%d] : <%s>", err, strerror(err));
169         throw UnexpectedErrorException(err, strerror(err));
170     }
171
172     ret = close(fileFd);
173
174     if (ret < 0) {
175         int err = errno;
176         LOGE("'close' function error [%d] : <%s>", err, strerror(err));
177         throw UnexpectedErrorException(err, strerror(err));
178     }
179 }
180
181 // from: man 2 fsync
182 // Calling fsync() does not necessarily ensure that the entry in the directory containing
183 // the file has also reached disk.  For that an explicit fsync() on a file descriptor for
184 // the directory is also needed.
185 void Integrity::syncDirectory(const std::string &dirname, mode_t mode) {
186     syncElement(dirname, O_DIRECTORY, mode);
187 }
188
189 void Integrity::createPrimaryHardLinks(const Buckets &buckets) {
190     for (const auto &bucketIter : buckets) {
191         const auto &bucketId = bucketIter.first;
192         const auto &bucketFilename = m_dbPath + m_bucketFilenamePrefix + bucketId;
193
194         deleteHardLink(bucketFilename);
195         createHardLink(bucketFilename + m_backupFilenameSuffix, bucketFilename);
196     }
197
198     const auto &indexFilename = m_dbPath + m_indexFilename;
199     const auto &checksumFilename = m_dbPath + PathConfig::StoragePath::checksumFilename;
200
201     deleteHardLink(indexFilename);
202     createHardLink(indexFilename + m_backupFilenameSuffix, indexFilename);
203     deleteHardLink(checksumFilename);
204     createHardLink(checksumFilename + m_backupFilenameSuffix, checksumFilename);
205 }
206
207 void Integrity::deleteBackupHardLinks(const Buckets &buckets) {
208     for (const auto &bucketIter : buckets) {
209         const auto &bucketId = bucketIter.first;
210         const auto &bucketFilename = m_dbPath + m_bucketFilenamePrefix +
211                                      bucketId + m_backupFilenameSuffix;
212
213         deleteHardLink(bucketFilename);
214     }
215
216     deleteHardLink(m_dbPath + m_indexFilename + m_backupFilenameSuffix);
217     deleteHardLink(m_dbPath + PathConfig::StoragePath::checksumFilename + m_backupFilenameSuffix);
218 }
219
220 void Integrity::createHardLink(const std::string &oldName, const std::string &newName) {
221     int ret = link(oldName.c_str(), newName.c_str());
222
223     if (ret < 0) {
224         int err = errno;
225         throw UnexpectedErrorException(err, strerror(err));
226         LOGN("Trying to link to non-existent file: <%s>", oldName.c_str());
227     }
228 }
229
230 void Integrity::deleteHardLink(const std::string &filename) {
231     int ret = unlink(filename.c_str());
232
233     if (ret < 0) {
234         int err = errno;
235         if (err != ENOENT) {
236             LOGE("'unlink' function error [%d] : <%s>", err, strerror(err));
237             throw UnexpectedErrorException(err, strerror(err));
238         } else {
239             LOGN("Trying to unlink non-existent file: <%s>", filename.c_str());
240         }
241     }
242 }
243
244 bool Integrity::isSpecialDirectory(const std::string &filename) {
245     return "." == filename || ".." == filename;
246 }
247
248 bool Integrity::isSpecialDatabaseEntry(const std::string &filename) {
249     return PathConfig::StoragePath::indexFilename == filename ||
250            PathConfig::StoragePath::checksumFilename == filename;
251 }
252
253 } /* namespace Cynara */