google/googet

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

#28 should fix this

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.