Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Self update under windows issues. #115

Open
slyshykO opened this issue Sep 22, 2022 · 16 comments · May be fixed by #162
Open

Self update under windows issues. #115

slyshykO opened this issue Sep 22, 2022 · 16 comments · May be fixed by #162
Assignees
Labels
bug Something isn't working hacktoberfest https://hacktoberfest.com/ help wanted Extra attention is needed 🐧 🍏 🪟 Cross OS support
Milestone

Comments

@slyshykO
Copy link

Is it possible to sync the tool from the tool under windows?
I am getting the error:

NO tool-sync v0.2.0   [error] The process cannot access the file because it is being used by another process. (os error 32)
@MitchellBerend
Copy link
Collaborator

Hi @slyshykO thanks for creating this issue, can you supply your tools.toml config file?
I think most (if not all) current contributers work on unix, so feedback on windows issues is very helpful. They can be incorporated in ci so they are caught earlier in the development process.

@slyshykO
Copy link
Author

slyshykO commented Sep 22, 2022

tool.toml.zip

This is my config.

The store_directory is set to $HOME/bin.
The tool is also located here in the $HOME/bin.

@MitchellBerend
Copy link
Collaborator

Are you on WSL? I did not know windows also had a $HOME environment variable.

For future reference, this is their config file

# This configuration is automatically generated by tool-sync 0.2.0
# https://github.com/chshersh/tool-sync
#######################################
#
# Installation directory for all the tools:
store_directory = "$HOME/bin"
#
# tool-sync provides native support for some of the tools without the need to
# configure them. Uncomment all the tools you want to install with a single
# 'tool sync' command:
#
# [bat]
# [difftastic]
# [exa]
[fd]
# [github]
[ripgrep]
[tool-sync]
#
# You can configure the installation of any tool by specifying corresponding options:
#
# [ripgrep]  # Name of the tool (new or one of the hardcoded to override default settings)
#     owner     = "BurntSushi"  # GitHub repository owner
#     repo      = "ripgrep"     # GitHub repository name
#     exe_name  = "rg"          # Executable name inside the asset

#     Uncomment to download a specific version or tag.
#     Without this tag latest will be used
#     tag       = "13.0.0"

#     Asset name to download on linux OSes
#     asset_name.linux = "x86_64-unknown-linux-musl"

#     Uncomment if you want to install on macOS as well
#     asset_name.macos = "apple-darwin"

#     Uncomment if you want to install on Windows as well
#     asset_name.windows = "x86_64-pc-windows-msvc"

@MitchellBerend
Copy link
Collaborator

@slyshykO
Can you please try it with this config, there might be an issue with the way the windows config is hardcoded. This overwrites the hardcoded version.

# # tool-sync default configuration file
# https://github.com/chshersh/tool-sync
# This file was automatically generated by tool-sync
#####################################################
#
#
store_directory = "$HOME/bin"
#
# tool-sync provides native support for some of the tools without the need to
# configure them. Uncomment all the tools you want to install with a single
# 'tool sync' command:
#
# [bat]
# [difftastic]
# [exa]
[fd]
# [github]
[ripgrep]
[tool-sync]
     owner     = "chshersh"  # GitHub repository owner
     repo      = "tool-sync"     # GitHub repository name
     exe_name  = "tool"          # Executable name inside the asset
#     Uncomment to download a specific version or tag.
#     Without this tag latest will be used
#     tag       = "13.0.0"
#     Asset name to download on linux OSes
     asset_name.windows = "x86_64-pc-windows-msvc.zip"

#
# You can configure the installation of any tool by specifying corresponding options:
#
# [ripgrep]  # Name of the tool (new or one of the hardcoded to override default settings)
#     owner     = "BurntSushi"  # GitHub repository owner
#     repo      = "ripgrep"     # GitHub repository name
#     exe_name  = "rg"          # Executable name inside the asset

#     Uncomment to download a specific version or tag.
#     Without this tag latest will be used
#     tag       = "13.0.0"

#     Asset name to download on linux OSes
#     asset_name.linux = "x86_64-unknown-linux-musl"

#     Uncomment if you want to install on macOS as well
#     asset_name.macos = "apple-darwin"

#     Uncomment if you want to install on Windows as well
#     asset_name.windows = "x86_64-pc-windows-msvc"

@MitchellBerend MitchellBerend added bug Something isn't working help wanted Extra attention is needed 🐧 🍏 🪟 Cross OS support labels Sep 22, 2022
@slyshykO
Copy link
Author

@MitchellBerend
Try to run with changed config - same error.
I think the problem is that it is impossible to rewrite the file that is used by the system.
So the tool can't rewrite itself cause it running at that time.

@MitchellBerend
Copy link
Collaborator

MitchellBerend commented Sep 22, 2022

Mhh okay I don't know enough about windows to help with that, is there a common way to do self update on windows?

Edit: a quick google search tells me others get around this by renaming their currently running program so there is no name collision. Is this something that is easy to do?

@chshersh chshersh added this to the v0.3.0: Auto milestone Sep 22, 2022
@chshersh
Copy link
Owner

I wasn't aware Windows has such a problem 😮

I don't know how to fix it, this requires some investigation. Any help is appreciated! 🙏🏻
But it would be nice to include the fix for this in the next version.

@FrancisMurillo
Copy link
Contributor

Searching for answers, I think trying to support this on Windows might not be feasible without increasing complexity. My suggestion is to warn users on Windows that tool-sync cannot be self-updated and should go thru other means by:

  • Checking if tool-sync is in the configuration then issue an error message
    • Also need to check with tool-sync install
  • [PREFERRED] Whenever tool-sync is about to be downloaded, output a warning message and NOOP instead

@chshersh
Copy link
Owner

So, I've asked about the potential solution on Twitter and, apparently, there's a simple way. Windows doesn't allow updating the executable while it's running. However (and this is weird) it allows renaming it (by moving to a different directory or just changing a name).

So, the simple solution to this problem seems to be following:

  • Copy currently running tool executable into a temporary directory
    • Use std::env::current_exe for getting path to the currently running tool executable
    • Use std::env::temp_dir to get the path to the system temporary directory
    • Rename an executable using fs::rename to store it in the temp dir. This way it'll be automatically deleted by the system after reboot.
      • ⚠️ Renaming doesn't work when doing it between different mount points. But I expect this to be okay on Windows. If not, we can copy and delete (if this is supported)

So, yes, this logic should be only on Windows (conditional compilation) and only for tool-sync because it's a special case.

@chshersh chshersh added the hacktoberfest https://hacktoberfest.com/ label Sep 26, 2022
@binyomen
Copy link

binyomen commented Nov 5, 2022

For what it's worth, here's my understanding of why this happens:

Linux/unix has a concept of "unlinking" rather than "deleting" files. This basically removes the file from the directory tree, but it doesn't delete the file object (inode1) in the kernel or free the file's data on disk until all open file descriptors are closed and hard links are unlinked. Basically each inode has a reference count consisting of all open file descriptors and all hard links (including the hard link you might consider the original path to the file). This means you can unlink the executable file for a running process on unix without the actual inode being deleted yet.

Windows doesn't really have this concept of unlinking. There is FILE_SHARE_DELETE23, which is an attribute you can set on an opened file that does something sort of similar to unix's ref-counted inodes, although I'm not entirely sure of the specifics. It doesn't seem like the loader for .exe and .dll files sets this attribute, and even if it did older versions of NTFS would keep a tombstone around4, preventing you from replacing the file with one with the same name. I'm not entirely sure why the loader doesn't specify FILE_SHARE_DELETE. I know windows uses the actual PE images (the format of .exe, .dll, and .sys files) as page files for text/static data at runtime, but assuming it ref-counts the actual file object I don't think this should provide a reason not to delete them? Unsure.

The reason why renaming works is because you're not actually deleting the underlying file data, you're just moving it to a new location in the directory tree. This doesn't require special permissions since it does not remove the file from disk.

My current workaround is this powershell script:

# tool-sync.ps1

$local:ErrorActionPreference = 'Stop'

$currentToolPath = (Get-Command tool).Source
$newToolDir = "$env:TEMP/$((New-Guid).Guid)"
$newToolPath = "$newToolDir/tool.exe"

mkdir $newToolDir > $null
Copy-Item $currentToolPath $newToolDir
& $newToolPath sync
Remove-Item -Force -Recurse $newToolDir

Footnotes

  1. https://en.wikipedia.org/wiki/Inode

  2. https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew

  3. https://stackoverflow.com/questions/196897/locking-executing-files-windows-does-linux-doesnt-why#comment21196301_196908

  4. https://github.com/golang/go/issues/32088#issuecomment-502850674

@binyomen
Copy link

binyomen commented Nov 5, 2022

Is anybody currently working on this? If not, I'd be happy to take it on.

@binyomen
Copy link

binyomen commented Nov 9, 2022

Actually, I just set this up on Linux for the first time, and self update doesn't seem to be working there either. I'm getting [error] Text file busy (os error 26). My guess is that we're copying files using std::fs::copy, which opens the destination file as writable and writes bytes to it, whereas what we should be doing (I think) is actually removing the destination file and then copying the new version to that location. Is anyone else seeing this? It seems we have a test in CI to validate this, but it runs using cargo run which means it's not actually overwriting the same file that's running.

@MitchellBerend
Copy link
Collaborator

I'll try and test this on my machine, I haven't actually self updated yet so I'm not sure.

@MitchellBerend
Copy link
Collaborator

@binyomen It doesn't actually work for me either. If what you are saying is the case the ci needs to be changed as well so it does not give a false positive.

~/rust/tool-sync on  main [$?] is 📦 v0.2.0 via 🦀 v1.65.0 
➜ ./bin/tool --config tool.toml 
tool-sync 0.2.0
Dmitrii Kovanikov <[email protected]>
A CLI tool to manage other CLI tools

USAGE:
    tool [OPTIONS] <SUBCOMMAND>

OPTIONS:
    -c, --config <FILE>    Sets a path to a configuration file (default: $HOME/.tool.toml)
    -h, --help             Print help information
    -V, --version          Print version information

SUBCOMMANDS:
    default-config    Generate a default .tool.toml file and prints it to std out
    help              Print this message or the help of the given subcommand(s)
    install           Install a tool if it is hardcoded into internal database
    sync              Sync all tools specified in configuration file
~/rust/tool-sync on  main [$?] is 📦 v0.2.0 via 🦀 v1.65.0 
❯ ./bin/tool --config tool.toml sync
🔄  All done!                                                                                                                                                             📦  Estimated total download size: 1.44 MiB
⛔  tool-sync v0.2.0   [error] Text file busy (os error 26)                                                                                                               ✨  Successfully installed 0 tools!
📁  Installation directory: /home/mitchell/rust/tool-sync/bin

@binyomen
Copy link

Sorry for the radio silence on this. I can work on both self update in unix and windows then.

@FrancisMurillo
Copy link
Contributor

FrancisMurillo commented May 19, 2023

A self_replace crate for this task might be able to resolve this now.

@FrancisMurillo FrancisMurillo linked a pull request May 20, 2023 that will close this issue
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working hacktoberfest https://hacktoberfest.com/ help wanted Extra attention is needed 🐧 🍏 🪟 Cross OS support
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants