corruption_test.cc 16.6 KB
Newer Older
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
S
Siying Dong 已提交
2 3 4
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).
5
//
J
jorlow@chromium.org 已提交
6 7 8 9
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

10 11
#ifndef ROCKSDB_LITE

12
#include "rocksdb/db.h"
J
jorlow@chromium.org 已提交
13 14 15

#include <errno.h>
#include <fcntl.h>
16
#include <inttypes.h>
J
jorlow@chromium.org 已提交
17 18
#include <sys/stat.h>
#include <sys/types.h>
19 20 21
#include "db/db_impl.h"
#include "db/log_format.h"
#include "db/version_set.h"
22
#include "file/filename.h"
23
#include "rocksdb/cache.h"
A
Aaron G 已提交
24
#include "rocksdb/convenience.h"
25
#include "rocksdb/env.h"
S
Siying Dong 已提交
26
#include "rocksdb/table.h"
27
#include "rocksdb/write_batch.h"
28
#include "table/block_based/block_based_table_builder.h"
29
#include "table/meta_blocks.h"
30
#include "file/filename.h"
31
#include "util/string_util.h"
32 33
#include "test_util/testharness.h"
#include "test_util/testutil.h"
J
jorlow@chromium.org 已提交
34

35
namespace rocksdb {
J
jorlow@chromium.org 已提交
36 37 38

static const int kValueSize = 1000;

I
Igor Sugak 已提交
39
class CorruptionTest : public testing::Test {
J
jorlow@chromium.org 已提交
40 41 42
 public:
  test::ErrorEnv env_;
  std::string dbname_;
43
  std::shared_ptr<Cache> tiny_cache_;
J
jorlow@chromium.org 已提交
44 45 46
  Options options_;
  DB* db_;

47
  CorruptionTest() {
48 49 50
    // If LRU cache shard bit is smaller than 2 (or -1 which will automatically
    // set it to 0), test SequenceNumberRecovery will fail, likely because of a
    // bug in recovery code. Keep it 4 for now to make the test passes.
51
    tiny_cache_ = NewLRUCache(100, 4);
S
sdong 已提交
52
    options_.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
J
jorlow@chromium.org 已提交
53
    options_.env = &env_;
54
    dbname_ = test::PerThreadDBPath("corruption_test");
J
jorlow@chromium.org 已提交
55 56
    DestroyDB(dbname_, options_);

A
Abhishek Kona 已提交
57
    db_ = nullptr;
J
jorlow@chromium.org 已提交
58
    options_.create_if_missing = true;
59 60 61
    BlockBasedTableOptions table_options;
    table_options.block_size_deviation = 0;  // make unit test pass for now
    options_.table_factory.reset(NewBlockBasedTableFactory(table_options));
J
jorlow@chromium.org 已提交
62 63 64 65
    Reopen();
    options_.create_if_missing = false;
  }

66 67 68
  ~CorruptionTest() override {
    delete db_;
    DestroyDB(dbname_, Options());
J
jorlow@chromium.org 已提交
69 70
  }

D
Dmitri Smirnov 已提交
71 72 73 74 75
  void CloseDb() {
    delete db_;
    db_ = nullptr;
  }

A
Abhishek Kona 已提交
76
  Status TryReopen(Options* options = nullptr) {
J
jorlow@chromium.org 已提交
77
    delete db_;
A
Abhishek Kona 已提交
78
    db_ = nullptr;
J
jorlow@chromium.org 已提交
79 80
    Options opt = (options ? *options : options_);
    opt.env = &env_;
X
Xing Jin 已提交
81
    opt.arena_block_size = 4096;
82 83 84 85
    BlockBasedTableOptions table_options;
    table_options.block_cache = tiny_cache_;
    table_options.block_size_deviation = 0;
    opt.table_factory.reset(NewBlockBasedTableFactory(table_options));
J
jorlow@chromium.org 已提交
86 87 88
    return DB::Open(opt, dbname_, &db_);
  }

A
Abhishek Kona 已提交
89
  void Reopen(Options* options = nullptr) {
J
jorlow@chromium.org 已提交
90 91 92 93 94
    ASSERT_OK(TryReopen(options));
  }

