本当は怖いDropboxの話 - Dropbox×ディレクトリ×シンボリックリンク=無限増殖(!?)

クラウドサービスの代名詞とも言える存在になった「Dropbox」。複数のパソコンやサーバー、スマートフォンなどを行き来する現代人にとって、もはや欠かせないオンラインストレージ・サービスでしょう。

カメは踏むほど1UPするのが赤い配管工なら、comeばcomeほど無料で容量が10GB+αまで増えるのがDropboxです(?)。万が一、まだDropboxを利用していないという方や、新しい端末でアカウントを取得するという方は「こちら」からの登録で+250MBの容量をGETしてしまいましょう(!?)。

しかし、世の中、そううまい話はありません。改めて、便利なサービスの裏側には危険が潜んでいることを肝に銘じておくべき時期なのかもしれません(??)。

「一日、一日、着実に。着実な日々の積み重ねを永遠に。その可能性は無限大」。そんな意味を込めた「(DxD)∞」(デイ・バイ・デイ・インフィニティ / Day by Day Infinity)。このブログのタイトルです(???)。

可能性が無限大なら良いですが資源は有限です。Dropboxのような便利なサービスも、使い方を誤ると貴重な資源を浪費する危険性があることについて、警鐘をならしたいと思います(?????)。

ストレージ容量と時間という有限の資源を、Dropboxの同期という名の無限ループが食いつぶしてしまう危険性についでです。無限増殖するのは、残機数でもストレージ容量でもなくファイルとディレクトリです(!?…orz)

概要

Dropboxの使い方として、各種の設定ファイルを同期させている人は多いのではないでしょうか。ファイルの置き場所を変更できない(したくない)場合には、シンボリックリンク(ハードリンク、ジャンクション)を利用することもあるかと思います(Dropboxでは、同期させるファイルを一つのディレクトリ、フォルダ内に置くことになっていますが、シンボリックリンクを作成するによって、他の場所からもファイルを参照できるようになります)。

しかし、Dropboxにシンボリックリンクを組み合わせる方法は非常に便利な反面、注意が必要です。ディレクトリへのシンボリックリンクを扱う場合、ちょっとした手違いで、リンクが自分自身を再帰的に参照してしまう可能性があります。こうなると、Dropboxがディレクトリ内に延々と同じディレクトリとファイルを同期することを繰り返し、Dropboxのストレージ容量と端末のリソースを食いつぶしていきます

手元のパソコンでGUIで利用している場合には気付きやすいですが、VPSなどのリモート端末を利用している場合やCUI版のDropboxを利用している場合には気付きにくいので注意が必要です。(余談ですが、VPSなどの共用サーバーでDropboxを利用する場合にはLAN Syncの無効化、Selective Syncによる同期ファイルの設定もしておきましょう。)

以下、(LinuxでDropboxを利用する際に)「比較的安全にシンボリックリンクを利用する方法」、「シンボリックリンクが循環してしまった場合の復旧方法」、「シンボリックリンクが循環する例」、「シンボリックリンクが循環しない例」についてまとめました。また、手動操作による間違いを防止するために簡単なシェルスクリプトも作成してみました。

前提となるディレクトリ構成とシンボリックリンク

以降、以下のような構成のディレクトリ・ファイルを例とします。

  • /home (ホームディレクトリ)
    • Dropbox (Dropboxで同期させているディレクトリ)
      • home
        • .vim (シンボリックリンクの対象)
          • 各種ディレクトリ・ファイル

Dropboxで同期させている「/home/user/Dropbox/home/.vim」に対して「/home/user/.vim」というシンボリックリンクを作成するものとします(/home/user/.vim -> /home/user/Dropbox/home/.vim)。

比較的安全にシンボリックリンクを利用する方法

lnコマンドでシンボリックリンクを作成する場合には、-sフラグに加え、-Tフラグを付けると比較的安全にシンボリックリンクを作成できます。-Tによって、シンボリックリンクが通常のファイルと同様に扱われるようになります。

ln -s -T ~/Dropbox/home/.vim ~/.vim

シンボリックリンクの循環はlnコマンド以外にcpmvコマンド等でも起こる可能性があります。シンボリックリンクのコピー、移動を行う場合にも-Tフラグを付けるようにします。

cp -T ~/.vim ~/.vim.bak
mv -T ~/.vim ~/.vim.bak

シンボリックリンクと同じ名前のファイルがあった場合にバックアップを作成するには、加えて--backup=numberedなどを付けるようにします。-b--backupとした場合は、バックアップは作られますが1世代分のみとなります。

ln -s -T --backup=numbered ~/Dropbox/home/.vim ~/.vim

シンボリックリンクが循環してしまった場合の復旧方法

シンボリックリンクが循環し、Dropboxが再帰的に同期を行うようになってしまった場合は、先にシンボリックリンクの対象ディレクトリを丸ごと削除します。

rm -rf /home/user/Dropbox/home/.vim

それからシンボリックリンクを削除します。

rm /home/user/.vim

