潜在的なディスクエラー………恐ろしい子!

Windows の次世代ファイル システム: ReFS」を読んだ。

私にとって、ReFSでいちばん欲しい機能は、潜在的なディスク エラーを防ぐディスク スクラブ処理だ。
気づかないうちにデータが回復不能になるかもしれないというのは不安だ。
ReFSではこれへの対策が入るという。
だから今すぐにでもReFSが欲しいのだが、いつ使えるようになるのか分からない。

「ディスクの 一部が経時的に破損していき、読み取りが頻繁に行われないためにそのことが検出されない事態」は、ときどき全ファイルを読み込んでみれば防げるのでは? と思い、cygwin で以下のようなコマンド*1を実行してみた。

/bin/find d:/ -type f -exec cat {} \; > /dev/null

一応これで全ファイルを読むことができるはず。読み取りエラーが発生すればわかるので、バックアップがあるファイルは回復できる。

この方法の問題点はシステム全体の性能が低下することだ。おそらくディスクキャッシュの中身がすべて役に立たないものにされてしまうからだろう。操作に対する応答が、使い物にならないほどに遅くなってしまった。

というわけで、キャッシュを汚さずにファイルの中身を読み込むコマンドを作ることにした。CreateFile のフラグに一つ追加するだけなので難しくはない。大した大きさじゃないので、なくさないようにここにメモっておくことにした。ファイルにコピペして、cl.exe でコンパイルすれば使える。第一引数がファイル名だ。

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define READ_BYTES (100 * 1024 * 1024)

int main(int argc, char* argv[])
{
  void* buf;
  DWORD totalBytesRead = 0;
  DWORD numberOfBytesRead;
  HANDLE hFile = CreateFile(argv[1],
                            GENERIC_READ,
                            FILE_SHARE_READ,
                            NULL,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN,
                            NULL);
  if (hFile == INVALID_HANDLE_VALUE) {
    fprintf(stderr, "CreateFile failed(%d): %s\n", GetLastError(), argv[1]);
    return 1;
  }

  buf = _aligned_malloc(READ_BYTES, 4 * 1024);
  if (buf == NULL) {
    fprintf(stderr, "_aligned malloc failed.\n");
    return 2;
  }

  while(1) {
    if (ReadFile(hFile, buf, READ_BYTES, &numberOfBytesRead, NULL)) {
      if (numberOfBytesRead == 0) {
        // printf("total %u bytes\n", totalBytesRead);
        break; // 読み終わりました。
      }
      totalBytesRead += numberOfBytesRead;
    } else {
      fprintf(stderr, "ReadFile error (%d): %s\n", GetLastError(), argv[1]);
      break;
    }
  }

  _aligned_free(buf);
  CloseHandle(hFile);
  return 0;
}

これをhoge.exe などの名前でPATHの通ったところにおいて、

/bin/find d:/ -type f -exec hoge {} \;

とすればきっと目的が達成できるにちがいない。

今のところ、システム全体が遅くなるようなこともなく、快適に動いている。
ReFSが使えるようになるまで、ときどきこれで全ファイルを読み込んでみようと思っている。
ディスクのエラーを発見できるかどうかは、テストデータを用意できないので、出てみてからのお楽しみなのだけど。

*1:/bin/find とするのは、WindowsのFINDが実行されないようにするため。