zircon/system/ulib/fs/journal/test/journal_test.cc
// Tests that when info block operations fail, subsequent operations also fail to avoid
// leaving the device in an inconsistent state.
//
// - Write Metadata (OK, but causes a delayed info block writeback)
// - Sync (cause info block writeback to happen, where it fails)
// - Write Metadata (fails, because info block writeback failed earlier)
Tests that when info block operations fail, subsequent operations also fail to avoid
leaving the device in an inconsistent state.

Write Metadata (OK, but causes a delayed info block writeback)
Sync (cause info block writeback to happen, where it fails)
Write Metadata (fails, because info block writeback failed earlier)
Tests that when info block operations fail subsequent operations also fail to avoid leaving the device in an inconsistent state . Write Metadata ( OK but causes a delayed info block writeback ) Sync ( cause info block writeback to happen where it fails ) Write Metadata ( fails because info block writeback failed earlier )
TEST_F(JournalTest, InfoBlockWriteFailureFailsSubsequentRequests) {
  storage::VmoBuffer metadata = registry()->InitializeBuffer(3);
  const std::vector<storage::UnbufferedOperation> operations = {
      {
          zx::unowned_vmo(metadata.vmo().get()),
          {
              storage::OperationType::kWrite,
              .vmo_offset = 0,
              .dev_offset = 20,
              .length = 1,
          },
      },
      {
          zx::unowned_vmo(metadata.vmo().get()),
          {
              storage::OperationType::kWrite,
              .vmo_offset = 0,
              .dev_offset = 200,
              .length = 1,
          },
      },
  };
  constexpr uint64_t kJournalStartBlock = 55;
  JournalRequestVerifier verifier(registry()->info(), registry()->journal(),
                                  registry()->writeback(), kJournalStartBlock);
  MockTransactionHandler::TransactionCallback callbacks[] = {
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyJournalWrite(operations[0], requests);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        verifier.VerifyMetadataWrite(operations[0], requests);
        verifier.ExtendJournalOffset(operations[0].op.length + kEntryMetadataBlocks);
        return ZX_OK;
      },
      [&](const std::vector<storage::BufferedOperation>& requests) {
        uint64_t sequence_number = 1;
        verifier.VerifyInfoBlockWrite(sequence_number, requests);
        return ZX_ERR_IO;
      }};
  std::atomic<bool> write_ok = false;
  std::atomic<bool> sync_failed = false;
  std::atomic<bool> second_write_failed = false;
  MockTransactionHandler handler(registry(), callbacks, std::size(callbacks));
  {
    Journal journal(&handler, take_info(), take_journal_buffer(), take_data_buffer(),
                    kJournalStartBlock, Journal::Options());
    auto metadata_promise =
        journal.WriteMetadata({operations[0]}).then([&](fit::result<void, zx_status_t>& result) {
          EXPECT_TRUE(result.is_ok());
          write_ok = true;
        });
    auto sync_promise = journal.Sync().then([&](fit::result<void, zx_status_t>& result) {
      EXPECT_STATUS(ZX_ERR_IO, result.error());
      sync_failed = true;
    });
    auto failed_promise =
        journal.WriteMetadata({operations[1]}).then([&](fit::result<void, zx_status_t>& result) {
          EXPECT_STATUS(ZX_ERR_IO_REFUSED, result.error());
          second_write_failed = true;
        });
    journal.schedule_task(
        metadata_promise.and_then(std::move(sync_promise)).and_then(std::move(failed_promise)));
  }
  EXPECT_TRUE(write_ok);
  EXPECT_TRUE(sync_failed);
  EXPECT_TRUE(second_write_failed);
}
TEST_F ( JournalTest , InfoBlockWriteFailureFailsSubsequentRequests ) { storage :: VmoBuffer metadata = registry ( ) -> InitializeBuffer ( 3 ) ; const std :: vector < storage :: UnbufferedOperation > operations = { { zx :: unowned_vmo ( metadata . vmo ( ) . get ( ) ) , { storage :: OperationType :: kWrite , . vmo_offset = 0 , . dev_offset = 20 , . length = 1 , } , } , { zx :: unowned_vmo ( metadata . vmo ( ) . get ( ) ) , { storage :: OperationType :: kWrite , . vmo_offset = 0 , . dev_offset = 200 , . length = 1 , } , } , } ; constexpr uint64_t kJournalStartBlock = 55 ; JournalRequestVerifier verifier ( registry ( ) -> info ( ) , registry ( ) -> journal ( ) , registry ( ) -> writeback ( ) , kJournalStartBlock ) ; MockTransactionHandler :: TransactionCallback callbacks [ ] = { [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { verifier . VerifyJournalWrite ( operations [ 0 ] , requests ) ; return ZX_OK ; } , [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { verifier . VerifyMetadataWrite ( operations [ 0 ] , requests ) ; verifier . ExtendJournalOffset ( operations [ 0 ] . op . length + kEntryMetadataBlocks ) ; return ZX_OK ; } , [ & ] ( const std :: vector < storage :: BufferedOperation > & requests ) { uint64_t sequence_number = 1 ; verifier . VerifyInfoBlockWrite ( sequence_number , requests ) ; return ZX_ERR_IO ; } } ; std :: atomic < bool > write_ok = false ; std :: atomic < bool > sync_failed = false ; std :: atomic < bool > second_write_failed = false ; MockTransactionHandler handler ( registry ( ) , callbacks , std :: size ( callbacks ) ) ; { Journal journal ( & handler , take_info ( ) , take_journal_buffer ( ) , take_data_buffer ( ) , kJournalStartBlock , Journal :: Options ( ) ) ; auto metadata_promise = journal . WriteMetadata ( { operations [ 0 ] } ) . then ( [ & ] ( fit :: result < void , zx_status_t > & result ) { EXPECT_TRUE ( result . is_ok ( ) ) ; write_ok = true ; } ) ; auto sync_promise = journal . Sync ( ) . then ( [ & ] ( fit :: result < void , zx_status_t > & result ) { EXPECT_STATUS ( ZX_ERR_IO , result . error ( ) ) ; sync_failed = true ; } ) ; auto failed_promise = journal . WriteMetadata ( { operations [ 1 ] } ) . then ( [ & ] ( fit :: result < void , zx_status_t > & result ) { EXPECT_STATUS ( ZX_ERR_IO_REFUSED , result . error ( ) ) ; second_write_failed = true ; } ) ; journal . schedule_task ( metadata_promise . and_then ( std :: move ( sync_promise ) ) . and_then ( std :: move ( failed_promise ) ) ) ; } EXPECT_TRUE ( write_ok ) ; EXPECT_TRUE ( sync_failed ) ; EXPECT_TRUE ( second_write_failed ) ; }
Tests that when info block operations fail, subsequent operations also fail to avoid
leaving the device in an inconsistent state.
Tests that when info block operations fail subsequent operations also fail to avoid leaving the device in an inconsistent state .
// At this point, the metadata operation will succeed. // This will fail the sync, but not the write request. // The metadata operation completed successfully. // Failure triggered by the info block writeback. // Failure triggered by the journal itself.