次にDropboxのWebインターフェイスからファイルを復元します。ただし、再帰的に同期されてしまったディレクトリの階層が深い場合や、ファイル数が多い場合には一括では復元できないことがあります。小分けにしながら復元するか、対象ディレクトリ・ファイルが多く復元できない場合はサポートへの問い合わせが必要になります。

最後に、復元したディレクトリから不要なファイルを削除し、同期の際に移動されてしまったファイルを元のディレクトリへ移動します。

シンボリックリンクが循環する例

シンボリックリンクが循環してしまう例をいくつか挙げてみます。(シンボリックリンクが循環するのは、ディレクトリへのシンボリックリンクを扱う場合です。例ではlnコマンドを使用していますが、cpmvコマンドの場合でも同様です。)

ln -s /home/user/Dropbox/home/.vim /home/user/.vim
# /home/user/.vim -> /home/user/Dropbox/home/.vim

ln -s /home/user/Dropbox/home/.vim /home/user/.vim
# シンボリックリンクの状態↓
# /home/user/.vim -> /home/user/Dropbox/home/.vim
# /home/user/Dropbox/home/.vim/.vim -> /home/user/Dropbox/home/.vim
# /home/user/Dropbox/home/.vim/.vim/.vim -> /home/user/Dropbox/home/.vim
# …
ln -s -b /home/user/Dropbox/home/.vim /home/user/.vim
# シンボリックリンクの状態↓
# /home/user/.vim -> /home/user/Dropbox/home/.vim

ln -s -b /home/user/Dropbox/home/.vim /home/user/.vim
# (バックアップは作成されない)
# シンボリックリンクの状態↓
# /home/user/.vim -> /home/user/Dropbox/home/.vim
#/ home/user/Dropbox/home/.vim/.vim -> /home/user/Dropbox/home/.vim
#/ home/user/Dropbox/home/.vim/.vim/.vim -> /home/user/Dropbox/home/.vim
# …
ln -s /home/Dropbox/home/.vim /home/user/.vim
# シンボリックリンクの状態↓
# /home/user/.vim -> /home/user/Dropbox/home/.vim

ln -s /home/user/.vim Dropbox/home/.vim
# シンボリックリンクの状態↓
# /home/user/.vim -> /home/user/Dropbox/home/.vim
# /home/user/Dropbox/home/.vim -> /home/user/.vim
# /home/user/Dropbox/home/.vim/.vim -> /home/user/.vim
# …

引数として与えたディレクトリがシンボリックリンクのため、実際にはリンク先の同じディレクトリを参照していることから循環が起こります。

通常は行うことがない処理のように思いますが、間違えたコマンドを入力したり、シェルスクリプトを二重に実行するなど、ちょっとした拍子に循環が起こる可能性はあります。特に、シェルスクリプトからシンボリックリンクの作成やバックアップ処理などを行う場合には、循環が起こる原因となる処理が含まれていないか注意が必要です。

シンボリックリンクが循環しない例

lnコマンドでシンボリックリンクを作成する際に-Tフラグを付けると、リンクが通常のファイルとして扱われるようになり、通常ではリンクが循環するケースを防止できます(cpmvコマンドの場合でも同様です)。

ln -s -T /home/user/Dropbox/home/.vim /home/user/.vim
# /home/user/.vim -> /home/user/Dropbox/home/.vim

ln -s -T /home/user/Dropbox/home/.vim /home/user/.vim
# (エラーになる)
# シンボリックリンクの状態↓
# /home/user/.vim -> /home/user/Dropbox/home/.vim
ln -s -T -b /home/user/Dropbox/home/.vim /home/user/.vim
# /home/user/.vim -> /home/user/Dropbox/home/.vim

ln -s -T -b /home/user/Dropbox/home/.vim /home/user/.vim
# シンボリックリンクの状態↓
# /home/user/.vim~ -> /home/user/Dropbox/home/.vim
# /home/user/.vim -> /home/user/Dropbox/home/.vim
# (1世代分のみバックアップが作成され、古いものは削除される)
ln -s -T --backup=numbered /home/user/Dropbox/home/.vim /home/user/.vim
# /home/user/.vim -> /home/user/Dropbox/home/.vim

ln -s -T --backup=numbered /home/user/Dropbox/home/.vim /home/user/.vim
# シンボリックリンクの状態↓
# /home/user/.vim.~1~ -> /home/user/Dropbox/home/.vim
# /home/user/.vim -> /home/user/Dropbox/home/.vim
# (バックアップが作成され、古いものも削除されない)

シェルスクリプトから、バックアップを取りつつシンボリックリンクを作成したい場合などには-T--backup=numberedを併用すると、比較的安全にシンボリックリンクを作成できます。

シンボリックリンクを作成するスクリプトの例

最後に、シンボリックリンクを作成するシェルスクリプトの例を紹介します。

まず、Dropboxの特定のディレクトリ下に、実際のディレクトリ構成と同じ構成のファイル・ディレクトリを置いておきます。