  void RepairDB() {
    delete db_;
A
Abhishek Kona 已提交
95
    db_ = nullptr;
96
    ASSERT_OK(::rocksdb::RepairDB(dbname_, options_));
J
jorlow@chromium.org 已提交
97 98
  }

I
Igor Canadi 已提交
99
  void Build(int n, int flush_every = 0) {
J
jorlow@chromium.org 已提交
100 101 102
    std::string key_space, value_space;
    WriteBatch batch;
    for (int i = 0; i < n; i++) {
I
Igor Canadi 已提交
103 104 105 106
      if (flush_every != 0 && i != 0 && i % flush_every == 0) {
        DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
        dbi->TEST_FlushMemTable();
      }
J
jorlow@chromium.org 已提交
107 108 109 110 111 112 113 114 115
      //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
      Slice key = Key(i, &key_space);
      batch.Clear();
      batch.Put(key, Value(i, &value_space));
      ASSERT_OK(db_->Write(WriteOptions(), &batch));
    }
  }

  void Check(int min_expected, int max_expected) {
D
Dmitri Smirnov 已提交
116 117
    uint64_t next_expected = 0;
    uint64_t missed = 0;
J
jorlow@chromium.org 已提交
118 119 120 121
    int bad_keys = 0;
    int bad_values = 0;
    int correct = 0;
    std::string value_space;
122 123 124 125
    // Do not verify checksums. If we verify checksums then the
    // db itself will raise errors because data is corrupted.
    // Instead, we want the reads to be successful and this test
    // will detect whether the appropriate corruptions have
C
clark.kang 已提交
126
    // occurred.
127
    Iterator* iter = db_->NewIterator(ReadOptions(false, true));
J
jorlow@chromium.org 已提交
128 129 130 131 132 133 134 135 136 137
    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
      uint64_t key;
      Slice in(iter->key());
      if (!ConsumeDecimalNumber(&in, &key) ||
          !in.empty() ||
          key < next_expected) {
        bad_keys++;
        continue;
      }
      missed += (key - next_expected);
D
Dmitri Smirnov 已提交
138
      next_expected = key + 1;
139
      if (iter->value() != Value(static_cast<int>(key), &value_space)) {
J
jorlow@chromium.org 已提交
140 141 142 143 144 145 146 147
        bad_values++;
      } else {
        correct++;
      }
    }
    delete iter;

    fprintf(stderr,
D
Dmitri Smirnov 已提交
148 149 150
      "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%llu\n",
            min_expected, max_expected, correct, bad_keys, bad_values,
            static_cast<unsigned long long>(missed));
J
jorlow@chromium.org 已提交
151 152 153 154
    ASSERT_LE(min_expected, correct);
    ASSERT_GE(max_expected, correct);
  }

155
  void CorruptFile(const std::string& fname, int offset, int bytes_to_corrupt) {
J
jorlow@chromium.org 已提交
156
    struct stat sbuf;
J
jorlow@chromium.org 已提交
157 158
    if (stat(fname.c_str(), &sbuf) != 0) {
      const char* msg = strerror(errno);
159
      FAIL() << fname << ": " << msg;
J
jorlow@chromium.org 已提交
160 161 162 163 164 165 166
    }

    if (offset < 0) {
      // Relative to end of file; make it absolute
      if (-offset > sbuf.st_size) {
        offset = 0;
      } else {
167
        offset = static_cast<int>(sbuf.st_size + offset);
J
jorlow@chromium.org 已提交
168 169 170
      }
    }
    if (offset > sbuf.st_size) {
171
      offset = static_cast<int>(sbuf.st_size);
J
jorlow@chromium.org 已提交
172 173
    }
    if (offset + bytes_to_corrupt > sbuf.st_size) {
174
      bytes_to_corrupt = static_cast<int>(sbuf.st_size - offset);
J
jorlow@chromium.org 已提交
175 176 177 178 179 180 181 182 183 184 185
    }

    // Do it
    std::string contents;
    Status s = ReadFileToString(Env::Default(), fname, &contents);
    ASSERT_TRUE(s.ok()) << s.ToString();
    for (int i = 0; i < bytes_to_corrupt; i++) {
      contents[i + offset] ^= 0x80;
    }
    s = WriteStringToFile(Env::Default(), contents, fname);
    ASSERT_TRUE(s.ok()) << s.ToString();
A
Aaron G 已提交
186 187 188
    Options options;
    EnvOptions env_options;
    ASSERT_NOK(VerifySstFileChecksum(options, env_options, fname));
J
jorlow@chromium.org 已提交
189 190
  }

191 192 193 194 195 196 197 198
  void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
    // Pick file to corrupt
    std::vector<std::string> filenames;
    ASSERT_OK(env_.GetChildren(dbname_, &filenames));
    uint64_t number;
    FileType type;
    std::string fname;
    int picked_number = -1;
199
    for (size_t i = 0; i < filenames.size(); i++) {
200 201 202 203
      if (ParseFileName(filenames[i], &number, &type) &&
          type == filetype &&
          static_cast<int>(number) > picked_number) {  // Pick latest file
        fname = dbname_ + "/" + filenames[i];
204
        picked_number = static_cast<int>(number);
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
      }
    }
    ASSERT_TRUE(!fname.empty()) << filetype;

