xref: /openbmc/estoraged/src/test/estoraged_test.cpp (revision da5aa614090fbd22399db85ec8866353c4c22d9c)
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