TARGET_BASEDIRにリンク対象のファイル・ディレクトリを含むディレクトリを、LINK_BASEDIRにシンボリックリンクを作成するディレクトリを、FILESにシンボリックリンクの対象とするファイル・ディレクトリをTARGET_BASEDIRからの相対パスで指定します。

BACKUP0以外の場合は、同名のファイル・ディレクトリがあった場合にバックアップが作成されます。同名のファイル・ディレクトリがある場合でも、それが同じファイル・ディレクトリへのリンクであればバックアップは作成されませんBACKUP0にした場合は、同名のファイル・ディレクトリがあっても上書きはされず、単にシンボリックリンクは作成されません

後はシェルスクリプトを実行すれば、指定したディレクトリを基点として、同じディレクトリ構成でシンボリックリンクが作成されます。

#!/bin/bash

TARGET_BASEDIR=$HOME/Dropbox/home/
LINK_BASEDIR=$HOME
FILES=(.bashrc .vim .vimrc)
BACKUP=1

check_configuration ()
{
    if [ ! $TARGET_BASEDIR ]; then
        echo "Error: TARGET_BASEDIR cannot be empty."
        return 1
    fi

    if [ ! $LINK_BASEDIR ]; then
        echo "Error: LINK_BASEDIR cannot be empty."
        return 1
    fi
 
    return 0
}

filter_configuration ()
{
    local _FILE
    local -i _I=0

    TARGET_BASEDIR=${TARGET_BASEDIR%/}/
    LINK_BASEDIR=${LINK_BASEDIR%/}/

    for _FILE in ${FILES[@]}; do
        FILES[$_I]=${_FILE%/}
        _I=`expr $_I+1`
    done
}

is_link_duplicated ()
{
    local _TARGET=`readlink $1`

    [ $_TARGET ] || return 1

    if [ $2 = $_TARGET ]; then
        return 0
    fi

    return 1
}

create_symlink ()
{
    ! is_link_duplicated $2 $1 || return 0

    if [ $BACKUP -eq 0 ]; then
        ln -s -T $1 $2 >/dev/null 2>&1
    else
        ln -s -T --backup=numbered $1 $2 >/dev/null 2>&1
    fi

    if [ $? -eq 1 ]; then
        echo 'Error: Cannot create symbolic link.'
        return 1
    fi

    return 0
}

create_symlinks ()
{
    local _TARGET_FILE
    local _LINK_FILE
    local _FILE

    for _FILE in ${FILES[@]}; do
        _TARGET_FILE=${TARGET_BASEDIR}${_FILE}
        _LINK_FILE=${LINK_BASEDIR}${_FILE}

        echo "$_LINK_FILE -> $_TARGET_FILE"

        create_symlink $_TARGET_FILE $_LINK_FILE || return 1
    done

    return 0
}

main ()
{
    check_configuration || return 1
    filter_configuration || return 1
    echo 'Creating symbolic links ...'
    create_symlinks || return 1
    echo 'Done.'

    return 0
}

main || exit 1

exit 0
バックアップ作成の有無を設定する変数を追加しました。また、既に同一のシンボリックリンクが存在する場合には、新たにシンボリックリンクを作成しないようにしました。

シェルスクリプト実行後のディレクトリ・ファイル構成は以下のようになります。

  • /home (ホームディレクトリ)
    • .bashrc -> /home/Dropbox/home/.bashrc
    • .vimrc -> /home/Dropbox/home/.vimrc
    • .vim -> /home/Dropbox/home/.vim
    • Dropbox (Dropboxで同期させているディレクトリ)
      • home
        • .bashrc
        • .vimrc
        • .vim

みなさん、「(DxDxS)∞」(Dropbox×Directory×Symbolic link)でディレクトリ・ファイル数が無限大、なんてことにならないようにシンボリックリンクには気を付けましょう(!?)

コメント (1)

こんにちわ。当記事興味深く読みました。
Windows10でもともとC:\配下に配置していたDropboxの同期領域の容量をSDメモリ(F:\)で稼ごうと、mklinkコマンド(C:\ D:\)でシンボリックリンクを作成しました。これで、設定当初1.5GほどあったローカルDisk(C:\)の空き容量が消費されず維持されると期待していたのですが、運用しているとC:\ドライブの容量満杯になり、この記事で言われる循環参照が原因ではないかと推測しています。
プログラミングは本業でなく不慣れなもので、ネット検索しましたがWindows環境のビンゴな手順さがせず、手前勝手なお願いで恐縮ですが、もしWindowsで循環参照しない作成方法のポイントご存知でしたらご教示戴けたらとコメントした次第です。

コメントフォーム

トラックバック (1)

[…] ボリックリンクを設定する際に、注意しないとディレクトリの無限増殖を起こしてしまうことがあるようです。こちらのブログで詳しい説明がなされていました。すごく勉強になります。 […]

この記事のトラックバックURI
http://dxd8.com/archives/219/trackback/
この記事のURI
http://dxd8.com/archives/219/