vim / vim

The official Vim repository
https://www.vim.org
Vim License
36.22k stars 5.41k forks source link

Option to disable writing extended attributes #15765

Open defr0std opened 1 day ago

defr0std commented 1 day ago

Cross-posting this request from https://github.com/neovim/neovim/issues/30562. The issue seems to be the same in vim 9.1+ and Neovim 0.10+.

Problem

Hi,

I am working for a large software company. Our codebase is remote, and mounted locally on the workstations. We also have various automation tools which pick up changes to the files and perform build, test, deployment, etc.

Ever since Neovim 0.10+, there appeared quite annoying behavior in this workflow. When you save the file in Neovim, our automation tools first pick up a change to the file having no contents (as if the file was written completely empty), the build/test fails, and only on the second attempt it succeeds.

I have debugged this problem and traced the root cause to writing extended attributes from Neovim. If I run strace on Neovim 0.9+ vs any commit after 0.10+, then I see the difference of making various xattr calls in Neovim 0.10.+ (see strace output attached below). Since there are many attributes to set, the file write operation is also significantly slower (although that is still tolerable).

I have also verified in a local build that if I remove the calls to os_copy_xattr, then the issue disappears.

Do you think it would be possible to add a configuration option to disable the handling of extended attributes?

Here is the output of strace -e trace=file --decode-fds=path -p xxx showing a single ":w" call. Note that file names and attribute names are masked. See the block of xattr calls in the middle of the trace.

# These calls are 
statx(AT_FDCWD</masked>, "/masked/filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14205, ...}) = 0
statx(AT_FDCWD</masked>, "/masked/filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14205, ...}) = 0
statx(AT_FDCWD</masked>, "/masked/filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14205, ...}) = 0
access("/masked/filename.cc", W_OK) = 0
statx(AT_FDCWD</masked>, "/masked/filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14205, ...}) = 0
statx(AT_FDCWD</masked>, "filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14205, ...}) = 0
access("filename.cc", W_OK) = 0
statx(AT_FDCWD</masked>, "filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14205, ...}) = 0
statx(AT_FDCWD</masked>, "filename.cc", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14205, ...}) = 0
statx(AT_FDCWD</masked>, "masked/4913", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, 0x7ffd0fa2f500) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD</masked>, "masked/4913", O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW|O_CLOEXEC, 0100664) = 16</masked/masked/4913>
statx(AT_FDCWD</masked>, "masked/4913", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=0, ...}) = 0
unlink("masked/4913") = 0
statx(AT_FDCWD</masked>, "filename.cc~", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7ffd0fa2f6e0) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD</masked>, "filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14205, ...}) = 0
statx(AT_FDCWD</masked>, "filename.cc~", AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7ffd0fa2f380) = -1 ENOENT (No such file or directory)
unlink("filename.cc~") = -1 ENOENT (No such file or directory)
rename("filename.cc", "filename.cc~") = 0
openat(AT_FDCWD</masked>, "filename.cc", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0664) = 16</masked/filename.cc>

# These calls are made only in Neovim 0.10+
listxattr("filename.cc~", NULL, 0) = 122
listxattr("filename.cc~", "masked"..., 122) = 122
getxattr("filename.cc~", "masked", NULL, 0) = 103
getxattr("filename.cc~", "masked", NULL, 0) = 32
getxattr("filename.cc~", "masked", NULL, 0) = 16
getxattr("filename.cc~", "masked", NULL, 0) = 36
getxattr("filename.cc~", "masked", NULL, 0) = 64
getxattr("filename.cc~", "masked", NULL, 0) = 60
getxattr("filename.cc~", "masked", "masked"..., 103) = 103
setxattr("filename.cc", "masked", "masked"..., 103, 0) = -1 EOPNOTSUPP (Operation not supported)
getxattr("filename.cc~", "masked", "...", 103) = 32
setxattr("filename.cc", "masked", "...", 32, 0) = -1 EOPNOTSUPP (Operation not supported)
getxattr("filename.cc~", "masked", "...", 103) = 16
setxattr("filename.cc", "masked", "...", 16, 0) = -1 EOPNOTSUPP (Operation not supported)
getxattr("filename.cc~", "masked", "..."..., 103) = 36
setxattr("filename.cc", "masked", "..."..., 36, 0) = -1 EOPNOTSUPP (Operation not supported)
getxattr("filename.cc~", "masked", "..."..., 103) = 64
setxattr("filename.cc", "masked", "..."..., 64, 0) = -1 EOPNOTSUPP (Operation not supported)
getxattr("filename.cc~", "masked", "//masked"..., 103) = 60
setxattr("filename.cc", "masked", "//masked"..., 60, 0) = -1 EOPNOTSUPP (Operation not supported)
# End of Neovim xattr calls

statx(AT_FDCWD</masked>, "filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14206, ...}) = 0
statx(AT_FDCWD</masked>, "filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14206, ...}) = 0
chmod("filename.cc", 0100664) = 0
statx(AT_FDCWD</masked>, "/masked/filename.cc", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=14206, ...}) = 0
unlink("filename.cc~") = 0

Expected behavior

Make it possible to disable copying of extended attributes via a configuration option.

chrisbra commented 1 day ago

I guess you can compile your own version using --disable-xattr

defr0std commented 1 day ago

Thanks, yes. This is what I am doing as a workaround after I found the root cause. But long term, it would be nicer if this was a part of the configuration.