ProFTPD の HiddenStores で atomic なアップロード
TL;DR
-
HiddenStores
でアップロードを2段階にできる(atomic なアップロード)- アップロード開始時に
.in.filename.
があればエラー応答で中断 - アップロード中は
.in.filename.
に書き込む - アップロード完了後に
filename
にリネームする
- アップロード開始時に
-
HiddenStores
は真偽値だけでなく prefix/suffix も指定できる -
DeleteAbortedStores
で中断時にファイルを削除できる-
HiddenStores
を使っている場合DeleteAbortedStores off
としない限り削除される -
HiddenStores
を使っていない場合DeleteAbortedStores on
とした場合のみ削除される
-
環境
- CentOS 7.1
- ProFTPD 1.3.5a (yum epel)
同時アップロード
ProFTPD のデフォルト設定だと、異なる接続から異なるファイルを同一のパスに同時にアップロードした場合、場合によっては内容が混ざってしまう事がある。
put a.txt |-------->| data.txt
put b.txt |--->| data.txt
上記の様なタイミングでアップロードを実行した場合、ファイルサイズが a.txt
と同じで、 a.txt
と b.txt
の内容を含んだ妙なファイルができあがる。
アップロード処理の実装は読んでいないので、理屈については割愛。
HiddenStores
ProFTPD の mod_xfer
には HiddenStores
というディレクティブがある。
これを有効にするとアップロードが2ステップになり、内容の混在やアップロード途中のファイルが参照されてしまう様な事態を防ぐことができる。
HiddenStores On
アップロード中は .in.filename.
という中間ファイルに書き込まれる仕組みになっており、アップロード(STOR
)開始時に該当ファイルが存在する場合は 550 エラー応答が返ってくる。
ftp> put hoge.dat
local: hoge.dat remote: hoge.dat
229 Entering Extended Passive Mode (|||9147|)
550 hoge.dat: Temporary hidden file /.in.hoge.dat. already exists
中間ファイルの名前を変える
HiddenStores
ディレクティブは prefix と suffix を指定する事もできる。
以下の様に設定すると、中間ファイル名は .pre.filename.suf.
の形式になる。
HiddenStores .pre. .suf.
アップロードをやめた場合
ftp シェルと C-c
Mac OSX 付属の ftp
コマンドの対話的シェルを利用してアップロードを行い、アップロード中に C-c
で中断した場合の挙動を確認。
-
put hoge.dat
を実行 - リモートの
.in.hoge.dat.
に書き込まれていく -
C-c
で中断 - リモートの
.in.hoge.dat.
がhoge.dat
にリネームされたHiddenStore: complex path, will rename /.in.hoge.dat. to /hoge.dat
ftp シェルと SIGTERM
今度は、同様の手順でアップロード中に、そのプロセスに対して SIGTERM
を送った場合の挙動を確認。
-
put hoge.dat
を実行(プロセスA) - リモートの
.in.hoge.dat.
に書き込まれていく - 別の zsh シェルで
pkill -TERM ftp
を実行 - リモートの
.in.hoge.dat.
はリネームされず削除されたHiddenStore: complex path, will rename /.in.hoge.dat. to /hoge.dat
removing aborted HiddenStores file '/.in.hoge.dat.'
DeleteAbortedStores ディレクティブとは?
mod_xfer
には DeleteAbortedStores
というディレクティブもあり、これは部分的にアップロードされた HiddenStores
ファイルを自動で削除する機能を有効にするものと説明されている。
ただし、対象となるのは ABOR
コマンドによって中断された転送のみで、接続の失敗によるものは対象としないとのこと。
SIGTERM
を送った場合に記録されたログメッセージ removing aborted HiddenStores file '/.in.hoge.dat.'
から、ソースコードの該当行を確認すると DeleteAbortedStores
ディレクティブによる挙動に見える。
if (session.xfer.xfer_type == STOR_HIDDEN) {
delete_stores = get_param_ptr(CURRENT_CONF, "DeleteAbortedStores", FALSE);
if (delete_stores == NULL ||
*delete_stores == TRUE) {
/* If a hidden store was aborted, remove only hidden file, not real
* one.
*/
if (session.xfer.path_hidden) {
pr_log_debug(DEBUG5, "removing aborted HiddenStores file '%s'",
session.xfer.path_hidden);
pr_fsio_unlink(session.xfer.path_hidden);
}
}
設定ファイルにディレクティブを書いていない状態なので NULL
となっていて、ファイルを unlink
する条件「 NULL
か TRUE
」を満たしたのではないか。
DeleteAbortedStores off
DeleteAbortedStores off
とした場合の C-c
による中断と SIGTERM
による中断について挙動を確認。
C-c
-
put hoge.dat
を実行 - リモートの
.in.hoge.dat.
に書き込まれていく C-c
- リモートの
.in.hoge.dat.
がhoge.dat
にリネームされたHiddenStore: complex path, will rename /.in.hoge.dat. to /hoge.dat
SIGTERM
-
put hoge.dat
を実行(プロセスA) - リモートの
.in.hoge.dat.
に書き込まれていく - 別の zsh シェルで
pkill -TERM ftp
を実行 - リモートの
.in.hoge.dat.
がhoge.dat
にリネームされたHiddenStore: complex path, will rename /.in.hoge.dat. to /hoge.dat
DeleteAbortedStores on
C-C で中断
-
put hoge.dat
を実行 - リモートの
.in.hoge.dat.
に書き込まれていく C-c
- リモートの
.in.hoge.dat.
がhoge.dat
にリネームされたHiddenStore: complex path, will rename /.in.hoge.dat. to /hoge.dat
SIGTERM
-
put hoge.dat
を実行(プロセスA) - リモートの
.in.hoge.dat.
に書き込まれていく - 別の zsh シェルで
pkill -TERM ftp
を実行 - リモートの
.in.hoge.dat.
はリネームされず削除されたHiddenStore: complex path, will rename /.in.hoge.dat. to /hoge.dat
removing aborted HiddenStores file '/.in.hoge.dat.'
HiddenStores off と DeleteAbortedStores
DeleteAbortedStores なし
C-c
による中断および SIGTERM
による中断でアップロード途中のファイルは残る。
DeleteAbortedStores on
C-c
による中断でアップロード途中のファイルが残り、 SIGTERM
による中断でアップロード途中のファイルは消される。
DeleteAbortedStores off
C-c
による中断および SIGTERM
による中断でアップロード途中のファイルは残る。
ソースコードで確認
HiddenStores
が無効である場合、ファイルを unlink
する条件が「 NULL
ではなく TRUE
である場合」となっている。
HiddenStores
が有効か無効かで unlink
する条件が異なる模様。
if (session.xfer.xfer_type == STOR_HIDDEN) {
...
} else if (session.xfer.path) {
delete_stores = get_param_ptr(CURRENT_CONF, "DeleteAbortedStores", FALSE);
if (delete_stores != NULL &&
*delete_stores == TRUE) {
pr_log_debug(DEBUG5, "removing aborted file '%s'", session.xfer.path);
pr_fsio_unlink(session.xfer.path);
}
}
HiddenStores とファイルモード
HiddenStores on
としている時の STOR
は、ファイルをモード O_WRONLY | O_CREAT | O_EXCL
でオープンする。
O_EXCL
が指定されているため、すでに中間ファイルが存在している場合はオープンに失敗する。
※ ただし、NFS を使っている場合は O_EXCL
を使わない。
Author And Source
この問題について(ProFTPD の HiddenStores で atomic なアップロード), 我々は、より多くの情報をここで見つけました https://qiita.com/koshigoe/items/6b577510e29e836ba77d著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .