junction_go.run:
package main import ( "os" "unsafe" "errors" "golang.org/x/sys/windows" "golang.org/x/xerrors" ) const ( _MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384 _FSCTL_SET_REPARSE_POINT = 589988 _INVALID_HANDLE_VALUE = ^windows.Handle(0) ) type _MountPointInfo struct { Tag uint32 DataLength uint16 Reserved uint16 TargetOffset uint16 TargetByteLength uint16 DescriptionOffset uint16 DescriptionByteLength uint16 Buffer [(_MAXIMUM_REPARSE_DATA_BUFFER_SIZE - 4 - 2*6) / 2]uint16 } func MountPointCreate(mountPointPath, target string) error { _mountPointPath, err := windows.UTF16FromString(mountPointPath) if err != nil { return xerrors.Errorf("UTF16FromString(%s): %v", mountPointPath, err) } _target, err := windows.UTF16FromString(target) if err != nil { return xerrors.Errorf("UTF16FromString(%s): %v", target, err) } var info _MountPointInfo info.Tag = windows.IO_REPARSE_TAG_MOUNT_POINT info.TargetOffset = 0 info.TargetByteLength = uint16(len(_target) * 2 ) for i := 0 ; i < len(_target) ; i++ { info.Buffer[i] = _target[i] } // copy(info.Buffer[:], _target) info.Buffer[len(_target)] = 0 info.DescriptionOffset = uint16((len(_target)+1) * 2) info.DescriptionByteLength = 0 info.DataLength = uint16(8 + (len(_target)+1+1)*2) err = windows.CreateDirectory(&_mountPointPath[0], nil) if err != nil { return xerrors.Errorf("windows.CreateDirectory(%s): %v", mountPointPath, err) } handle, err := windows.CreateFile(&_mountPointPath[0], windows.GENERIC_WRITE, 0, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) if err != nil { return xerrors.Errorf("windows.CreateFile(%s): %v", mountPointPath, err) } if handle == _INVALID_HANDLE_VALUE { return errors.New("windows.CreateFile: invalid handle value") } defer windows.CloseHandle(handle) var size uint32 err = windows.DeviceIoControl( handle, _FSCTL_SET_REPARSE_POINT, (*byte)(unsafe.Pointer(&info)), uint32(8+info.DataLength), nil, 0, &size, nil) if err != nil { return xerrors.Errorf("windows.DeviceIoControl: %v", err) } return nil } func main() { if len(os.Args) < 3 { println("go run junction.go DST SRC") return } if err := MountPointCreate(os.Args[1], os.Args[2]); err != nil { println(err.Error()) os.Exit(1) } }
これ、動作するけど、ジャンクションにならなんだよな…
$ go run junction_run.go Hoge c:\tmp $ cd Hoge\ chdir Hoge\: The filename, directory name, or volume label syntax is incorrect. exit status 1 $ ls -l Hoge\ -rw---- 0 Mar 24 17:43:26 Hoge@ -> c:\tmp $
これ、バッファのサイズとか、ちょっとでも触るとAPIがエラーを返してくる。で、現在のソースだと実行は成功するんだけど、できたディレクトリはジャンクションとして機能しない。リパースポイント先は記録されているのに(nyagos の ls で表示できるから)
うーん、なんでだろ(というか、この記事は事実上、あきらめの儀式である)
参考リンク
- ディレクトリジャンクションを作成する (mixi 日記アーカイブ)
- リパースデータバッファの取得 (mixi 日記アーカイブ)
- FSCTL_SET_REPARSE_POINT control code (Windows)
- DeviceIoControl function (ioapiset.h) | Microsoft Docs
追記
実は、2時間後に解決したのだった…