ちょっとジャンクション(NTFS)の更新日時を書き換えようとしたら、すんなりできなくて結構手間取ったのでメモ。
まずはディレクトリとジャンクションをセットで作成。
$directory = New-Item -Path "test_directory" -ItemType Directory
$junction = New-Item -Value "test_directory" -Path "test_junction" -ItemType Junction
dir
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2023/09/07 3:48 test_directory
d----l 2023/09/07 3:48 test_junction
そして、ジャンクションの方に更新日時を設定してみると、ジャンクションではなくリンク先のディレクトリの更新日時が書き換えられてしまいました。
$junction.LastWriteTime = "2000-01-02T03:04:05+09:00"
dir
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2000/01/02 3:04 test_directory
d----l 2023/09/07 3:48 test_junction
仕様と言えばそうなのかも知れませんが、それだったら表示される更新日時もリンク先のものでないと一貫性が無いような……。
まずは「Junction LastWriteTime」とかで調べてみると、英語圏で同様の疑問が挙がっていましたが手っ取り早い解決策は無さそうでした。(Windows APIを直接使う必要がありそうな感じ。)
更新日時変更をWindows APIを使って行う場合、CreateFile→SetFileTimeという手順になるのでこっちを調べてみたところ、ジャンクション(リパースポイント)をCreateFileで開くときにOPEN_REPARSE_POINTフラグを付けて開くとリンクを辿らずに開いてくれそうです。
OPEN_REPARSE_POINTを指定するにはWindows APIで直接指定するしか無さそうなので、自分でプログラムを作ることにしました。
日時を自由に指定できるようにするには日時文字列の解釈が必要になって面倒なので、他のファイル・フォルダの日時をコピーするプログラムです。これなら、GetFileTimeが使えるので楽。
SetFileTimeがcreationTime/lastAccessTime/lastWriteTimeを扱うので全部コピーしています。(コピーしたくなければNULLを渡せば変更されない。)
手抜きなので失敗してもメッセージとかは出ません(^_^;)
- ソースコード
#include <windows.h>
#include <tchar.h>
int _tmain(int argc, _TCHAR *argv[])
{
HANDLE hFrom;
HANDLE hTo;
FILETIME creationTime, lastAccessTime, lastWriteTime;
if (argc < 3)
{
return 1;
}
//----------------------------------------
// Get FILETIME from argv[1]
//----------------------------------------
hFrom = CreateFile(
argv[1],
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (hFrom == INVALID_HANDLE_VALUE)
{
return 1;
}
if (!GetFileTime(hFrom, &creationTime, &lastAccessTime, &lastWriteTime))
{
return 1;
}
CloseHandle(hFrom);
//----------------------------------------
// Set FILETIME to argv[2]
//----------------------------------------
hTo = CreateFile(
argv[2],
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (hTo == INVALID_HANDLE_VALUE)
{
return 1;
}
if (!SetFileTime(hTo, &creationTime, &lastAccessTime, &lastWriteTime))
{
return 1;
}
CloseHandle(hTo);
return 0;
}
- コンパイル
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat"
cl.exe /DUNICODE /D_UNICODE /source-charset:utf-8 CopyFileTime.c
CopyFileTime.exe test_directory test_junction
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2000/01/02 3:04 test_directory
d----l 2000/01/02 3:04 test_junction
ちゃんと日時コピーできてました。
コピー元の方はOPEN_REPARSE_POINTを付けずに開いている(リンク先を辿る)ので、
CopyFileTime.exe test_junction test_junction
としても同じ効果のはず。