    CorruptFile(fname, offset, bytes_to_corrupt);
  }

  // corrupts exactly one file at level `level`. if no file found at level,
  // asserts
  void CorruptTableFileAtLevel(int level, int offset, int bytes_to_corrupt) {
    std::vector<LiveFileMetaData> metadata;
    db_->GetLiveFilesMetaData(&metadata);
    for (const auto& m : metadata) {
      if (m.level == level) {
        CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
        return;
      }
    }
223
    FAIL() << "no file found at level";
224 225 226
  }


227 228 229 230 231 232 233 234
  int Property(const std::string& name) {
    std::string property;
    int result;
    if (db_->GetProperty(name, &property) &&
        sscanf(property.c_str(), "%d", &result) == 1) {
      return result;
    } else {
      return -1;
J
jorlow@chromium.org 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247
    }
  }

  // Return the ith key
  Slice Key(int i, std::string* storage) {
    char buf[100];
    snprintf(buf, sizeof(buf), "%016d", i);
    storage->assign(buf, strlen(buf));
    return Slice(*storage);
  }

  // Return the value to associate with the specified key
  Slice Value(int k, std::string* storage) {
248 249 250 251 252 253 254 255 256 257
    if (k == 0) {
      // Ugh.  Random seed of 0 used to produce no entropy.  This code
      // preserves the implementation that was in place when all of the
      // magic values in this file were picked.
      *storage = std::string(kValueSize, ' ');
      return Slice(*storage);
    } else {
      Random r(k);
      return test::RandomString(&r, kValueSize, storage);
    }
J
jorlow@chromium.org 已提交
258 259 260
  }
};

I
Igor Sugak 已提交
261
TEST_F(CorruptionTest, Recovery) {
J
jorlow@chromium.org 已提交
262 263
  Build(100);
  Check(100, 100);
D
Dmitri Smirnov 已提交
264 265 266 267 268 269 270 271 272 273
#ifdef OS_WIN
  // On Wndows OS Disk cache does not behave properly
  // We do not call FlushBuffers on every Flush. If we do not close
  // the log file prior to the corruption we end up with the first
  // block not corrupted but only the second. However, under the debugger
  // things work just fine but never pass when running normally
  // For that reason people may want to run with unbuffered I/O. That option
  // is not available for WAL though.
  CloseDb();
#endif
J
jorlow@chromium.org 已提交
274
  Corrupt(kLogFile, 19, 1);      // WriteBatch tag for first record
J
jorlow@chromium.org 已提交
275
  Corrupt(kLogFile, log::kBlockSize + 1000, 1);  // Somewhere in second block
I
Igor Canadi 已提交
276 277 278
  ASSERT_TRUE(!TryReopen().ok());
  options_.paranoid_checks = false;
  Reopen(&options_);
J
jorlow@chromium.org 已提交
279 280 281

  // The 64 records in the first two log blocks are completely lost.
  Check(36, 36);
J
jorlow@chromium.org 已提交
282 283
}

