e8b66ad64e97b18408b076772b602fec92aa89fd
[platform/core/security/ode.git] / server / secure-erase.cpp
1 /*
2  *  Copyright (c) 2017 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 #include <fstream>
17
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <linux/fs.h>
21 #include <linux/fiemap.h>
22
23 #include <vconf.h>
24 #include <klay/exception.h>
25 #include <klay/filesystem.h>
26 #include <klay/audit/logger.h>
27
28 #include "ext4-tool.h"
29 #include "block-device.h"
30 #include "progress-bar.h"
31 #include "rmi/secure-erase.h"
32
33 #define PRIVILEGE_PLATFORM "http://tizen.org/privilege/internal/default/platform"
34
35 namespace ode {
36
37 namespace {
38
39 std::unique_ptr<ProgressBar> progressBar;
40
41 static int totalFileCount = 0;
42 static int erasedFileCount = 0;
43
44 static int getTotalFileCount(const std::string &name)
45 {
46         int total = 0;
47         runtime::DirectoryIterator iter(name), end;
48
49         while (iter != end) {
50                 std::string next = name + "/" + iter->getName();
51                 runtime::File file(next);
52                 if (!file.exists()) {
53                         return -1;
54                 }
55
56                 if (file.isFile()) {
57                         total++;
58                 } else if (file.isDirectory()) {
59                         int subTotal = getTotalFileCount(next);
60                         if (subTotal != -1)
61                                 total += subTotal;
62                 }
63                 ++iter;
64         }
65
66         return total;
67 }
68
69 } /* namespace */
70
71 SecureErase::SecureErase(ODEControlContext &ctx) :
72         context(ctx), devicePath("")
73 {
74         context.expose(this, PRIVILEGE_PLATFORM, (int)(SecureErase::erase)(std::string));
75         context.expose(this, PRIVILEGE_PLATFORM, (int)(SecureErase::clean)(std::string));
76
77         progressBar.reset(new ProgressBar([](int v) {
78                 ::vconf_set_str(VCONFKEY_ODE_ERASE_PROGRESS, std::to_string(v).c_str());
79         }));
80 }
81
82 SecureErase::~SecureErase()
83 {
84 }
85
86 int SecureErase::erase(const std::string &name)
87 {
88         auto eraseWorker = [name, this]() {
89                 try {
90                         runtime::File file(name);
91
92                         BlockDevice blockDevice(name);
93                         devicePath = blockDevice.getName();
94
95                         if (file.isFile()) {
96                                 totalFileCount = 1;
97                                 fileErase(name);
98                         } else if (file.isDirectory()) {
99                                 totalFileCount = getTotalFileCount(name);
100                                 erasedFileCount = 0;
101                                 directoryErase(name);
102                         } else if (file.isDevice()) {
103                                 Ext4Tool ext4Tool(name);
104                                 unsigned int totalBlock = ext4Tool.getTotalBlockCount();
105                                 unsigned int blockSize = (unsigned int)blockDevice.getSize();
106
107                                 for (unsigned int i = 0; i < totalBlock; i++) {
108                                         Block block(i * blockSize, blockSize);
109                                         blockDevice.discard(block);
110                                         progressBar->update(i, totalBlock, 1);
111                                 }
112                         }
113                         dropCachePage();
114                         progressBar->done();
115                 } catch (runtime::Exception &e) {}
116         };
117
118         std::thread asyncWork(eraseWorker);
119         asyncWork.detach();
120
121         return 0;
122 }
123
124 int SecureErase::clean(const std::string &name)
125 {
126         try {
127                 runtime::File file(name, O_WRONLY);
128                 if (!file.exists() || !file.isDevice())
129                         return -1;
130         } catch (runtime::Exception &e) {}
131
132         auto cleanWorker = [name, this]() {
133                 try {
134                         BlockDevice blockDevice(name);
135                         unsigned totalBlock, blockSize;
136
137                         Ext4Tool ext4Tool(name);
138                         totalBlock = ext4Tool.getTotalBlockCount();
139                         blockSize = (unsigned int) blockDevice.getSize();
140
141                         for (unsigned int i = 0; i < totalBlock; i++) {
142                                 if (!ext4Tool.isUsedBlock(i)) {
143                                         Block block(i * blockSize, blockSize);
144                                         blockDevice.discard(block);
145                                 }
146
147                                 progressBar->update(i, totalBlock, 1);
148                         }
149                         dropCachePage();
150                         progressBar->done();
151                 } catch (runtime::Exception &e) {}
152         };
153
154         std::thread asyncWork(cleanWorker);
155         asyncWork.detach();
156
157         return 0;
158 }
159
160 int SecureErase::fileErase(const std::string &name)
161 {
162         int ret = 0, fd = 0;
163         int extentBlockCount = 0;
164         char buf[4096] = "";
165         struct fiemap *fmap = (struct fiemap *)buf;
166         struct fiemap_extent *fm_ext = NULL;
167         int count = (sizeof(buf) - sizeof(*fmap)) / sizeof(struct fiemap_extent);
168
169         /* [TBD] stop the related process */
170
171         BlockDevice blockDevice(devicePath);
172
173         ::memset(fmap, 0, sizeof(struct fiemap));
174
175         fd = ::open(name.c_str(), O_RDONLY);
176         if (fd < 0) {
177                 return -1;
178         }
179
180         fmap->fm_length = ~0ULL;
181         fmap->fm_flags = 0;
182         fmap->fm_extent_count = count;
183
184         ret = ::ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fmap);
185         if (ret < 0) {
186                 ::close(fd);
187                 return -1;
188         }
189
190         ::close(fd);
191
192         fm_ext = &fmap->fm_extents[0];
193         extentBlockCount = (int)fmap->fm_mapped_extents;
194
195         for (int i = 0; i < extentBlockCount; i++) {
196                 Block block(fm_ext[i].fe_physical, fm_ext[i].fe_length);
197                 ret = blockDevice.secDiscard(block);
198                 if (ret < 0) {
199                         return -1;
200                 }
201
202                 if (totalFileCount == 1) {
203                         progressBar->update(i, extentBlockCount, 1);
204                 }
205         }
206
207         return ret;
208 }
209
210 int SecureErase::directoryErase(const std::string &name)
211 {
212         runtime::DirectoryIterator iter(name), end;
213         while (iter != end) {
214                 std::string next = name + "/" + iter->getName();
215                 runtime::File file(next);
216                 if (!file.exists()) {
217                         return -1;
218                 }
219
220                 if (file.isFile()) {
221                         fileErase(next);
222                         ::remove(next.c_str());
223                         erasedFileCount++;
224                         progressBar->update(erasedFileCount, totalFileCount, 1);
225                 } else if (file.isDirectory()) {
226                         directoryErase(next);
227                 }
228                 ++iter;
229         }
230
231         fileErase(name);
232         ::rmdir(name.c_str());
233         return 0;
234 }
235
236 void SecureErase::dropCachePage()
237 {
238         std::ofstream file;
239
240         file.open("/proc/sys/vm/drop_caches");
241         if (file.fail()) {
242                 throw runtime::Exception("Failed to access drop_caches file");
243         }
244         file << "3\n";
245         file.close();
246         ::sync();
247
248         return;
249 }
250
251 } // namespace ode