static StoreFileList load(FileSystem fs, Path path) throws IOException {
    byte[] data;
    int expectedChecksum;
    try (FSDataInputStream in = fs.open(path)) {
      int length = in.readInt();
      if (length <= 0 || length > MAX_FILE_SIZE) {
        throw new IOException("Invalid file length " + length
          + ", either less than 0 or greater then max allowed size " + MAX_FILE_SIZE);
      }
      data = new byte[length];
      in.readFully(data);
      expectedChecksum = in.readInt();
    }
    CRC32 crc32 = new CRC32();
    crc32.update(data);
    int calculatedChecksum = (int) crc32.getValue();
    if (expectedChecksum != calculatedChecksum) {
      throw new IOException(
        "Checksum mismatch, expected " + expectedChecksum + ", actual " + calculatedChecksum);
    }
    StoreFileList storeFileList = StoreFileList.parseFrom(data);
    if (storeFileList.getVersion() > VERSION) {
      LOG.error(
        "The loaded store file list is in version {}, which is higher than expected"
          + " version {}. Stop loading to prevent potential data loss. This usually because your"
          + " cluster is downgraded from a newer version. You need extra steps before downgrading,"
          + " like switching back to default store file tracker.",
        storeFileList.getVersion(), VERSION);
      throw new IOException("Higher store file list version detected, expected " + VERSION
        + ", got " + storeFileList.getVersion());
    }
    return storeFileList;
  }
To fully avoid listing, here we use two files for tracking. When loading, we will try to read
both the two files, if only one exists, we will trust this one, if both exist, we will compare
the timestamp to see which one is newer and trust that one. And we will record in memory that
which one is trusted by us, and when we need to update the store file list, we will write to the
other file.
<p/>
So in this way, we could avoid listing when we want to load the store file list file.
<p/>
To prevent loading partial file, we use the first 4 bytes as file length, and also append a 4
bytes crc32 checksum at the end. This is because the protobuf message parser sometimes can return
without error on partial bytes if you stop at some special points, but the return message will
have incorrect field value. We should try our best to prevent this happens because loading an
incorrect store file list file usually leads to data loss.
<p/>
To prevent failing silently while downgrading, where we may miss some newly introduced fields in
{@link StoreFileList} which are necessary, we introduce a 'version' field in
{@link StoreFileList}. If we find out that we are reading a {@link StoreFileList} with higher
version, we will fail immediately and tell users that you need extra steps while downgrading, to
prevent potential data loss.
hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/storefiletracker/StoreFileListFile.java