I
Igor Sugak 已提交
284
TEST_F(CorruptionTest, RecoverWriteError) {
J
jorlow@chromium.org 已提交
285 286 287 288 289
  env_.writable_file_error_ = true;
  Status s = TryReopen();
  ASSERT_TRUE(!s.ok());
}

I
Igor Sugak 已提交
290
TEST_F(CorruptionTest, NewFileErrorDuringWrite) {
J
jorlow@chromium.org 已提交
291 292
  // Do enough writing to force minor compaction
  env_.writable_file_error_ = true;
293 294
  const int num =
      static_cast<int>(3 + (Options().write_buffer_size / kValueSize));
J
jorlow@chromium.org 已提交
295 296
  std::string value_storage;
  Status s;
I
Igor Canadi 已提交
297 298
  bool failed = false;
  for (int i = 0; i < num; i++) {
J
jorlow@chromium.org 已提交
299 300 301
    WriteBatch batch;
    batch.Put("a", Value(100, &value_storage));
    s = db_->Write(WriteOptions(), &batch);
I
Igor Canadi 已提交
302 303 304 305
    if (!s.ok()) {
      failed = true;
    }
    ASSERT_TRUE(!failed || !s.ok());
J
jorlow@chromium.org 已提交
306 307 308 309 310 311 312
  }
  ASSERT_TRUE(!s.ok());
  ASSERT_GE(env_.num_writable_file_errors_, 1);
  env_.writable_file_error_ = false;
  Reopen();
}

I
Igor Sugak 已提交
313
TEST_F(CorruptionTest, TableFile) {
J
jorlow@chromium.org 已提交
314 315
  Build(100);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
316
  dbi->TEST_FlushMemTable();
A
Abhishek Kona 已提交
317 318
  dbi->TEST_CompactRange(0, nullptr, nullptr);
  dbi->TEST_CompactRange(1, nullptr, nullptr);
J
jorlow@chromium.org 已提交
319 320 321

  Corrupt(kTableFile, 100, 1);
  Check(99, 99);
A
Aaron G 已提交
322
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
323 324
}

I
Igor Sugak 已提交
325
TEST_F(CorruptionTest, TableFileIndexData) {
I
Igor Canadi 已提交
326 327 328 329 330 331
  Options options;
  // very big, we'll trigger flushes manually
  options.write_buffer_size = 100 * 1024 * 1024;
  Reopen(&options);
  // build 2 tables, flush at 5000
  Build(10000, 5000);
J
jorlow@chromium.org 已提交
332
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
333
  dbi->TEST_FlushMemTable();
J
jorlow@chromium.org 已提交
334

I
Igor Canadi 已提交
335
  // corrupt an index block of an entire file
336
  Corrupt(kTableFile, -2000, 500);
J
jorlow@chromium.org 已提交
337
  Reopen();
A
Andrew Kryczka 已提交
338
  dbi = reinterpret_cast<DBImpl*>(db_);
339
  // one full file may be readable, since only one was corrupted
I
Igor Canadi 已提交
340
  // the other file should be fully non-readable, since index was corrupted
341
  Check(0, 5000);
A
Aaron G 已提交
342
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
343 344
}

I
Igor Sugak 已提交
345
TEST_F(CorruptionTest, MissingDescriptor) {
J
jorlow@chromium.org 已提交
346 347 348 349 350 351
  Build(1000);
  RepairDB();
  Reopen();
  Check(1000, 1000);
}

