sshfs+LUKS 設定における fsync() の意味

sshfs+LUKS 設定における fsync() の意味

背景:信頼できないコンピューターのストレージを暗号化する方法を探しています。現在の設定では、sshfsを使用してLUKS暗号化イメージにアクセスします。離れて機械、解読済み地元のext3としてインストールされました。 (sshfsのみを使用すると、リモートシステムにアクセスできる人が自分のデータを表示できます。)以下は設定例です。

# On the local machine:
sshfs remote:/home/crypt /home/crypt
cryptsetup luksOpen /home/crypt/container.img container
mount /dev/mapper/container /home/crypt-open

# Place cleartext files in /home/crypt-open,
# then reverse the above steps to unmount.

ネットワーク障害に対する回復力を高めたい。このために、この設定でキャッシュ/バッファリングがどのように発生するかを理解したいと思います。次の 2 つのコマンドを検討してください。

dd if=/dev/random of=/home/crypt-open/test.dat bs=1000000 count=100
dd if=/dev/random of=/home/crypt-open/test.dat bs=1000000 count=100 conv=fsync

最初のコマンドは非常に迅速に返され、コマンドが返された後でもデータが送信され続けていることをネットワークトラフィックに表示できます。 2番目のコマンドは、データ転送が完了するのを待っているようです。

特定の問題:fsync()この設定では何を保証しますか?fsync()これらのレイヤーのデータは、戻り時にどのくらいの同期が保証されますか?リモートコンピュータのハードドライブと常に同期させるにはどうすればよいですか?

--- /home/crypt-open on the local machine
|
| (ext3 fs)
|
--- /dev/mapper/container on the local machine
|
| (LUKS)
|
--- /home/crypt/container.img on the local machine
|
| (sshfs)
|
--- /home/crypt/container.img on the remote machine
|
| (ext3 fs)
|
--- hard drive on the remote machine

答え1

ここで最も弱いリンクはSSHFSコードであると仮定します。残りの部分はカーネルにあり、多く使用されているので大丈夫でしょう。以前はFUSEコードを実際に見たことがないので、私が逃したことがあるかもしれませんが、SSHFSソースコード、 SSHFS の実装はfsync()多くの操作を実行せず、単にflush()IOストリームを呼び出します。

static int sshfs_fsync(const char *path, int isdatasync,
                       struct fuse_file_info *fi)
{
    (void) isdatasync;
    return sshfs_flush(path, fi);
}

私たちは、この関数がfsyncを強制するリモートシステムにどのような同期コマンドも送信しないことをsshfs.c:2551知っています。私は、このフラグが「書き込むたびにサーバーからfsync」ではなく、「書き込みから戻る前にコマンドがサーバーに送信されるのを待つ」ことを意味するとsshfs_flush()思います。なぜなら、2番目の意味が非常に奇妙だからです。sshfs.sync_writeしたがって、fsync のボトルネックはリモートディスク速度ではなく、ネットワーク速度のためにゆっくりと測定されます。

static int sshfs_flush(const char *path, struct fuse_file_info *fi)
{
    int err;
    struct sshfs_file *sf = get_sshfs_file(fi);
    struct list_head write_reqs;
    struct list_head *curr_list;

    if (!sshfs_file_is_conn(sf))
        return -EIO;

    if (sshfs.sync_write)
        return 0;

    (void) path;
    pthread_mutex_lock(&sshfs.lock);
    if (!list_empty(&sf->write_reqs)) {
        curr_list = sf->write_reqs.prev;
        list_del(&sf->write_reqs);
        list_init(&sf->write_reqs);
        list_add(&write_reqs, curr_list);
        while (!list_empty(&write_reqs))
            pthread_cond_wait(&sf->write_finished, &sshfs.lock);
    }
    err = sf->write_error;
    sf->write_error = 0;
    pthread_mutex_unlock(&sshfs.lock);
    return err;
}

リモートSFTP実装は実際に書き込み時にfsyncを実行できますが、これは本当ではないと思います。古い言葉によるとSFTP標準ドラフト(これが私が見つけることができる最善の方法です。)この動作を指定する方法があります。

7.9. attrib-bits and attrib-bits-valid
...
SSH_FILEXFER_ATTR_FLAGS_SYNC
       When the file is modified, the changes are written synchronously
       to the disk.

これはこれがデフォルト設定ではないことを意味します(fsyncがより速くないため)。標準的な文書によると、リモートファイルにfsyncを要求する方法がないようですが、OpenSSHはそれをSFTPの拡張としてサポートしているようです。

/* SSH2_FXP_EXTENDED submessages */
struct sftp_handler extended_handlers[] = {
    ...
    { "fsync", "[email protected]", 0, process_extended_fsync, 1 },
    ...
};

static void
process_extended_fsync(u_int32_t id)
{
    int handle, fd, ret, status = SSH2_FX_OP_UNSUPPORTED;

    handle = get_handle();
    debug3("request %u: fsync (handle %u)", id, handle);
    verbose("fsync \"%s\"", handle_to_name(handle));
    if ((fd = handle_to_fd(handle)) < 0)
        status = SSH2_FX_NO_SUCH_FILE;
    else if (handle_is_ok(handle, HANDLE_FILE)) {
        ret = fsync(fd);
        status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
    }
    send_status(id, status);
}

私はこの拡張を照会し、SSHFSでfsyncを適切にサポートするのが難しいと思います。これはかなり合理的な仕事のようです。つまり、Linuxのネットワークブロックデバイスサポートを使用する方が簡単になると思います。このサポートはこれらすべてを正しくサポートしていると思います(私が自分で使ったことがないので悪いかもしれません)。

関連情報