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