I
Igor Sugak 已提交
352
TEST_F(CorruptionTest, SequenceNumberRecovery) {
J
jorlow@chromium.org 已提交
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
  RepairDB();
  Reopen();
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v5", v);
  // Write something.  If sequence number was not recovered properly,
  // it will be hidden by an earlier write.
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v6", v);
  Reopen();
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v6", v);
}

I
Igor Sugak 已提交
373
TEST_F(CorruptionTest, CorruptedDescriptor) {
J
jorlow@chromium.org 已提交
374 375
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
376
  dbi->TEST_FlushMemTable();
A
Abhishek Kona 已提交
377
  dbi->TEST_CompactRange(0, nullptr, nullptr);
J
jorlow@chromium.org 已提交
378 379 380 381 382 383 384 385 386 387 388 389

  Corrupt(kDescriptorFile, 0, 1000);
  Status s = TryReopen();
  ASSERT_TRUE(!s.ok());

  RepairDB();
  Reopen();
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("hello", v);
}

I
Igor Sugak 已提交
390
TEST_F(CorruptionTest, CompactionInputError) {
I
Igor Canadi 已提交
391 392
  Options options;
  Reopen(&options);
J
jorlow@chromium.org 已提交
393 394
  Build(10);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
395
  dbi->TEST_FlushMemTable();
396 397 398
  dbi->TEST_CompactRange(0, nullptr, nullptr);
  dbi->TEST_CompactRange(1, nullptr, nullptr);
  ASSERT_EQ(1, Property("rocksdb.num-files-at-level2"));
J
jorlow@chromium.org 已提交
399 400 401

  Corrupt(kTableFile, 100, 1);
  Check(9, 9);
A
Aaron G 已提交
402
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
403 404 405 406

  // Force compactions by writing lots of values
  Build(10000);
  Check(10000, 10000);
A
Aaron G 已提交
407
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
408 409
}

I
Igor Sugak 已提交
410
TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
J
jorlow@chromium.org 已提交
411 412
  Options options;
  options.paranoid_checks = true;
413 414
  options.write_buffer_size = 131072;
  options.max_write_buffer_number = 2;
J
jorlow@chromium.org 已提交
415
  Reopen(&options);
416 417
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);

418
  // Fill levels >= 1
419
  for (int level = 1; level < dbi->NumberLevels(); level++) {
420 421
    dbi->Put(WriteOptions(), "", "begin");
    dbi->Put(WriteOptions(), "~", "end");
422
    dbi->TEST_FlushMemTable();
423 424 425 426
    for (int comp_level = 0; comp_level < dbi->NumberLevels() - level;
         ++comp_level) {
      dbi->TEST_CompactRange(comp_level, nullptr, nullptr);
    }
427
  }
J
jorlow@chromium.org 已提交
428

429 430
  Reopen(&options);

I
Igor Canadi 已提交
431
  dbi = reinterpret_cast<DBImpl*>(db_);
J
jorlow@chromium.org 已提交
432
  Build(10);
433
  dbi->TEST_FlushMemTable();
434
  dbi->TEST_WaitForCompact();
435
  ASSERT_EQ(1, Property("rocksdb.num-files-at-level0"));
J
jorlow@chromium.org 已提交
436

437
  CorruptTableFileAtLevel(0, 100, 1);
J
jorlow@chromium.org 已提交
438
  Check(9, 9);
A
Aaron G 已提交
439
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
440 441 442 443

  // Write must eventually fail because of corrupted table
  Status s;
  std::string tmp1, tmp2;
I
Igor Canadi 已提交
444
  bool failed = false;
445
  for (int i = 0; i < 10000; i++) {
J
jorlow@chromium.org 已提交
446
    s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
I
Igor Canadi 已提交
447 448 449 450 451
    if (!s.ok()) {
      failed = true;
    }
    // if one write failed, every subsequent write must fail, too
    ASSERT_TRUE(!failed || !s.ok()) << "write did not fail in a corrupted db";
J
jorlow@chromium.org 已提交
452 453 454 455
  }
  ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
}

