Upgrading in-use files breaks if %TEMP% is not on C:
jeremyvisser opened this issue · 5 comments
When updating a package, the client.RemoveOrRename()
function first attempts to delete a file, and if that fails (e.g. if it is in use) it instead moves it to a temp file returned by ioutil.TempFile()
.
This fails if the system has been configured to point %TEMP%
/ %TMP%
to a different drive to the source file.
For example:
googet_install.go:165: Error installing googet.x86_64.1.0.0@1234: rename \\?\C:\ProgramData\GooGet\googet.exe \\?\D:\TEMP\921630347: The system cannot move the file to a different disk drive.
In this case, it is trying to move C:\ProgramData\GooGet\googet.exe
to D:\TEMP\921630347
which is unsupported by os.Rename()
.
This is a known issue, closed as wontfix:
golang/go#13766
Speculation as to how Golang is meant to succeed as a general purpose programming language if it can't even move files across drives in Windows is left as an exercise to the reader.
My suggestion for how to fix is to instead rename the file in-place (in the same directory), and add an entry to the following MULTI_SZ
registry value to instruct Windows to delete the file on next reboot:
HKLM\System\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations
There's another alternative that involves directly calling MoveFileEx()
with the MOVEFILE_DELAY_UNTIL_REBOOT
flag, which avoids having to manipulate a MULTI_SZ
structure in the registry.
It goes roughly like this:
(1) Attempt to delete file, which fails
(2) Rename file in-place, to avoid renaming across drives
(3) Rename file to tmp dir with MoveFileEx()
syscall with the MOVEFILE_DELAY_UNTIL_REBOOT
flag set
Just compiled latest and moved my temp directory to D:. Still appears to fail if the file is being moved from a different drive.
Installing googet and dependencies...
ERROR: 2017/08/17 16:12:41.273852 logger.go:207: Error installing googet.x86_64.2.9.5@165615918: rename \?\C:\ProgramData\GooGet\googet.exe \?\d:\AppData\Local\Temp\776305427: The system cannot move the file to a different disk drive. It does not delete the existing file.
hmm, I'll have to do some more testing. when I tried it I'm sure it worked with different drives and removed either removed the old file or logged it for removal on reboot.
Are you sure you were running the new version of GooGet when you tested?
I tested this again an it works, reinstalling googet will cause the moved binary to remain until reboot because it's still in use when the deletion runs. A package that installs a binary running as a service that then restarts the service in the post install script will delete that moved binary at the end of install.
PS C:> googet -version
GooGet version: 2.10.0@1
PS C:> setx TEMP D:
SUCCESS: Specified value was saved.
PS C:> googet -noconfirm install -reinstall googet
Reinstalling googet.x86_64 2.10.0@1 and dependencies...
Reinstallation of googet.x86_64 2.10.0@1 completed
PS C:> ls C:\ProgramData\GooGet
Directory: C:\ProgramData\GooGet
Mode LastWriteTime Length Name
d----- 7/26/2017 7:22 PM cache
d----- 8/30/2017 8:29 PM repos
-a---- 8/31/2017 5:09 PM 7308048 googet.exe
-a---- 8/31/2017 5:06 PM 7308048 googet.exe.old207188255
-a---- 8/31/2017 5:09 PM 122538 googet.log
-a---- 8/31/2017 5:09 PM 11656 googet.state
PS C:> (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager').PendingFileRenameOperations
??\C:\Users\ajackura\AppData\Local\Temp\2\googet.exe.old207188255
Yeah, my mistake here. It looks like I was still using an old in my path. The new version does appear to fix this issue.