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 "rocksdb/cache.h"
A
Aaron G 已提交
23
#include "rocksdb/convenience.h"
24
#include "rocksdb/env.h"
S
Siying Dong 已提交
25
#include "rocksdb/table.h"
26
#include "rocksdb/write_batch.h"
27 28
#include "table/block_based_table_builder.h"
#include "table/meta_blocks.h"
29
#include "util/filename.h"
30
#include "util/string_util.h"
J
jorlow@chromium.org 已提交
31 32 33
#include "util/testharness.h"
#include "util/testutil.h"

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

static const int kValueSize = 1000;

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

46
  CorruptionTest() {
47 48 49
    // 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.
50
    tiny_cache_ = NewLRUCache(100, 4);
S
sdong 已提交
51
    options_.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
J
jorlow@chromium.org 已提交
52
    options_.env = &env_;
53
    dbname_ = test::PerThreadDBPath("corruption_test");
J
jorlow@chromium.org 已提交
54 55
    DestroyDB(dbname_, options_);

A
Abhishek Kona 已提交
56
    db_ = nullptr;
J
jorlow@chromium.org 已提交
57
    options_.create_if_missing = true;
58 59 60
    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 已提交
61 62 63 64 65 66 67 68 69
    Reopen();
    options_.create_if_missing = false;
  }

  ~CorruptionTest() {
     delete db_;
     DestroyDB(dbname_, Options());
  }

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

A
Abhishek Kona 已提交
75
  Status TryReopen(Options* options = nullptr) {
J
jorlow@chromium.org 已提交
76
    delete db_;
A
Abhishek Kona 已提交
77
    db_ = nullptr;
J
jorlow@chromium.org 已提交
78 79
    Options opt = (options ? *options : options_);
    opt.env = &env_;
X
Xing Jin 已提交
80
    opt.arena_block_size = 4096;
81 82 83 84
    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 已提交
85 86 87
    return DB::Open(opt, dbname_, &db_);
  }

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

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

I
Igor Canadi 已提交
98
  void Build(int n, int flush_every = 0) {
J
jorlow@chromium.org 已提交
99 100 101
    std::string key_space, value_space;
    WriteBatch batch;
    for (int i = 0; i < n; i++) {
I
Igor Canadi 已提交
102 103 104 105
      if (flush_every != 0 && i != 0 && i % flush_every == 0) {
        DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
        dbi->TEST_FlushMemTable();
      }
J
jorlow@chromium.org 已提交
106 107 108 109 110 111 112 113 114
      //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 已提交
115 116
    uint64_t next_expected = 0;
    uint64_t missed = 0;
J
jorlow@chromium.org 已提交
117 118 119 120
    int bad_keys = 0;
    int bad_values = 0;
    int correct = 0;
    std::string value_space;
121 122 123 124
    // 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 已提交
125
    // occurred.
126
    Iterator* iter = db_->NewIterator(ReadOptions(false, true));
J
jorlow@chromium.org 已提交
127 128 129 130 131 132 133 134 135 136
    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 已提交
137
      next_expected = key + 1;
138
      if (iter->value() != Value(static_cast<int>(key), &value_space)) {
J
jorlow@chromium.org 已提交
139 140 141 142 143 144 145 146
        bad_values++;
      } else {
        correct++;
      }
    }
    delete iter;

    fprintf(stderr,
D
Dmitri Smirnov 已提交
147 148 149
      "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 已提交
150 151 152 153
    ASSERT_LE(min_expected, correct);
    ASSERT_GE(max_expected, correct);
  }

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

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

    // 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 已提交
185 186 187
    Options options;
    EnvOptions env_options;
    ASSERT_NOK(VerifySstFileChecksum(options, env_options, fname));
J
jorlow@chromium.org 已提交
188 189
  }

190 191 192 193 194 195 196 197
  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;
198
    for (size_t i = 0; i < filenames.size(); i++) {
199 200 201 202
      if (ParseFileName(filenames[i], &number, &type) &&
          type == filetype &&
          static_cast<int>(number) > picked_number) {  // Pick latest file
        fname = dbname_ + "/" + filenames[i];
203
        picked_number = static_cast<int>(number);
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
      }
    }
    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;
      }
    }
222
    FAIL() << "no file found at level";
223 224 225
  }


226 227 228 229 230 231 232 233
  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 已提交
234 235 236 237 238 239 240 241 242 243 244 245 246
    }
  }

  // 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) {
247 248 249 250 251 252 253 254 255 256
    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 已提交
257 258 259
  }
};

I
Igor Sugak 已提交
260
TEST_F(CorruptionTest, Recovery) {
J
jorlow@chromium.org 已提交
261 262
  Build(100);
  Check(100, 100);
D
Dmitri Smirnov 已提交
263 264 265 266 267 268 269 270 271 272
#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 已提交
273
  Corrupt(kLogFile, 19, 1);      // WriteBatch tag for first record
J
jorlow@chromium.org 已提交
274
  Corrupt(kLogFile, log::kBlockSize + 1000, 1);  // Somewhere in second block
I
Igor Canadi 已提交
275 276 277
  ASSERT_TRUE(!TryReopen().ok());
  options_.paranoid_checks = false;
  Reopen(&options_);
J
jorlow@chromium.org 已提交
278 279 280

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

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

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

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

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

I
Igor Sugak 已提交
324
TEST_F(CorruptionTest, TableFileIndexData) {
I
Igor Canadi 已提交
325 326 327 328 329 330
  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 已提交
331
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
332
  dbi->TEST_FlushMemTable();
J
jorlow@chromium.org 已提交
333

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

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

I
Igor Sugak 已提交
351
TEST_F(CorruptionTest, SequenceNumberRecovery) {
J
jorlow@chromium.org 已提交
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
  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 已提交
372
TEST_F(CorruptionTest, CorruptedDescriptor) {
J
jorlow@chromium.org 已提交
373 374
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
375
  dbi->TEST_FlushMemTable();
A
Abhishek Kona 已提交
376
  dbi->TEST_CompactRange(0, nullptr, nullptr);
J
jorlow@chromium.org 已提交
377 378 379 380 381 382 383 384 385 386 387 388

  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 已提交
389
TEST_F(CorruptionTest, CompactionInputError) {
I
Igor Canadi 已提交
390 391
  Options options;
  Reopen(&options);
J
jorlow@chromium.org 已提交
392 393
  Build(10);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
394
  dbi->TEST_FlushMemTable();
395 396 397
  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 已提交
398 399 400

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

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

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

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

428 429
  Reopen(&options);

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

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

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

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

  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);
467
  dbi->TEST_FlushMemTable();
J
jorlow@chromium.org 已提交
468 469 470 471
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
}

472 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
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 已提交
505
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
I
Igor Canadi 已提交
506 507 508 509 510 511 512 513 514 515
  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);
516
    ASSERT_GT(metadata.size(), size_t(0));
I
Igor Canadi 已提交
517 518 519
    std::string filename = dbname_ + metadata[0].name;

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

    if (iter == 0) {  // corrupt file size
523
      std::unique_ptr<WritableFile> file;
I
Igor Canadi 已提交
524 525 526 527 528 529 530 531 532 533 534 535 536 537
      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);
  }
}

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

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

#else
#include <stdio.h>

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

#endif  // !ROCKSDB_LITE