changeset 50662:12f13b13f414 stable

revlog: avoid possible collision between directory and temporary index Since 6.4, we create a temporary index file to write the split data without overwriting the inline version too early. However, the store encoding does not prevent these new `.i.s` file to collide with a directory with the same name. While the odds for such a collision to happens are fairly low, the collision would prevent Mercurial from working. The store encoding have a mitigation solution in place to prevent such collisions from happening for `.i` and `.d` files, but not for other extensions. We cannot update this encoding scheme to solve the issue since it would diverge from older version of Mercurial. Instead, we create an alternative directory tree dedicated to such files. The use of the `.i` extension combined with store encoding will prevent collisions there.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 08 Jun 2023 14:28:21 +0200
parents 978ffa09910b
children da372c745e0f
files mercurial/revlog.py tests/test-transaction-rollback-on-revlog-split.t tests/test-upgrade-repo.t
diffstat 3 files changed, 36 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/revlog.py	Thu Jun 08 11:08:19 2023 +0200
+++ b/mercurial/revlog.py	Thu Jun 08 14:28:21 2023 +0200
@@ -2026,7 +2026,15 @@
 
         The file will only exist if a splitting operation is in progress, but
         it is always expected at the same location."""
-        return self.radix + b'.i.s'
+        parts = os.path.split(self.radix)
+        if len(parts) > 1:
+            # adds a '-s' prefix to the ``data/` or `meta/` base
+            head = parts[0] + b'-s'
+            return os.path.join(head, *parts[1:])
+        else:
+            # the revlog is stored at the root of the store (changelog or
+            # manifest), no risk of collision.
+            return self.radix + b'.i.s'
 
     def _enforceinlinesize(self, tr, side_write=True):
         """Check if the revlog is too big for inline and convert if so.
--- a/tests/test-transaction-rollback-on-revlog-split.t	Thu Jun 08 11:08:19 2023 +0200
+++ b/tests/test-transaction-rollback-on-revlog-split.t	Thu Jun 08 14:28:21 2023 +0200
@@ -84,6 +84,8 @@
   > Directory_With,Special%Char/Complex_File.babar
   > foo/bar/babar_celeste/foo
   > 1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/f
+  > some_dir/sub_dir/foo_bar
+  > some_dir/sub_dir/foo_bar.i.s/tutu
   > "
   $ for f in $files; do
   >     mkdir -p `dirname $f`
@@ -112,7 +114,7 @@
 Reference size:
   $ f -s file
   file: size=135168
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.d: size=267307
   .hg/store/data/file.i: size=320
 
@@ -140,7 +142,7 @@
 Reference size:
   $ f -s file
   file: size=1024
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.i: size=1174
 
   $ cat > .hg/hgrc <<EOF
@@ -162,11 +164,12 @@
 The inline revlog still exist, but a split version exist next to it
 
   $ cat .hg/store/journal | tr '\0' ' ' | grep '\.s'
-  [1]
-  $ f -s .hg/store/data/file*
+  data/some_dir/sub_dir/foo_bar.i.s/tutu.i 1174
+  data/some_dir/sub_dir/foo_bar.i.s/tutu.d 0
+  $ f -s .hg/store/data*/file*
+  .hg/store/data-s/file: size=320
   .hg/store/data/file.d: size=267307
   .hg/store/data/file.i: size=132395
-  .hg/store/data/file.i.s: size=320
 
 
 The first file.i entry should match the "Reference size" above.
@@ -177,19 +180,19 @@
   $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file
   data/file.i 1174
   data/file.d 0
-  $ cat .hg/store/journal.backupfiles | tr -s '\000' ' ' | tr -s '\00' ' '| grep data/file
+  $ cat .hg/store/journal.backupfiles | tr -s '\000' ' ' | tr -s '\00' ' '| grep 'data.*/file'
    data/file.i data/journal.backup.file.i 0
-   data/file.i.s 0
+   data-s/file 0
 
 recover is rolling the split back, the fncache is still valid
 
   $ hg recover
   rolling back interrupted transaction
   (verify step skipped, run `hg verify` to check your repository content)
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.i: size=1174
   $ hg tip
-  changeset:   1:cc8dfb126534
+  changeset:   1:64b04c8dc267
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -214,7 +217,7 @@
 Reference size:
   $ f -s file
   file: size=1024
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.i: size=1174
 
   $ cat > .hg/hgrc <<EOF
@@ -242,12 +245,12 @@
 
 The inline revlog still exist, but a split version exist next to it
 
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
+  .hg/store/data-s/file: size=320
   .hg/store/data/file.d: size=267307
   .hg/store/data/file.i: size=132395
-  .hg/store/data/file.i.s: size=320
 
-  $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file
+  $ cat .hg/store/journal | tr -s '\000' ' ' | grep 'data.*/file'
   data/file.i 1174
   data/file.d 0
 
@@ -256,10 +259,10 @@
   $ hg recover
   rolling back interrupted transaction
   (verify step skipped, run `hg verify` to check your repository content)
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.i: size=1174
   $ hg tip
-  changeset:   1:cc8dfb126534
+  changeset:   1:64b04c8dc267
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -279,7 +282,7 @@
 Reference size:
   $ f -s file
   file: size=1024
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.i: size=1174
 
   $ cat > .hg/hgrc <<EOF
@@ -307,11 +310,11 @@
 
 The inline revlog was over written on disk
 
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.d: size=267307
   .hg/store/data/file.i: size=320
 
-  $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file
+  $ cat .hg/store/journal | tr -s '\000' ' ' | grep 'data.*/file'
   data/file.i 1174
   data/file.d 0
 
@@ -320,10 +323,10 @@
   $ hg recover
   rolling back interrupted transaction
   (verify step skipped, run `hg verify` to check your repository content)
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.i: size=1174
   $ hg tip
-  changeset:   1:cc8dfb126534
+  changeset:   1:64b04c8dc267
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -357,13 +360,13 @@
 
 The split was rollback
 
-  $ f -s .hg/store/data/file*
+  $ f -s .hg/store/data*/file*
   .hg/store/data/file.d: size=0
   .hg/store/data/file.i: size=1174
 
 
   $ hg tip
-  changeset:   1:cc8dfb126534
+  changeset:   1:64b04c8dc267
   tag:         tip
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
--- a/tests/test-upgrade-repo.t	Thu Jun 08 11:08:19 2023 +0200
+++ b/tests/test-upgrade-repo.t	Thu Jun 08 14:28:21 2023 +0200
@@ -839,6 +839,7 @@
   00changelog.i
   00manifest.i
   data
+  data-s
   fncache
   phaseroots
   requires
@@ -862,6 +863,7 @@
   00changelog.i
   00manifest.i
   data
+  data-s
   fncache
   phaseroots
   requires