msys2-runtime
msys2-runtime copied to clipboard
rm: cannot remove: Invalid argument in docker volume folder
Setup
Dockerfile
FROM mcr.microsoft.com/windows/servercore:ltsc2019
RUN powershell iex(iwr -useb https://chocolatey.org/install.ps1)
RUN choco install msys2 -y --params "/NoUpdate"
- Version
> rm --version
rm (GNU coreutils) 8.32
Details
- Which terminal/shell are you running Git from? e.g Bash/CMD/PowerShell/other
CMD
- What commands did you run to trigger this issue?
docker build -t msys-test .
docker run -it --rm -v c:\test msys-test
c:\>c:\tools\msys64\usr\bin\touch a
c:\>c:\tools\msys64\usr\bin\rm a
c:\>c:\tools\msys64\usr\bin\touch test\a
c:\>c:\tools\msys64\usr\bin\rm test\a
/usr/bin/rm: cannot remove 'test\a': Invalid argument
- What did you expect to occur after running these commands?
rm should delete the file
- What actually happened instead?
rm returns an error
How does this succeed c:\tools\msys64\usr\bin\touch test\a
? Was the test
folder created somehow? Also I can not reproduce your actual test case in normal Microsoft's Windows installation.
"test" folder created with docker mount: docker run -it --rm -v c:\test msys-test. Аs you can see, the "touch test\a" command completed successfully.
"test" folder created with docker mount: docker run -it --rm -v c:\test msys-test.
I think this one is the crucial tidbit. In Git for Windows, we figured out that Docker mounts are represented as reparse points that totally look like symbolic links, albeit with a totally bogus target path (at least bogus as far as user space is concerned). The workaround looks like this: https://github.com/git-for-windows/git/commit/91060f3aabcda6a31d2ea053463a5e521a4165d6.
We most likely need to do something similar in the Cygwin runtime (because the MSYS2 runtime is a close fork of the Cygwin runtime, and this issue most likely also affects Cygwin): before "resolving a symbolic link", we need to whether its target path starts with /ContainerMappedDirectories/
. If it does, we need to forget about the item being marked with the IO_REPARSE_TAG_SYMLINK
tag and pretend that it is not a reparse point at all.
I think option MSYS=nonativeinnerlinks
would help, but you'd probably be better off bringing this issue to upstream cygwin.
@github-cygwin If @dscho's analysis is correct this could probably be band-aided pretty easily by checking for that prefix
I think option
MSYS=nonativeinnerlinks
would help, but you'd probably be better off bringing this issue to upstream cygwin.
No luck even after setting this environment variable.
Since the demanding tone of https://github.com/msys2/msys2-runtime/issues/59#issuecomment-943631447 rubs me in all the wrong ways, I will only give a pointer here and refuse to spend any more of my expert time on this ticket: it should be interesting to instrument check_reparse_point_string()
to figure out what target path is seen there, and why it apparently passes that check (it probably shouldn't).
@ZFail Just to rule out the possibility that it could be related to how MSYS bash interprets the string, can you try out the below command & see if it works in your setup?
c:\>c:\tools\msys64\usr\bin\touch test\p
c:\>c:\tools\msys64\usr\bin\rm test\p
Assuming \a is wrongly interpreted by bash??
This is successful strace log, rm t
in ordinary place.
154 87740 [main] rm 1554 _unlink_nt: Trying to delete \??\C:\build2\t, isdir = 0
272 88012 [main] rm 1554 _unlink_nt: \??\C:\build2\t, return status = 0x0
failed trace, in mounted volume.
164 88607 [main] rm 559 _unlink_nt: Trying to delete \??\C:\build\t, isdir = 0
373 88980 [main] rm 559 _unlink_nt: \??\C:\build\t, return status = 0xC000000D
It fails in unlink_nt(), there is a problem that does not related to reparsepoint.
I tried old msys-2.0.dll, that works with mounted volume. I assume that FILE_DISPOSITION_POSIX_SEMANTICS is not supported on mounted volumes.
cygcheck -V
that worked with mounted volume is
cygcheck (msys) 3.1.7
System Checker for Msys
Copyright (C) 1998 - 2020 Cygwin Authors
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
vvv additional report msys2-base-x86_64-20181211.tar.xz works fine. msys2-base-x86_64-20190524.tar.xz has issue.
0xC000000D
That looks like STATUS_INVALID_PARAMETER
.
I assume that FILE_DISPOSITION_POSIX_SEMANTICS is not supported on mounted volumes.
@YO4 you mean to imply that this part of the code fails?
If so, it is inside a conditional testing for:
if (wincap.has_posix_unlink_semantics ()
&& !pc.isremote () && pc.fs_is_ntfs ())
That condition seems to make sense, but maybe it is incomplete? Or maybe something inside the conditional code block needs to be adjusted.
To shed more light into this, it would probably make most sense to instrument the code more. To do that, clone https://github.com/msys2/msys2-runtime/, litter that _unlink_nt()
function with small_printf()
statements e.g. like this:
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index f6ffb1cf61..97bccf818e 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -721,6 +721,7 @@ _unlink_nt (path_conv &pc, bool shareable)
/* First check if we can use POSIX unlink semantics: W10 1709+, local NTFS.
With POSIX unlink semantics the entire job gets MUCH easier and faster.
Just try to do it and if it fails, it fails. */
+small_printf("%s:%d: has posix unlink semantics: %d, is remote: %d, is ntfs: %d\n", __FILE__, __LINE__, (int)wincap.has_posix_unlink_semantics(), (int)pc.isremote(), (int)pc.fs_is_ntfs());
if (wincap.has_posix_unlink_semantics ()
&& !pc.isremote () && pc.fs_is_ntfs ())
{
@@ -752,6 +753,12 @@ _unlink_nt (path_conv &pc, bool shareable)
fdie.Flags |= FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
status = NtSetInformationFile (fh, &io, &fdie, sizeof fdie,
FileDispositionInformationEx);
+small_printf("%s:%d: NtSetInformationFile returned status 0x%lx\n", __FILE__, __LINE__, (DWORD)status);
+if (status == STATUS_INVALID_PARAMETER)
+ {
+ status = STATUS_CANNOT_DELETE;
+small_printf("%s:%d: forced status 0x%lx\n", __FILE__, __LINE__, (DWORD)status);
+ }
/* Restore R/O attribute in case we have multiple hardlinks. */
if (access & FILE_WRITE_ATTRIBUTES)
NtSetAttributesFile (fh, pc.file_attributes ());
Then, commit, push and open a draft Pull Request. This will kick off a build and publish the build artifacts. You can then download these artifacts in a GitHub workflow run using something like this:
- name: use msys2-runtime build artifact
shell: bash
run: |
run_id=4182314287 &&
name=install &&
curl -L https://api.github.com/repos/msys2/msys2-runtime/actions/runs/$run_id/artifacts |
jq -r '.artifacts[] | select(.name | test("'$name'")) | [.name, .archive_download_url] | @tsv' |
tr -d '\r' |
while read name url
do
echo "$name"
curl -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" \
-#sLo /tmp/"$name".zip "$url" &&
unzip -q -d / /tmp/"$name".zip usr/bin/msys-2.0.dll
done
You will most likely have to play variations on this theme because you will need to get this msys-2.0.dll
into the Docker container.
tested on my local environment
../../.././winsup/cygwin/syscalls.cc:696: has posix unlink semantics: 1, is remote: 0, is ntfs: 1
../../.././winsup/cygwin/syscalls.cc:728: NtSetInformationFile returned status 0xC000000D
../../.././winsup/cygwin/syscalls.cc:732: forced status 0xC0000121
and unlink success.
Another problem appeared. Is this a related issue?
# touch t && mv t u
mv: cannot move 't' to a subdirectory of itself, 'u'
- in windows container, bind mounted folder
- syscalls.cc unlink patch applied
- in ordinal folder, mv succeeded, although in a container.
strace mv t u
clipped report
msys-2.0.dll is from msys2-base-x86_64-20230127.tar.xz
264 67051 [main] mv 978 normalize_posix_path: src /c/build/t
128 67179 [main] mv 978 normalize_posix_path: /c/build/t = normalize_posix_path (/c/build/t)
135 67314 [main] mv 978 mount_info::conv_to_win32_path: conv_to_win32_path (/c/build/t)
118 67432 [main] mv 978 mount_info::conv_to_win32_path: iscygdrive (/c/build/t) mount_table->cygdrive /
115 67547 [main] mv 978 mount_info::cygdrive_win32_path: src '/c/build/t', dst 'C:\build\t'
115 67662 [main] mv 978 mount_info::conv_to_win32_path: cygdrive_win32_path (/c/build/t)
115 67777 [main] mv 978 mount_info::conv_to_win32_path: src_path /c/build/t, dst C:\build\t, flags 0x6020, rc 0
320 68097 [main] mv 978 symlink_info::check: 0x0 = NtCreateFile (\??\C:\build\t)
251 68348 [main] mv 978 symlink_info::check: not a symlink
215 68563 [main] mv 978 symlink_info::check: 0 = symlink.check(C:\build\t, 0x7FFFFB250) (mount_flags 0x6020, path_flags 0x0)
141 68704 [main] mv 978 path_conv::check: this->path(C:\build\t), has_acls(1)
124 68828 [main] mv 978 normalize_posix_path: src /c/build/u
125 68953 [main] mv 978 normalize_posix_path: /c/build/u = normalize_posix_path (/c/build/u)
122 69075 [main] mv 978 mount_info::conv_to_win32_path: conv_to_win32_path (/c/build/u)
117 69192 [main] mv 978 mount_info::conv_to_win32_path: iscygdrive (/c/build/u) mount_table->cygdrive /
117 69309 [main] mv 978 mount_info::cygdrive_win32_path: src '/c/build/u', dst 'C:\build\u'
116 69425 [main] mv 978 mount_info::conv_to_win32_path: cygdrive_win32_path (/c/build/u)
115 69540 [main] mv 978 mount_info::conv_to_win32_path: src_path /c/build/u, dst C:\build\u, flags 0x6020, rc 0
220 69760 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u)
134 69894 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u)
200 70094 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.exe)
131 70225 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.exe)
192 70417 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.lnk)
143 70560 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.lnk)
190 70750 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.exe.lnk)
131 70881 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.exe.lnk)
130 71011 [main] mv 978 symlink_info::check: 0 = symlink.check(C:\build\u, 0x7FFFFB250) (mount_flags 0x6020, path_flags 0x0)
121 71132 [main] mv 978 mount_info::conv_to_win32_path: conv_to_win32_path (/c/build)
116 71248 [main] mv 978 mount_info::conv_to_win32_path: iscygdrive (/c/build) mount_table->cygdrive /
114 71362 [main] mv 978 mount_info::cygdrive_win32_path: src '/c/build', dst 'C:\build'
116 71478 [main] mv 978 mount_info::conv_to_win32_path: cygdrive_win32_path (/c/build)
114 71592 [main] mv 978 mount_info::conv_to_win32_path: src_path /c/build, dst C:\build, flags 0x6020, rc 0
196 71788 [main] mv 978 symlink_info::check: 0x0 = NtCreateFile (\??\C:\build)
180 71968 [main] mv 978 symlink_info::check: not a symlink
220 72188 [main] mv 978 symlink_info::check: 0 = symlink.check(C:\build, 0x7FFFFB250) (mount_flags 0x6020, path_flags 0x0)
135 72323 [main] mv 978 path_conv::check: this->path(C:\build\u), has_acls(1)
627 72950 [main] mv 978 seterrno_from_nt_status: /c/S/msys2-runtime/src/msys2-runtime/winsup/cygwin/syscalls.cc:2683 status 0xC000000D -> windows error 87
137 73087 [main] mv 978 geterrno_from_win_error: windows error 87 == errno 22
171 73258 [main] mv 978 rename2: -1 = rename(/c/build/t, /c/build/u), errno 22
119 73377 [main] mv 978 normalize_posix_path: src /c/build/u
113 73490 [main] mv 978 normalize_posix_path: /c/build/u = normalize_posix_path (/c/build/u)
114 73604 [main] mv 978 mount_info::conv_to_win32_path: conv_to_win32_path (/c/build/u)
114 73718 [main] mv 978 mount_info::conv_to_win32_path: iscygdrive (/c/build/u) mount_table->cygdrive /
114 73832 [main] mv 978 mount_info::cygdrive_win32_path: src '/c/build/u', dst 'C:\build\u'
114 73946 [main] mv 978 mount_info::conv_to_win32_path: cygdrive_win32_path (/c/build/u)
117 74063 [main] mv 978 mount_info::conv_to_win32_path: src_path /c/build/u, dst C:\build\u, flags 0x6020, rc 0
140 74203 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u)
130 74333 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u)
151 74484 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.exe)
129 74613 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.exe)
145 74758 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.lnk)
128 74886 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.lnk)
151 75037 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.exe.lnk)
130 75167 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.exe.lnk)
128 75295 [main] mv 978 symlink_info::check: 0 = symlink.check(C:\build\u, 0x7FFFFB6B0) (mount_flags 0x6020, path_flags 0x0)
114 75409 [main] mv 978 mount_info::conv_to_win32_path: conv_to_win32_path (/c/build)
114 75523 [main] mv 978 mount_info::conv_to_win32_path: iscygdrive (/c/build) mount_table->cygdrive /
115 75638 [main] mv 978 mount_info::cygdrive_win32_path: src '/c/build', dst 'C:\build'
114 75752 [main] mv 978 mount_info::conv_to_win32_path: cygdrive_win32_path (/c/build)
114 75866 [main] mv 978 mount_info::conv_to_win32_path: src_path /c/build, dst C:\build, flags 0x6020, rc 0
197 76063 [main] mv 978 symlink_info::check: 0x0 = NtCreateFile (\??\C:\build)
173 76236 [main] mv 978 symlink_info::check: not a symlink
164 76400 [main] mv 978 symlink_info::check: 0 = symlink.check(C:\build, 0x7FFFFB6B0) (mount_flags 0x6020, path_flags 0x0)
131 76531 [main] mv 978 path_conv::check: this->path(C:\build\u), has_acls(1)
116 76647 [main] mv 978 __set_errno: int stat_worker(path_conv&, stat*):1996 setting errno 2
115 76762 [main] mv 978 stat_worker: -1 = (\??\C:\build\u,0x7FFFFC9F0)
128 76890 [main] mv 978 normalize_posix_path: src /c/build/t
114 77004 [main] mv 978 normalize_posix_path: /c/build/t = normalize_posix_path (/c/build/t)
114 77118 [main] mv 978 mount_info::conv_to_win32_path: conv_to_win32_path (/c/build/t)
114 77232 [main] mv 978 mount_info::conv_to_win32_path: iscygdrive (/c/build/t) mount_table->cygdrive /
114 77346 [main] mv 978 mount_info::cygdrive_win32_path: src '/c/build/t', dst 'C:\build\t'
114 77460 [main] mv 978 mount_info::conv_to_win32_path: cygdrive_win32_path (/c/build/t)
114 77574 [main] mv 978 mount_info::conv_to_win32_path: src_path /c/build/t, dst C:\build\t, flags 0x6020, rc 0
190 77764 [main] mv 978 symlink_info::check: 0x0 = NtCreateFile (\??\C:\build\t)
169 77933 [main] mv 978 symlink_info::check: not a symlink
213 78146 [main] mv 978 symlink_info::check: 0 = symlink.check(C:\build\t, 0x7FFFFB210) (mount_flags 0x6020, path_flags 0x0)
122 78268 [main] mv 978 path_conv::check: this->path(C:\build\t), has_acls(1)
116 78384 [main] mv 978 normalize_posix_path: src /c/build/u
113 78497 [main] mv 978 normalize_posix_path: /c/build/u = normalize_posix_path (/c/build/u)
124 78621 [main] mv 978 mount_info::conv_to_win32_path: conv_to_win32_path (/c/build/u)
114 78735 [main] mv 978 mount_info::conv_to_win32_path: iscygdrive (/c/build/u) mount_table->cygdrive /
114 78849 [main] mv 978 mount_info::cygdrive_win32_path: src '/c/build/u', dst 'C:\build\u'
115 78964 [main] mv 978 mount_info::conv_to_win32_path: cygdrive_win32_path (/c/build/u)
113 79077 [main] mv 978 mount_info::conv_to_win32_path: src_path /c/build/u, dst C:\build\u, flags 0x6020, rc 0
136 79213 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u)
130 79343 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u)
145 79488 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.exe)
128 79616 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.exe)
147 79763 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.lnk)
128 79891 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.lnk)
151 80042 [main] mv 978 symlink_info::check: 0xC0000034 = NtCreateFile (\??\C:\build\u.exe.lnk)
168 80210 [main] mv 978 symlink_info::check: 0xC0000034 = NtQueryInformationFile (\??\C:\build\u.exe.lnk)
135 80345 [main] mv 978 symlink_info::check: 0 = symlink.check(C:\build\u, 0x7FFFFB210) (mount_flags 0x6020, path_flags 0x0)
115 80460 [main] mv 978 mount_info::conv_to_win32_path: conv_to_win32_path (/c/build)
116 80576 [main] mv 978 mount_info::conv_to_win32_path: iscygdrive (/c/build) mount_table->cygdrive /
115 80691 [main] mv 978 mount_info::cygdrive_win32_path: src '/c/build', dst 'C:\build'
114 80805 [main] mv 978 mount_info::conv_to_win32_path: cygdrive_win32_path (/c/build)
114 80919 [main] mv 978 mount_info::conv_to_win32_path: src_path /c/build, dst C:\build, flags 0x6020, rc 0
243 81162 [main] mv 978 symlink_info::check: 0x0 = NtCreateFile (\??\C:\build)
203 81365 [main] mv 978 symlink_info::check: not a symlink
173 81538 [main] mv 978 symlink_info::check: 0 = symlink.check(C:\build, 0x7FFFFB210) (mount_flags 0x6020, path_flags 0x0)
117 81655 [main] mv 978 path_conv::check: this->path(C:\build\u), has_acls(1)
544 82199 [main] mv 978 seterrno_from_nt_status: /c/S/msys2-runtime/src/msys2-runtime/winsup/cygwin/syscalls.cc:2683 status 0xC000000D -> windows error 87
122 82321 [main] mv 978 geterrno_from_win_error: windows error 87 == errno 22
167 82488 [main] mv 978 rename2: -1 = rename(/c/build/t, /c/build/u), errno 22
related to FILE_RENAME_POSIX_SEMANTICS ?
tested on my local environment
../../.././winsup/cygwin/syscalls.cc:696: has posix unlink semantics: 1, is remote: 0, is ntfs: 1 ../../.././winsup/cygwin/syscalls.cc:728: NtSetInformationFile returned status 0xC000000D ../../.././winsup/cygwin/syscalls.cc:732: forced status 0xC0000121
and unlink success.
Awesome. @YO4 could you prepare a proper commit (leaving out the small_printf()
statements, of course) and open a PR?
# touch t && mv t u mv: cannot move 't' to a subdirectory of itself, 'u'
I lost the context, could you describe what type of entities t
and u
are, especially in relation to the mapped Docker volume?
related to FILE_RENAME_POSIX_SEMANTICS ?
Quite possibly ;-) We know it's a problem for _unlink_nt()
, therefore it would make sense that it may need special handling in rename2()
, too.
However, 0xC0000034 corresponds to STATUS_OBJECT_NAME_NOT_FOUND
... so it might actually be a different issue.
The best advice I can give is to again litter the code with small_printf()
statements that are designed to shed more light into where an unhandled error condition occurs that needs to be handled (much like STATUS_INVALID_PARAMETER
in _unlink_nt()
).
Sorry to keep you waiting. I apologize for the inconvenience. But I wanted to clarify the conditions under which the problem occurs.
And I believe I have confirmed that posix_semantics in bind mount returns an error in the hyper-v container. You can see that in https://ci.appveyor.com/project/YO4/test-msys2-in-container
The runner provided by Github Actions could not be used for this purpose because it cannot run the container using hyper-v isolation so I used Appveyor in this case. The ltsc2016 image is not affected by this issue because it does not support posix_semantics. The Visual Studio 2017 image works on ltsc2016, but it did not seem to be able to run the hyper-v container.
I was working on windows 10 that has default hyper-v isolation, so I was affected by this issue. But on Windows Server, process isolation is default, so the issue is hard to notice in some environments.
I've done some testing locally, and found forcing use_posix_semantics = 0
in rename2
makes the rename calls successful.
The output from NtSetInformationFile
is STATUS_INVALID_PARAMETER
otherwise.
strace for failed mv: https://pastebin.com/QtP0w4QH
strace for working mv: https://pastebin.com/PASMmigz
I can't see any file attributes that would signify why STATUS_INVALID_PARAMETER
is being generated, or that could be used to turn off use_posix_semantics
for bind mounts. The only difference seems to be in oldpc.status.fs.flags
:
ordinary file: 0x1c706df bind mount file: 0x2c706ff
The differing flags being:
#define FILE_VOLUME_QUOTAS 0x00000020 // bind
#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 // normal
#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 // bind
Given NtSetInformationFile
is returning the error, but there doesn't seem to be a problem with the call, it seems like a bug in the lower level calls.
There are related discussions: https://github.com/microsoft/opengcs/issues/337 and https://github.com/moby/moby/issues/39922 though opengcs has now moved to https://github.com/microsoft/hcsshim
Maybe I missed it while trying to read through all comments: As I see this error both when running git bash and in cywin I wonder whether a ticket for cygwin exists already in this regard?
@schwaerz the fixes have been in cygwin/master for a while, but have only gone into version 3.5+. The current release of MSYS2 is from 3.4.10.
@AJDurant You do not happen to know the cygwin package (version) that includes the fix?
@schwaerz in the source code, the fixes are in both 3.5.0 and 3.5.1 (the current latest). I'm not a cygwin user, so I haven't tested it.
I also moved all out workflows to Win11/Server2022 to allow process isolation for containers, and avoid this issue.
Thanks. I can confirm that file deletion works when using cygwin 3.5.1 👍