I
Igor Sugak 已提交
456
TEST_F(CorruptionTest, UnrelatedKeys) {
J
jorlow@chromium.org 已提交
457 458
  Build(10);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
459
  dbi->TEST_FlushMemTable();
J
jorlow@chromium.org 已提交
460
  Corrupt(kTableFile, 100, 1);
A
Aaron G 已提交
461
  ASSERT_NOK(dbi->VerifyChecksum());
J
jorlow@chromium.org 已提交
462 463 464 465 466 467

  std::string tmp1, tmp2;
  ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
468
  dbi->TEST_FlushMemTable();
J
jorlow@chromium.org 已提交
469 470 471 472
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
}

473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
TEST_F(CorruptionTest, RangeDeletionCorrupted) {
  ASSERT_OK(
      db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "b"));
  ASSERT_OK(db_->Flush(FlushOptions()));
  std::vector<LiveFileMetaData> metadata;
  db_->GetLiveFilesMetaData(&metadata);
  ASSERT_EQ(static_cast<size_t>(1), metadata.size());
  std::string filename = dbname_ + metadata[0].name;

  std::unique_ptr<RandomAccessFile> file;
  ASSERT_OK(options_.env->NewRandomAccessFile(filename, &file, EnvOptions()));
  std::unique_ptr<RandomAccessFileReader> file_reader(
      new RandomAccessFileReader(std::move(file), filename));

  uint64_t file_size;
  ASSERT_OK(options_.env->GetFileSize(filename, &file_size));

  BlockHandle range_del_handle;
  ASSERT_OK(FindMetaBlock(
      file_reader.get(), file_size, kBlockBasedTableMagicNumber,
      ImmutableCFOptions(options_), kRangeDelBlock, &range_del_handle));

  ASSERT_OK(TryReopen());
  CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
  // The test case does not fail on TryReopen because failure to preload table
  // handlers is not considered critical.
  ASSERT_OK(TryReopen());
  std::string val;
  // However, it does fail on any read involving that file since that file
  // cannot be opened with a corrupt range deletion meta-block.
  ASSERT_TRUE(db_->Get(ReadOptions(), "a", &val).IsCorruption());
}

I
Igor Sugak 已提交
506
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
I
Igor Canadi 已提交
507 508 509 510 511 512 513 514 515 516
  for (int iter = 0; iter < 2; ++iter) {
    Options options;
    options.paranoid_checks = true;
    options.create_if_missing = true;
    Reopen(&options);
    Build(10);
    ASSERT_OK(db_->Flush(FlushOptions()));
    DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
    std::vector<LiveFileMetaData> metadata;
    dbi->GetLiveFilesMetaData(&metadata);
517
    ASSERT_GT(metadata.size(), size_t(0));
I
Igor Canadi 已提交
518 519 520
    std::string filename = dbname_ + metadata[0].name;

    delete db_;
I
Igor Canadi 已提交
521
    db_ = nullptr;
I
Igor Canadi 已提交
522 523

    if (iter == 0) {  // corrupt file size
524
      std::unique_ptr<WritableFile> file;
I
Igor Canadi 已提交
525 526 527 528 529 530 531 532 533 534 535 536 537 538
      env_.NewWritableFile(filename, &file, EnvOptions());
      file->Append(Slice("corrupted sst"));
      file.reset();
    } else {  // delete the file
      env_.DeleteFile(filename);
    }

    Status x = TryReopen(&options);
    ASSERT_TRUE(x.IsCorruption());
    DestroyDB(dbname_, options_);
    Reopen(&options);
  }
}

539
}  // namespace rocksdb
J
jorlow@chromium.org 已提交
540 541

int main(int argc, char** argv) {
I
Igor Sugak 已提交
542 543
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
J
jorlow@chromium.org 已提交
544
}
545 546 547 548

#else
#include <stdio.h>

549
int main(int /*argc*/, char** /*argv*/) {
550 551 552 553 554
  fprintf(stderr, "SKIPPED as RepairDB() is not supported in ROCKSDB_LITE\n");
  return 0;
}

#endif  // !ROCKSDB_LITE