1 #include "estoraged_test.hpp" 2 3 #include "estoraged.hpp" 4 5 #include <unistd.h> 6 7 #include <boost/asio/io_context.hpp> 8 #include <sdbusplus/asio/connection.hpp> 9 #include <sdbusplus/asio/object_server.hpp> 10 #include <xyz/openbmc_project/Common/error.hpp> 11 #include <xyz/openbmc_project/Inventory/Item/Volume/client.hpp> 12 13 #include <exception> 14 #include <filesystem> 15 #include <fstream> 16 #include <iterator> 17 #include <memory> 18 #include <string> 19 #include <vector> 20 21 #include <gmock/gmock.h> 22 #include <gtest/gtest.h> 23 24 namespace estoraged_test 25 { 26 27 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 28 using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound; 29 using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Volume; 30 using std::filesystem::path; 31 using ::testing::_; 32 using ::testing::ContainsRegex; 33 using ::testing::Return; 34 using ::testing::StrEq; 35 36 class EStoragedTest : public testing::Test 37 { 38 public: 39 const char* testFileName = "testfile"; 40 const char* testLuksDevName = "testfile_luksDev"; 41 const uint64_t testSize = 24; 42 std::ofstream testFile; 43 const char* testPath = "/test/openbmc_project/storage/test_dev"; 44 const char* estoragedInterface = 45 "xyz.openbmc_project.Inventory.Item.Volume"; 46 const char* driveInterface = "xyz.openbmc_project.Inventory.Item.Drive"; 47 std::string passwordString; 48 std::vector<uint8_t> password; 49 MockCryptsetupInterface* mockCryptIface{}; 50 MockFilesystemInterface* mockFsIface{}; 51 boost::asio::io_context io; 52 std::shared_ptr<sdbusplus::asio::connection> conn; 53 std::unique_ptr<sdbusplus::asio::object_server> objectServer; 54 std::unique_ptr<estoraged::EStoraged> esObject; 55 56 EStoragedTest() : 57 passwordString("password"), 58 password(passwordString.begin(), passwordString.end()) 59 {} 60 61 void SetUp() override 62 { 63 /* Create an empty file that we'll pretend is a 'storage device'. */ 64 testFile.open(testFileName, 65 std::ios::out | std::ios::binary | std::ios::trunc); 66 testFile.close(); 67 if (testFile.fail()) 68 { 69 throw std::runtime_error("Failed to open test file"); 70 } 71 72 std::unique_ptr<MockCryptsetupInterface> cryptIface = 73 std::make_unique<MockCryptsetupInterface>(); 74 mockCryptIface = cryptIface.get(); 75 std::unique_ptr<MockFilesystemInterface> fsIface = 76 std::make_unique<MockFilesystemInterface>(); 77 mockFsIface = fsIface.get(); 78 79 conn = std::make_shared<sdbusplus::asio::connection>(io); 80 // request D-Bus server name. 81 conn->request_name("xyz.openbmc_project.eStoraged.test"); 82 objectServer = std::make_unique<sdbusplus::asio::object_server>(conn); 83 84 esObject = std::make_unique<estoraged::EStoraged>( 85 *objectServer, testFileName, testLuksDevName, testSize, 86 std::move(cryptIface), std::move(fsIface)); 87 } 88 89 void TearDown() override 90 { 91 EXPECT_EQ(0, unlink(testFileName)); 92 } 93 }; 94 95 /* Test case to format and then lock the LUKS device. */ 96 TEST_F(EStoragedTest, FormatPass) 97 { 98 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 99 100 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 101 .Times(1); 102 103 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 104 105 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 106 .Times(1); 107 108 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 109 110 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 111 .WillOnce(Return(false)); 112 113 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 114 .WillOnce(Return(true)); 115 116 EXPECT_CALL(*mockFsIface, 117 doMount(ContainsRegex("/dev/mapper/"), 118 StrEq(esObject->getMountPoint()), _, _, _)) 119 .WillOnce(Return(0)); 120 121 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 122 .WillOnce(Return(0)); 123 124 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 125 .WillOnce(Return(true)); 126 127 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).Times(1); 128 129 /* Format the encrypted device. */ 130 esObject->formatLuks(password, Volume::FilesystemType::ext4); 131 EXPECT_FALSE(esObject->isLocked()); 132 133 esObject->lock(); 134 EXPECT_TRUE(esObject->isLocked()); 135 } 136 137 /* 138 * Test case where the mount point directory already exists, so it shouldn't 139 * try to create it. 140 */ 141 TEST_F(EStoragedTest, MountPointExistsPass) 142 { 143 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 144 145 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 146 .Times(1); 147 148 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 149 150 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 151 .Times(1); 152 153 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 154 155 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 156 .WillOnce(Return(true)); 157 158 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 159 .Times(0); 160 161 EXPECT_CALL(*mockFsIface, 162 doMount(ContainsRegex("/dev/mapper/"), 163 StrEq(esObject->getMountPoint()), _, _, _)) 164 .WillOnce(Return(0)); 165 166 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 167 .WillOnce(Return(0)); 168 169 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 170 .WillOnce(Return(true)); 171 172 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).Times(1); 173 174 /* Format the encrypted device. */ 175 esObject->formatLuks(password, Volume::FilesystemType::ext4); 176 EXPECT_FALSE(esObject->isLocked()); 177 178 esObject->lock(); 179 EXPECT_TRUE(esObject->isLocked()); 180 } 181 182 /* Test case where the device/file doesn't exist. */ 183 TEST_F(EStoragedTest, FormatNoDeviceFail) 184 { 185 /* Delete the test file. */ 186 EXPECT_EQ(0, unlink(testFileName)); 187 188 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 189 ResourceNotFound); 190 EXPECT_FALSE(esObject->isLocked()); 191 192 /* Create the test file again, so that the TearDown function works. */ 193 testFile.open(testFileName, 194 std::ios::out | std::ios::binary | std::ios::trunc); 195 testFile.close(); 196 } 197 198 /* Test case where we fail to format the LUKS device. */ 199 TEST_F(EStoragedTest, FormatFail) 200 { 201 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)) 202 .WillOnce(Return(-1)); 203 204 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 205 InternalFailure); 206 EXPECT_FALSE(esObject->isLocked()); 207 } 208 209 /* Test case where we fail to set the password for the LUKS device. */ 210 TEST_F(EStoragedTest, AddKeyslotFail) 211 { 212 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 213 214 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 215 .WillOnce(Return(-1)); 216 217 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 218 InternalFailure); 219 EXPECT_TRUE(esObject->isLocked()); 220 } 221 222 /* Test case where we fail to load the LUKS header. */ 223 TEST_F(EStoragedTest, LoadLuksHeaderFail) 224 { 225 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 226 227 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 228 .Times(1); 229 230 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).WillOnce(Return(-1)); 231 232 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 233 InternalFailure); 234 EXPECT_TRUE(esObject->isLocked()); 235 } 236 237 /* Test case where we fail to activate the LUKS device. */ 238 TEST_F(EStoragedTest, ActivateFail) 239 { 240 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 241 242 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 243 .Times(1); 244 245 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 246 247 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 248 .WillOnce(Return(-1)); 249 250 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 251 InternalFailure); 252 EXPECT_TRUE(esObject->isLocked()); 253 } 254 255 /* Test case where we fail to create the filesystem. */ 256 TEST_F(EStoragedTest, CreateFilesystemFail) 257 { 258 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 259 260 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 261 .Times(1); 262 263 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 264 265 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 266 .Times(1); 267 268 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(-1)); 269 270 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 271 InternalFailure); 272 EXPECT_FALSE(esObject->isLocked()); 273 } 274 275 /* Test case where we fail to create the mount point. */ 276 TEST_F(EStoragedTest, CreateMountPointFail) 277 { 278 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 279 280 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 281 .Times(1); 282 283 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 284 285 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 286 .Times(1); 287 288 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 289 290 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 291 .WillOnce(Return(false)); 292 293 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 294 .WillOnce(Return(false)); 295 296 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 297 InternalFailure); 298 EXPECT_FALSE(esObject->isLocked()); 299 } 300 301 /* Test case where we fail to mount the filesystem. */ 302 TEST_F(EStoragedTest, MountFail) 303 { 304 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 305 306 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 307 .Times(1); 308 309 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 310 311 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 312 .Times(1); 313 314 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 315 316 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 317 .WillOnce(Return(false)); 318 319 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 320 .WillOnce(Return(true)); 321 322 EXPECT_CALL(*mockFsIface, 323 doMount(ContainsRegex("/dev/mapper/"), 324 StrEq(esObject->getMountPoint()), _, _, _)) 325 .WillOnce(Return(-1)); 326 327 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 328 .WillOnce(Return(true)); 329 330 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 331 InternalFailure); 332 EXPECT_FALSE(esObject->isLocked()); 333 } 334 335 /* Test case where we fail to unmount the filesystem. */ 336 TEST_F(EStoragedTest, UnmountFail) 337 { 338 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 339 340 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 341 .Times(1); 342 343 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 344 345 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 346 .Times(1); 347 348 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 349 350 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 351 .WillOnce(Return(false)); 352 353 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 354 .WillOnce(Return(true)); 355 356 EXPECT_CALL(*mockFsIface, 357 doMount(ContainsRegex("/dev/mapper/"), 358 StrEq(esObject->getMountPoint()), _, _, _)) 359 .WillOnce(Return(0)); 360 361 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 362 .WillOnce(Return(-1)); 363 364 esObject->formatLuks(password, Volume::FilesystemType::ext4); 365 EXPECT_FALSE(esObject->isLocked()); 366 367 EXPECT_THROW(esObject->lock(), InternalFailure); 368 EXPECT_FALSE(esObject->isLocked()); 369 } 370 371 /* Test case where we fail to remove the mount point. */ 372 TEST_F(EStoragedTest, RemoveMountPointFail) 373 { 374 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 375 376 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 377 .Times(1); 378 379 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 380 381 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 382 .Times(1); 383 384 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 385 386 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 387 .WillOnce(Return(false)); 388 389 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 390 .WillOnce(Return(true)); 391 392 EXPECT_CALL(*mockFsIface, 393 doMount(ContainsRegex("/dev/mapper/"), 394 StrEq(esObject->getMountPoint()), _, _, _)) 395 .WillOnce(Return(0)); 396 397 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 398 .WillOnce(Return(0)); 399 400 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 401 .WillOnce(Return(false)); 402 403 esObject->formatLuks(password, Volume::FilesystemType::ext4); 404 EXPECT_FALSE(esObject->isLocked()); 405 406 /* This will fail to remove the mount point. */ 407 EXPECT_THROW(esObject->lock(), InternalFailure); 408 EXPECT_FALSE(esObject->isLocked()); 409 } 410 411 /* Test case where we fail to deactivate the LUKS device. */ 412 TEST_F(EStoragedTest, DeactivateFail) 413 { 414 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 415 416 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 417 .Times(1); 418 419 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 420 421 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 422 .Times(1); 423 424 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 425 426 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 427 .WillOnce(Return(false)); 428 429 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 430 .WillOnce(Return(true)); 431 432 EXPECT_CALL(*mockFsIface, 433 doMount(ContainsRegex("/dev/mapper/"), 434 StrEq(esObject->getMountPoint()), _, _, _)) 435 .WillOnce(Return(0)); 436 437 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 438 .WillOnce(Return(0)); 439 440 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 441 .WillOnce(Return(true)); 442 443 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).WillOnce(Return(-1)); 444 445 /* Format the encrypted device. */ 446 esObject->formatLuks(password, Volume::FilesystemType::ext4); 447 EXPECT_FALSE(esObject->isLocked()); 448 449 EXPECT_THROW(esObject->lock(), InternalFailure); 450 EXPECT_FALSE(esObject->isLocked()); 451 } 452 453 } // namespace estoraged_test 454