1 #include "estoraged_test.hpp"
2
3 #include "estoraged.hpp"
4 #include "estoraged_conf.hpp"
5
6 #include <linux/mmc/core.h>
7 #include <linux/mmc/ioctl.h>
8 #include <linux/mmc/mmc.h>
9 #include <unistd.h>
10
11 #include <boost/asio/io_context.hpp>
12 #include <sdbusplus/asio/connection.hpp>
13 #include <sdbusplus/asio/object_server.hpp>
14 #include <xyz/openbmc_project/Common/error.hpp>
15 #include <xyz/openbmc_project/Inventory/Item/Volume/server.hpp>
16
17 #include <array>
18 #include <exception>
19 #include <filesystem>
20 #include <fstream>
21 #include <iterator>
22 #include <memory>
23 #include <span>
24 #include <string>
25 #include <vector>
26
27 #include <gmock/gmock.h>
28 #include <gtest/gtest.h>
29
30 namespace estoraged_test
31 {
32
33 using sdbusplus::server::xyz::openbmc_project::inventory::item::Volume;
34 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
35 using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound;
36 using std::filesystem::path;
37 using ::testing::_;
38 using ::testing::ElementsAreArray;
39 using ::testing::Return;
40 using ::testing::StrEq;
41
42 class MockFd : public stdplus::Fd
43 {
44 public:
45 MOCK_METHOD(int, ioctl, (unsigned long id, void* data), (override));
46 MOCK_METHOD(int, constIoctl, (unsigned long id, void* data),
47 (const, override));
48 MOCK_METHOD(std::span<std::byte>, read, (std::span<std::byte>), (override));
49 MOCK_METHOD(std::span<std::byte>, recv,
50 (std::span<std::byte>, stdplus::fd::RecvFlags), (override));
51 MOCK_METHOD((std::tuple<std::span<std::byte>, std::span<std::byte>>),
52 recvfrom,
53 (std::span<std::byte>, stdplus::fd::RecvFlags,
54 std::span<std::byte>),
55 (override));
56 MOCK_METHOD(std::span<const std::byte>, write, (std::span<const std::byte>),
57 (override));
58 MOCK_METHOD(std::span<const std::byte>, send,
59 (std::span<const std::byte>, stdplus::fd::SendFlags),
60 (override));
61 MOCK_METHOD(std::span<const std::byte>, sendto,
62 (std::span<const std::byte>, stdplus::fd::SendFlags,
63 std::span<const std::byte>),
64 (override));
65 MOCK_METHOD(size_t, lseek, (off_t, stdplus::fd::Whence), (override));
66 MOCK_METHOD(void, truncate, (off_t), (override));
67 MOCK_METHOD(void, bind, (std::span<const std::byte>), (override));
68 MOCK_METHOD(void, connect, (std::span<const std::byte>), (override));
69 MOCK_METHOD(void, listen, (int), (override));
70 MOCK_METHOD((std::optional<std::tuple<int, std::span<std::byte>>>), accept,
71 (std::span<std::byte> sockaddr), (override));
72 MOCK_METHOD(void, setsockopt,
73 (stdplus::fd::SockLevel, stdplus::fd::SockOpt,
74 std::span<const std::byte>),
75 (override));
76 MOCK_METHOD(void, fcntlSetfd, (stdplus::fd::FdFlags), (override));
77 MOCK_METHOD(stdplus::fd::FdFlags, fcntlGetfd, (), (const, override));
78 MOCK_METHOD(void, fcntlSetfl, (stdplus::fd::FileFlags), (override));
79 MOCK_METHOD(stdplus::fd::FileFlags, fcntlGetfl, (), (const override));
80 MOCK_METHOD(std::span<std::byte>, mmap,
81 (std::byte*, std::size_t, stdplus::fd::ProtFlags,
82 stdplus::fd::MMapFlags, off_t),
83 (override));
84 MOCK_METHOD(void, munmap, (std::span<std::byte>), (override));
85 };
86
87 class EStoragedTest : public testing::Test
88 {
89 public:
90 const char* testFileName = "testfile";
91 const char* testLuksDevName = "testfile_luksDev";
92 const char* testCryptDir = "/tmp";
93 const std::string testConfigPath =
94 "/xyz/openbmc_project/inventory/system/board/test_board/test_emmc";
95 const uint64_t testSize = 24;
96 const uint8_t testLifeTime = 25;
97 const std::string testPartNumber = "12345678";
98 const std::string testSerialNumber = "ABCDEF1234";
99 const std::string testLocationCode = "U102020";
100 const std::string testDriveType = "SSD";
101 const std::string testDriveProtocol = "eMMC";
102 std::ofstream testFile;
103 std::string passwordString;
104 std::vector<uint8_t> password;
105 MockCryptsetupInterface* mockCryptIface{};
106 static const constexpr std::array<const char*, 2> options = {
107 "-E", "discard"};
108 MockFilesystemInterface* mockFsIface{};
109 boost::asio::io_context io;
110 std::shared_ptr<sdbusplus::asio::connection> conn;
111 std::unique_ptr<sdbusplus::asio::object_server> objectServer;
112 std::unique_ptr<estoraged::EStoraged> esObject;
113
EStoragedTest()114 EStoragedTest() :
115 passwordString("password"),
116 password(passwordString.begin(), passwordString.end())
117 {}
118
SetUp()119 void SetUp() override
120 {
121 /* Create an empty file that we'll pretend is a 'storage device'. */
122 testFile.open(testFileName,
123 std::ios::out | std::ios::binary | std::ios::trunc);
124 testFile.close();
125 if (testFile.fail())
126 {
127 throw std::runtime_error("Failed to open test file");
128 }
129
130 std::unique_ptr<MockCryptsetupInterface> cryptIface =
131 std::make_unique<MockCryptsetupInterface>();
132 mockCryptIface = cryptIface.get();
133 std::unique_ptr<MockFilesystemInterface> fsIface =
134 std::make_unique<MockFilesystemInterface>();
135 mockFsIface = fsIface.get();
136
137 /* Set up location of dummy mapped crypt file. */
138 EXPECT_CALL(*cryptIface, cryptGetDir).WillOnce(Return(testCryptDir));
139
140 conn = std::make_shared<sdbusplus::asio::connection>(io);
141 // request D-Bus server name.
142 conn->request_name("xyz.openbmc_project.eStoraged.test");
143 objectServer = std::make_unique<sdbusplus::asio::object_server>(conn);
144
145 std::unique_ptr<MockFd> mockFd = std::make_unique<MockFd>();
146 esObject = std::make_unique<estoraged::EStoraged>(
147 std::move(mockFd), *objectServer, testConfigPath, testFileName,
148 testLuksDevName, testSize, testLifeTime, testPartNumber,
149 testSerialNumber, testLocationCode, ERASE_MAX_GEOMETRY,
150 ERASE_MIN_GEOMETRY, testDriveType, testDriveProtocol,
151 std::move(cryptIface), std::move(fsIface));
152 }
153
TearDown()154 void TearDown() override
155 {
156 EXPECT_EQ(0, unlink(testFileName));
157 }
158 };
159
160 const char* mappedDevicePath = "/tmp/testfile_luksDev";
161 std::ofstream mappedDevice;
162
createMappedDev()163 int createMappedDev()
164 {
165 mappedDevice.open(mappedDevicePath,
166 std::ios::out | std::ios::binary | std::ios::trunc);
167 mappedDevice.close();
168 if (mappedDevice.fail())
169 {
170 throw std::runtime_error("Failed to open test mapped device");
171 }
172
173 return 0;
174 }
175
removeMappedDev()176 int removeMappedDev()
177 {
178 EXPECT_EQ(0, unlink(mappedDevicePath));
179
180 return 0;
181 }
182
183 /* Test case to format and then lock the LUKS device. */
TEST_F(EStoragedTest,FormatPass)184 TEST_F(EStoragedTest, FormatPass)
185 {
186 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
187
188 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
189 .Times(1);
190
191 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
192
193 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
194 .WillOnce(&createMappedDev);
195
196 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()),
197 ElementsAreArray(EStoragedTest::options)))
198 .WillOnce(Return(0));
199
200 EXPECT_CALL(*mockFsIface, runFsck(StrEq(esObject->getCryptDevicePath()),
201 StrEq("-t ext4 -p")))
202 .WillOnce(Return(0));
203
204 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint())))
205 .WillOnce(Return(false));
206
207 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
208 .WillOnce(Return(true));
209
210 EXPECT_CALL(*mockFsIface,
211 doMount(StrEq(esObject->getCryptDevicePath()),
212 StrEq(esObject->getMountPoint()), _, _, _))
213 .WillOnce(Return(0));
214
215 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
216 .WillOnce(Return(0));
217
218 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
219 .WillOnce(Return(true));
220
221 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _))
222 .WillOnce(&removeMappedDev);
223
224 /* Format the encrypted device. */
225 esObject->formatLuks(password, Volume::FilesystemType::ext4);
226 EXPECT_FALSE(esObject->isLocked());
227
228 esObject->lock();
229 EXPECT_TRUE(esObject->isLocked());
230 }
231
232 /*
233 * Test case where the mount point directory already exists, so it shouldn't
234 * try to create it.
235 */
TEST_F(EStoragedTest,MountPointExistsPass)236 TEST_F(EStoragedTest, MountPointExistsPass)
237 {
238 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
239
240 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
241 .Times(1);
242
243 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
244
245 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
246 .WillOnce(&createMappedDev);
247
248 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()),
249 ElementsAreArray(EStoragedTest::options)))
250 .WillOnce(Return(0));
251
252 EXPECT_CALL(*mockFsIface, runFsck(StrEq(esObject->getCryptDevicePath()),
253 StrEq("-t ext4 -p")))
254 .WillOnce(Return(0));
255
256 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint())))
257 .WillOnce(Return(true));
258
259 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
260 .Times(0);
261
262 EXPECT_CALL(*mockFsIface,
263 doMount(StrEq(esObject->getCryptDevicePath()),
264 StrEq(esObject->getMountPoint()), _, _, _))
265 .WillOnce(Return(0));
266
267 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
268 .WillOnce(Return(0));
269
270 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
271 .WillOnce(Return(true));
272
273 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _))
274 .WillOnce(&removeMappedDev);
275
276 /* Format the encrypted device. */
277 esObject->formatLuks(password, Volume::FilesystemType::ext4);
278 EXPECT_FALSE(esObject->isLocked());
279
280 esObject->lock();
281 EXPECT_TRUE(esObject->isLocked());
282 }
283
284 /* Test case where the device/file doesn't exist. */
TEST_F(EStoragedTest,FormatNoDeviceFail)285 TEST_F(EStoragedTest, FormatNoDeviceFail)
286 {
287 /* Delete the test file. */
288 EXPECT_EQ(0, unlink(testFileName));
289
290 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
291 ResourceNotFound);
292 EXPECT_TRUE(esObject->isLocked());
293
294 /* Create the test file again, so that the TearDown function works. */
295 testFile.open(testFileName,
296 std::ios::out | std::ios::binary | std::ios::trunc);
297 testFile.close();
298 }
299
300 /* Test case where we fail to format the LUKS device. */
TEST_F(EStoragedTest,FormatFail)301 TEST_F(EStoragedTest, FormatFail)
302 {
303 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _))
304 .WillOnce(Return(-1));
305
306 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
307 InternalFailure);
308 EXPECT_TRUE(esObject->isLocked());
309 }
310
311 /* Test case where we fail to set the password for the LUKS device. */
TEST_F(EStoragedTest,AddKeyslotFail)312 TEST_F(EStoragedTest, AddKeyslotFail)
313 {
314 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
315
316 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
317 .WillOnce(Return(-1));
318
319 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
320 InternalFailure);
321 EXPECT_TRUE(esObject->isLocked());
322 }
323
324 /* Test case where we fail to load the LUKS header. */
TEST_F(EStoragedTest,LoadLuksHeaderFail)325 TEST_F(EStoragedTest, LoadLuksHeaderFail)
326 {
327 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
328
329 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
330 .Times(1);
331
332 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).WillOnce(Return(-1));
333
334 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
335 InternalFailure);
336 EXPECT_TRUE(esObject->isLocked());
337 }
338
339 /* Test case where we fail to activate the LUKS device. */
TEST_F(EStoragedTest,ActivateFail)340 TEST_F(EStoragedTest, ActivateFail)
341 {
342 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
343
344 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
345 .Times(1);
346
347 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
348
349 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
350 .WillOnce(Return(-1));
351
352 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
353 InternalFailure);
354 EXPECT_TRUE(esObject->isLocked());
355 }
356
357 /* Test case where we fail to create the filesystem. */
TEST_F(EStoragedTest,CreateFilesystemFail)358 TEST_F(EStoragedTest, CreateFilesystemFail)
359 {
360 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
361
362 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
363 .Times(1);
364
365 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
366
367 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
368 .WillOnce(&createMappedDev);
369
370 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()),
371 ElementsAreArray(EStoragedTest::options)))
372 .WillOnce(Return(-1));
373
374 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
375 InternalFailure);
376 EXPECT_FALSE(esObject->isLocked());
377
378 EXPECT_EQ(0, removeMappedDev());
379 }
380
381 /* Test case where we fail to create the mount point. */
TEST_F(EStoragedTest,CreateMountPointFail)382 TEST_F(EStoragedTest, CreateMountPointFail)
383 {
384 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
385
386 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
387 .Times(1);
388
389 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
390
391 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
392 .WillOnce(&createMappedDev);
393
394 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()),
395 ElementsAreArray(EStoragedTest::options)))
396 .WillOnce(Return(0));
397
398 EXPECT_CALL(*mockFsIface, runFsck(StrEq(esObject->getCryptDevicePath()),
399 StrEq("-t ext4 -p")))
400 .WillOnce(Return(0));
401
402 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint())))
403 .WillOnce(Return(false));
404
405 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
406 .WillOnce(Return(false));
407
408 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
409 InternalFailure);
410 EXPECT_FALSE(esObject->isLocked());
411
412 EXPECT_EQ(0, removeMappedDev());
413 }
414
415 /* Test case where we fail to mount the filesystem. */
TEST_F(EStoragedTest,MountFail)416 TEST_F(EStoragedTest, MountFail)
417 {
418 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
419
420 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
421 .Times(1);
422
423 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
424
425 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
426 .WillOnce(&createMappedDev);
427
428 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()),
429 ElementsAreArray(EStoragedTest::options)))
430 .WillOnce(Return(0));
431
432 EXPECT_CALL(*mockFsIface, runFsck(StrEq(esObject->getCryptDevicePath()),
433 StrEq("-t ext4 -p")))
434 .WillOnce(Return(0));
435
436 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint())))
437 .WillOnce(Return(false));
438
439 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
440 .WillOnce(Return(true));
441
442 EXPECT_CALL(*mockFsIface,
443 doMount(StrEq(esObject->getCryptDevicePath()),
444 StrEq(esObject->getMountPoint()), _, _, _))
445 .WillOnce(Return(-1));
446
447 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
448 .WillOnce(Return(true));
449
450 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
451 InternalFailure);
452 EXPECT_FALSE(esObject->isLocked());
453
454 EXPECT_EQ(0, removeMappedDev());
455 }
456
457 /* Test case where we fail to unmount the filesystem. */
TEST_F(EStoragedTest,UnmountFail)458 TEST_F(EStoragedTest, UnmountFail)
459 {
460 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
461
462 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
463 .Times(1);
464
465 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
466
467 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
468 .WillOnce(&createMappedDev);
469
470 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()),
471 ElementsAreArray(EStoragedTest::options)))
472 .WillOnce(Return(0));
473
474 EXPECT_CALL(*mockFsIface, runFsck(StrEq(esObject->getCryptDevicePath()),
475 StrEq("-t ext4 -p")))
476 .WillOnce(Return(0));
477
478 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint())))
479 .WillOnce(Return(false));
480
481 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
482 .WillOnce(Return(true));
483
484 EXPECT_CALL(*mockFsIface,
485 doMount(StrEq(esObject->getCryptDevicePath()),
486 StrEq(esObject->getMountPoint()), _, _, _))
487 .WillOnce(Return(0));
488
489 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
490 .WillOnce(Return(-1));
491
492 esObject->formatLuks(password, Volume::FilesystemType::ext4);
493 EXPECT_FALSE(esObject->isLocked());
494
495 EXPECT_THROW(esObject->lock(), InternalFailure);
496 EXPECT_FALSE(esObject->isLocked());
497
498 EXPECT_EQ(0, removeMappedDev());
499 }
500
501 /* Test case where we fail to remove the mount point. */
TEST_F(EStoragedTest,RemoveMountPointFail)502 TEST_F(EStoragedTest, RemoveMountPointFail)
503 {
504 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
505
506 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
507 .Times(1);
508
509 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
510
511 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
512 .WillOnce(&createMappedDev);
513
514 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()),
515 ElementsAreArray(EStoragedTest::options)))
516 .WillOnce(Return(0));
517
518 EXPECT_CALL(*mockFsIface, runFsck(StrEq(esObject->getCryptDevicePath()),
519 StrEq("-t ext4 -p")))
520 .WillOnce(Return(0));
521
522 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint())))
523 .WillOnce(Return(false));
524
525 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
526 .WillOnce(Return(true));
527
528 EXPECT_CALL(*mockFsIface,
529 doMount(StrEq(esObject->getCryptDevicePath()),
530 StrEq(esObject->getMountPoint()), _, _, _))
531 .WillOnce(Return(0));
532
533 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
534 .WillOnce(Return(0));
535
536 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
537 .WillOnce(Return(false));
538
539 esObject->formatLuks(password, Volume::FilesystemType::ext4);
540 EXPECT_FALSE(esObject->isLocked());
541
542 /* This will fail to remove the mount point. */
543 EXPECT_THROW(esObject->lock(), InternalFailure);
544 EXPECT_FALSE(esObject->isLocked());
545
546 EXPECT_EQ(0, removeMappedDev());
547 }
548
549 /* Test case where we fail to deactivate the LUKS device. */
TEST_F(EStoragedTest,DeactivateFail)550 TEST_F(EStoragedTest, DeactivateFail)
551 {
552 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
553
554 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
555 .Times(1);
556
557 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
558
559 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
560 .WillOnce(&createMappedDev);
561
562 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()),
563 ElementsAreArray(EStoragedTest::options)))
564 .WillOnce(Return(0));
565
566 EXPECT_CALL(*mockFsIface, runFsck(StrEq(esObject->getCryptDevicePath()),
567 StrEq("-t ext4 -p")))
568 .WillOnce(Return(0));
569
570 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint())))
571 .WillOnce(Return(false));
572
573 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
574 .WillOnce(Return(true));
575
576 EXPECT_CALL(*mockFsIface,
577 doMount(StrEq(esObject->getCryptDevicePath()),
578 StrEq(esObject->getMountPoint()), _, _, _))
579 .WillOnce(Return(0));
580
581 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
582 .WillOnce(Return(0));
583
584 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
585 .WillOnce(Return(true));
586
587 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).WillOnce(Return(-1));
588
589 /* Format the encrypted device. */
590 esObject->formatLuks(password, Volume::FilesystemType::ext4);
591 EXPECT_FALSE(esObject->isLocked());
592
593 EXPECT_THROW(esObject->lock(), InternalFailure);
594 EXPECT_FALSE(esObject->isLocked());
595
596 EXPECT_EQ(0, removeMappedDev());
597 }
598
599 /* Test case where we successfully change the password. */
TEST_F(EStoragedTest,ChangePasswordSuccess)600 TEST_F(EStoragedTest, ChangePasswordSuccess)
601 {
602 std::string newPasswordString("newPassword");
603 std::vector<uint8_t> newPassword(newPasswordString.begin(),
604 newPasswordString.end());
605
606 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
607
608 EXPECT_CALL(*mockCryptIface,
609 cryptKeyslotChangeByPassphrase(
610 _, _, _, reinterpret_cast<const char*>(password.data()),
611 password.size(),
612 reinterpret_cast<const char*>(newPassword.data()),
613 newPassword.size()))
614 .WillOnce(Return(0));
615
616 /* Change the password for the LUKS-encrypted device. */
617 esObject->changePassword(password, newPassword);
618 }
619
620 /* Test case where we fail to change the password. */
TEST_F(EStoragedTest,ChangePasswordFail)621 TEST_F(EStoragedTest, ChangePasswordFail)
622 {
623 std::string newPasswordString("newPassword");
624 std::vector<uint8_t> newPassword(newPasswordString.begin(),
625 newPasswordString.end());
626
627 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
628
629 EXPECT_CALL(*mockCryptIface,
630 cryptKeyslotChangeByPassphrase(
631 _, _, _, reinterpret_cast<const char*>(password.data()),
632 password.size(),
633 reinterpret_cast<const char*>(newPassword.data()),
634 newPassword.size()))
635 .WillOnce(Return(-1));
636
637 EXPECT_THROW(esObject->changePassword(password, newPassword),
638 InternalFailure);
639 }
640
TEST(EMMCBackgroundOperation,IoCtlFailure)641 TEST(EMMCBackgroundOperation, IoCtlFailure)
642 {
643 std::unique_ptr<MockFd> mockFd = std::make_unique<MockFd>();
644
645 EXPECT_CALL(*mockFd, ioctl(MMC_IOC_CMD, testing::_)).WillOnce(Return(1));
646 EXPECT_THROW(estoraged::EStoraged::enableBackgroundOperation(
647 std::move(mockFd), "/dev/test"),
648 estoraged::BkopsIoctlFailure);
649 }
650
TEST(EMMCBackgroundOperation,BkOpsNotSupported)651 TEST(EMMCBackgroundOperation, BkOpsNotSupported)
652 {
653 std::unique_ptr<MockFd> mockFd = std::make_unique<MockFd>();
654
655 EXPECT_CALL(*mockFd, ioctl(MMC_IOC_CMD, testing::_)).WillOnce(Return(0));
656 EXPECT_FALSE(estoraged::EStoraged::enableBackgroundOperation(
657 std::move(mockFd), "/dev/test"));
658 }
659
TEST(EMMCBackgroundOperation,EnableFailure)660 TEST(EMMCBackgroundOperation, EnableFailure)
661 {
662 std::unique_ptr<MockFd> mockFd = std::make_unique<MockFd>();
663
664 EXPECT_CALL(*mockFd, ioctl(MMC_IOC_CMD, testing::_))
665 .WillOnce(testing::Invoke([](unsigned long, void* data) {
666 struct mmc_ioc_cmd* idata = static_cast<struct mmc_ioc_cmd*>(data);
667 EXPECT_EQ(idata->opcode, MMC_SEND_EXT_CSD);
668 EXPECT_EQ(idata->blksz, 512);
669 EXPECT_EQ(idata->flags, MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC);
670
671 // Set specific bytes to simulate a hardware state
672 // NOLINTNEXTLINE(performance-no-int-to-ptr)
673 *reinterpret_cast<uint8_t*>(idata->data_ptr + 502) =
674 0x01; // BKOPS_SUPPORT = Supported
675 // NOLINTNEXTLINE(performance-no-int-to-ptr)
676 *reinterpret_cast<uint8_t*>(idata->data_ptr + 163) =
677 0x00; // BKOPS_EN = Disabled
678 return 0; // Success
679 }))
680 .WillOnce(Return(1));
681 EXPECT_THROW(estoraged::EStoraged::enableBackgroundOperation(
682 std::move(mockFd), "/dev/test"),
683 estoraged::BkopsEnableFailure);
684 }
685
TEST(EMMCBackgroundOperation,AlreadyEnabled)686 TEST(EMMCBackgroundOperation, AlreadyEnabled)
687 {
688 std::unique_ptr<MockFd> mockFd = std::make_unique<MockFd>();
689
690 EXPECT_CALL(*mockFd, ioctl(MMC_IOC_CMD, testing::_))
691 .WillOnce(testing::Invoke([](unsigned long, void* data) {
692 struct mmc_ioc_cmd* idata = static_cast<struct mmc_ioc_cmd*>(data);
693 EXPECT_EQ(idata->opcode, MMC_SEND_EXT_CSD);
694 EXPECT_EQ(idata->blksz, 512);
695 EXPECT_EQ(idata->flags, MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC);
696
697 // Set specific bytes to simulate a hardware state
698 // NOLINTNEXTLINE(performance-no-int-to-ptr)
699 *reinterpret_cast<uint8_t*>(idata->data_ptr + 502) =
700 0x01; // BKOPS_SUPPORT = Supported
701 // NOLINTNEXTLINE(performance-no-int-to-ptr)
702 *reinterpret_cast<uint8_t*>(idata->data_ptr + 163) =
703 EXT_CSD_MANUAL_BKOPS_MASK; // BKOPS_EN = Manual Mode
704 return 0; // Success
705 }));
706 EXPECT_FALSE(estoraged::EStoraged::enableBackgroundOperation(
707 std::move(mockFd), "/dev/test"));
708 }
709
TEST(EMMCBackgroundOperation,EnableSuccess)710 TEST(EMMCBackgroundOperation, EnableSuccess)
711 {
712 std::unique_ptr<MockFd> mockFd = std::make_unique<MockFd>();
713
714 EXPECT_CALL(*mockFd, ioctl(MMC_IOC_CMD, testing::_))
715 .WillOnce(testing::Invoke([](unsigned long, void* data) {
716 struct mmc_ioc_cmd* idata = static_cast<struct mmc_ioc_cmd*>(data);
717 EXPECT_EQ(idata->opcode, MMC_SEND_EXT_CSD);
718 EXPECT_EQ(idata->blksz, 512);
719 EXPECT_EQ(idata->flags, MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC);
720
721 // Set specific bytes to simulate a hardware state
722 // NOLINTNEXTLINE(performance-no-int-to-ptr)
723 *reinterpret_cast<uint8_t*>(idata->data_ptr + 502) =
724 0x01; // BKOPS_SUPPORT = Supported
725 // NOLINTNEXTLINE(performance-no-int-to-ptr)
726 *reinterpret_cast<uint8_t*>(idata->data_ptr + 163) =
727 0x00; // BKOPS_EN = Disabled
728 return 0; // Success
729 }))
730 .WillOnce(testing::Invoke([](unsigned long, void* data) {
731 struct mmc_ioc_cmd* idata = static_cast<struct mmc_ioc_cmd*>(data);
732 EXPECT_EQ(idata->opcode, MMC_SWITCH);
733 EXPECT_EQ(idata->arg, (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
734 (EXT_CSD_BKOPS_EN << 16) |
735 (EXT_CSD_MANUAL_BKOPS_MASK << 8) |
736 EXT_CSD_CMD_SET_NORMAL);
737 EXPECT_EQ(idata->flags, MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC);
738 return 0; // Success
739 }));
740 EXPECT_TRUE(estoraged::EStoraged::enableBackgroundOperation(
741 std::move(mockFd), "/dev/test"));
742 }
743
744 } // namespace estoraged_test
745