Skip to content

mingw: do not clear the read-only attribute in utime() - #6505

Open
azchohfi wants to merge 1 commit into
git-for-windows:mainfrom
azchohfi:mingw-utime-readonly
Open

azchohfi wants to merge 1 commit into
git-for-windows:mainfrom
azchohfi:mingw-utime-readonly

Conversation

@azchohfi

Copy link
Copy Markdown

When Git is about to write an object that it already has, it freshens the existing file with
utime() instead. On Windows that happens a lot: git add of content that was added before, git stash,
rebases that recreate blobs, and so on. Loose objects are read-only, and mingw_utime() clears
FILE_ATTRIBUTE_READONLY before every call and restores it afterwards. Each freshen therefore opens
the object file three times instead of once.

The toggle comes from 852f098 (Windows: fix utime() for read-only files, 2010), when utime()
opened the file O_RDWR. Since 090a308 (2022) it opens the file with CreateFileW() and only
FILE_WRITE_ATTRIBUTES access, which Windows grants on read-only files. This PR tries the open first.
It clears the attribute only if the open is refused with ERROR_ACCESS_DENIED, so a file system that
enforces it keeps working as before.

Effect. Measured with git add -A after rewriting 200 tracked files whose new content was already
in the object store (a 40,000-file repository, Git for Windows v2.55.0.windows.5 with and without this
patch, same toolchain). 20 interleaved runs, median, paired 95% CI:

before after saved
ReFS Dev Drive 103.5 ms 97.4 ms 6.6 ms [3.9, 7.9]
NTFS with real-time antivirus 349.0 ms 316.1 ms 31.6 ms [24.4, 35.1]

Per freshened object, measured with the same Win32 calls outside Git, the cost drops from 159 µs to 63 µs
on the Dev Drive and from 460 µs to 206 µs on NTFS. Each freshen now makes one NtCreateFile, and the two
NtOpenFile / NtSetInformationFile pairs are gone.

Tests. t6501-freshen-objects, t5304-prune, t3700-add and t7063-status-untracked-cache pass. The
freshened objects keep FILE_ATTRIBUTE_READONLY, and their mtime is updated.

The same code is in upstream compat/mingw.c (without long-path support). I can send it to the
mailing list too if you prefer it to go upstream first.

Since 090a308 (t/helper/test-chmtime: update mingw to support
chmtime on directories, 2022-03-02), mingw_utime() opens the file with
CreateFileW() and only FILE_WRITE_ATTRIBUTES access. Windows grants
that access on read-only files; FILE_ATTRIBUTE_READONLY only denies
writing the file's data and deleting it. Clearing the attribute first,
and restoring it afterwards, dates from 852f098 (Windows: fix
utime() for read-only files, 2010-03-30), when the file was opened
O_RDWR, and is no longer needed.

It is not free. Loose objects are read-only, and Git freshens an
existing loose object with utime() whenever it is about to write an
object it already has (for example `git add` of content that was added
before, `git stash`, or a rebase that recreates blobs). Each call made
two extra opens of the object file through SetFileAttributesW(), which
on Windows means two more passes through the file system filter stack
(on-access antivirus scanning in particular).

Open the file first, and fall back to clearing the attribute only if
the open is refused with ERROR_ACCESS_DENIED, so that a file system
that does enforce the attribute for FILE_WRITE_ATTRIBUTES keeps
working as before.

Signed-off-by: Alexandre Zollinger Chohfi <alzollin@microsoft.com>
@azchohfi

Copy link
Copy Markdown
Author

The one failing job, linux-sha256 (ubuntu:rolling), fails in apt-get update before Git is built:

E: Failed to fetch http://security.ubuntu.com/ubuntu/dists/stonking-security/main/binary-amd64/Packages.gz  File has unexpected size (19135 != 19604). Mirror sync in progress?

It did the same on the re-run this morning. The same job passed on other pull requests a little later (the
fixes-from-microsoft-git and integrate-sha1dc-gfw runs), and this change only touches compat/mingw.c.
Could someone re-run the failed job? I can't re-run it from my fork.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant