
scpアップロード中のファイルはサーバーに存在する — 監視トリガーが未完成ファイルを処理してしまう問題とsupによる対策
Cyberduck や scp、sftp で Linux サーバーへファイルをアップロードするとき、転送が終わるまで対象ディレクトリには何も見えないと思っていた。 でも実際には、アップロード開始直後から転送先にファイルが作られ、そこへ少しずつデータが書き込まれていくことがある。大きなファイルを送ると、サーバー側で ls -lh したときにサイズが増えていく様子が見える。 これ自体は普通の挙動だが、知らないと少し危ない。 たとえば、アップロード先を別のバッチ処理やファイル監視デーモンが見ている場合、まだ途中のファイルを「完成したファイル」として読み込んでしまう可能性がある。jar、画像、CSV、バックアップファイルなど、読み込み側がファイル名だけで判断していると起きやすい。 WinSCP(Windows 向けの SFTP/SCP クライアント)はデフォルトでこの問題を回避している。転送中は内部で一時ファイル名を使い、完了後に正式名へリネームする仕組みが組み込まれているためだ。一方、macOS の Cyberduck は有料課金していてもこの挙動にはなっておらず、普通にアップロードすると転送中から正式名でファイルが見える。 アップロード中のファイルは見える scp や sftp の put は、基本的には転送先のファイルを開いて、そこへデータを書いていく操作だ。 そのため、転送中は次の状態になりうる。 転送完了前のファイルが、不完全なサイズで存在する 転送に失敗すると、中途半端なファイルとして残ることがある 監視処理やバッチ処理が、そのファイルを先に読んでしまうことがある 確認してみる 挙動は、大きめのダミーファイルを送るとすぐ確認できる。 まず手元の Mac などで、500MB のファイルを作る。 dd if=/dev/urandom of=testfile.bin bs=1M count=500 サーバー側では、転送先ディレクトリを監視しておく。 watch -n 0.5 'ls -lh /path/to/target/' 別ターミナルから scp でアップロードする。 scp testfile.bin user@hostname:/path/to/target/ サーバー側の watch で testfile.bin が現れ、サイズが少しずつ増えていけば、転送中のファイルが見えている。 確認が終わったら、ダミーファイルを削除する。 # 手元 rm testfile.bin # サーバー側 rm /path/to/target/testfile.bin supコマンドで安全に転送する 対策の考え方はシンプルで、最初から正式名で置かないことだ。一時ファイル名でアップロードし、完了後にアトミックな mv で正式名に切り替える。 ...