Mercurial > hg
changeset 51806:95cdc01f313d
sparse: reliably avoid writing to store without a lock
With the code as written before this patch we can still end up writing to
store in `debugsparse`. Obviously we'll write to it if by accident a store
requirement is modified, but more importantly we write to it if another
concurrent transaction modifies the requirements file on disk.
We can't rule this out since we're not holding the store lock,
so it's better to explicitly pass a permission to write instead
of inferring it based on file contents.
author | Arseniy Alekseyev <aalekseyev@janestreet.com> |
---|---|
date | Fri, 16 Aug 2024 11:12:19 +0100 |
parents | 0d7ccb163b4f |
children | 0b2c978f595f |
files | mercurial/scmutil.py mercurial/sparse.py |
diffstat | 2 files changed, 10 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/scmutil.py Thu Aug 15 13:52:14 2024 +0100 +++ b/mercurial/scmutil.py Fri Aug 16 11:12:19 2024 +0100 @@ -1668,7 +1668,7 @@ return requirementsmod.TREEMANIFEST_REQUIREMENT in repo.requirements -def writereporequirements(repo, requirements=None) -> None: +def writereporequirements(repo, requirements=None, maywritestore=True) -> None: """writes requirements for the repo Requirements are written to .hg/requires and .hg/store/requires based @@ -1681,10 +1681,11 @@ if wcreq is not None: writerequires(repo.vfs, wcreq) if storereq is not None: - writerequires(repo.svfs, storereq) + writerequires(repo.svfs, storereq, maywrite=maywritestore) elif repo.ui.configbool(b'format', b'usestore'): # only remove store requires if we are using store - repo.svfs.tryunlink(b'requires') + if maywritestore: + repo.svfs.tryunlink(b'requires') def readrequires(vfs, allowmissing): @@ -1701,9 +1702,11 @@ return set(read(b'requires').splitlines()) -def writerequires(opener, requirements) -> None: +def writerequires(opener, requirements, maywrite=True) -> None: on_disk = readrequires(opener, True) if not (on_disk == set(requirements)): + if not maywrite: + raise error.Abort(_(b"store requirements are not as expected")) with opener(b'requires', b'w', atomictemp=True) as fp: for r in sorted(requirements): fp.write(b"%s\n" % r)
--- a/mercurial/sparse.py Thu Aug 15 13:52:14 2024 +0100 +++ b/mercurial/sparse.py Fri Aug 16 11:12:19 2024 +0100 @@ -632,10 +632,10 @@ if requirements.SPARSE_REQUIREMENT in oldrequires and removing: repo.requirements.discard(requirements.SPARSE_REQUIREMENT) - scmutil.writereporequirements(repo) + scmutil.writereporequirements(repo, maywritestore=False) elif requirements.SPARSE_REQUIREMENT not in oldrequires: repo.requirements.add(requirements.SPARSE_REQUIREMENT) - scmutil.writereporequirements(repo) + scmutil.writereporequirements(repo, maywritestore=False) try: writeconfig(repo, includes, excludes, profiles) @@ -644,7 +644,7 @@ if repo.requirements != oldrequires: repo.requirements.clear() repo.requirements |= oldrequires - scmutil.writereporequirements(repo) + scmutil.writereporequirements(repo, maywritestore=False) writeconfig(repo, oldincludes, oldexcludes, oldprofiles) raise