📁

なぜかコピーできないDosStream.Zone.Identifier付きファイルをコピーする

に公開

結論

ファイルとしてのコピーは不可能なので、ファイル本体のバイナリを読み取って別ファイルとして新規に書き出すことでコピーするしかない。元ファイルはフォルダごと消す。

コマンド例

@echo off
chcp 932 >nul
if "%~1"=="" goto :usage
if "%~2"=="" goto :usage

echo コピー中: %~f1 -^> %~f2
powershell -Command "[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding('shift_jis'); [System.IO.File]::WriteAllBytes('%~f2', [System.IO.File]::ReadAllBytes('%~f1')); Write-Host 'コピー完了'" 2>nul
if %ERRORLEVEL% equ 0 (
    echo 成功
) else (
    echo 失敗
)
goto :end

:usage
echo 使い方: bcopy [ソース] [コピー先]
echo 例: bcopy src.file dest.file

:end

経緯など

新しいハードディスクを買ったので古いファイルをコピーしていたら、いくつかのファイルがコピーできないことに気づいた。Windowsエクスプローラ上でもエラー、Copyコマンドでもエラーになる。

> copy a.file F:\archive\
ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。
        0 個のファイルをコピーしました。

もちろんa.fileもF:\archiveも存在しているし、権限もある。実際同じフォルダ内の別のファイルは問題なくコピーされている。a.fileが壊れているわけでもなく、対応アプリから開くこともできる。何かのプロセスにつかまれているわけでもない。

AIと一緒にアレコレ切り分けした結果、このファイルには謎のZone.Identifierが付与されていることが分かった。

> dir /r
a.file
a.file:DosStream.Zone.Identifier:$DATA

Zone.IdentifierファイルはNTFSにおける代替データストリーム(ADS)というやつで、いわゆるメタデータというやつだ。インターネットからダウンロードされたファイルなどに付与されているのをよく見る。Windowsはこれを見て「インターネットから手に入れたファイルは危険やで」などと警告してくれたりする。WSL内ファイルシステムにエクスプローラからファイルコピーすると急に出てくることで有名(要出典)。

しかしこれはよく見るやつと違ってDosStreamという名前が頭についている。初めて見たしGemini君にDeepResearchしてもらっても碌な情報が見つからなかった。特定状況下でSambaが付与する、みたいな情報があったので、もしかしたら過去にNASに保管していたときに付与されたのかもしれない。

ともあれこれをなんとかコピーしたいのだが、ファイル操作レベルでのコピー操作はすべて失敗した。copy, xcopy, PowerShellのCopy-Item -Forceなど。

PS > Copy-Item "a.file" "F:\archive\" -Force
Copy-Item : ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。
発生場所 行:1 文字:1
+ Copy-Item "a.file" "F:\archive\" -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Copy-Item], IOException
    + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.CopyItemCommand

ファイル名に見えない制御文字などが入っていてパスが狂っているのか?と思いファイル名のバイナリを取得したが問題なかった。

PS > [System.Text.Encoding]::UTF8.GetBytes("a.file") | ForEach-Object { $_.ToString("x2") }
61
2e
66
69
6c
65

とするとやはりDosStream.Zone.Identifierが怪しいので、そもそも何が書いてあるのかとDosStream.Zone.Identifierの中を見ようとしたがこれもダメ。

Get-Content "a.file" -Stream "DosStream.Zone.Identifier"
Get-Content : ファイル 'a.file' の代替データ ストリーム 'DosStream.Zone.Identifier' を開けませんで
した。
発生場所 行:1 文字:1
+ Get-Content "a.file" -Stream "DosStream.Zone.Identifier"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (a.file:String) [Get-Content], FileNotFoundException
    + FullyQualifiedErrorId : GetContentReaderFileNotFoundError,Microsoft.PowerShell.Commands.GetContentCommand

もういいやとDosStream.Zone.Identifierの削除を試したがこれもダメだった。SysinternalのstreamsコマンドはDosStream.Zone.Identifierを認識してくれない。echoコマンドでDosStream.Zone.Identifierをゼロ埋めしても新たなDosStream.Zone.Identifierが出来上がる。無敵か。

ということで結局、冒頭記載のようにファイル本体のバイナリを読んで別ファイルとして書き直して解決した。マジで何者なんだ、DosStream.Zone.Identifier。

Discussion