caches: preserve permissions of top-level .hg
When using hg on a shared filesystem on UNIX, cache files normally
inherit the permissions of the .hg directory. This is most commonly used
to ensure everything is writable by all users. The sqlite3 cache files
don't have a way to directly set the permission, so check if a special
mode is necessary and try to apply them to newly created database files.
--- a/hgext3rd/evolve/obsdiscovery.py Sat May 02 23:31:31 2020 +0800
+++ b/hgext3rd/evolve/obsdiscovery.py Sun May 03 01:01:19 2020 +0200
@@ -17,6 +17,7 @@
import hashlib
import heapq
+import os
import sqlite3
import struct
import weakref
@@ -30,6 +31,7 @@
node,
obsolete,
scmutil,
+ store,
util,
)
from mercurial.i18n import _
@@ -343,6 +345,7 @@
# cache status
self._ondiskcachekey = None
self._data = {}
+ self._createmode = store._calcmode(self._vfs)
def clear(self, reset=False):
super(_obshashcache, self).clear(reset=reset)
@@ -490,12 +493,19 @@
def _db(self):
try:
- util.makedirs(self._vfs.dirname(self._path))
+ util.makedirs(self._vfs.dirname(self._path), self._createmode)
except OSError:
return None
+ if self._createmode is not None:
+ pre_existed = os.access(self._path, os.R_OK)
con = sqlite3.connect(encoding.strfromlocal(self._path), timeout=30,
isolation_level=r"IMMEDIATE")
con.text_factory = bytes
+ if self._createmode is not None and not pre_existed:
+ try:
+ os.chmod(self._path, self._createmode & 0o666)
+ except OSError:
+ pass
return con
@util.propertycache
@@ -599,6 +609,7 @@
self._new.clear()
self._valid = True
self._ondiskcachekey = self._cachekey
+
@eh.wrapfunction(obsolete.obsstore, '_addmarkers')
def _addmarkers(orig, obsstore, *args, **kwargs):
obsstore.rangeobshashcache.clear()
--- a/hgext3rd/evolve/stablerangecache.py Sat May 02 23:31:31 2020 +0800
+++ b/hgext3rd/evolve/stablerangecache.py Sun May 03 01:01:19 2020 +0200
@@ -9,6 +9,7 @@
import abc
import heapq
+import os
import random
import sqlite3
import time
@@ -19,6 +20,7 @@
error,
localrepo,
node as nodemod,
+ store,
util,
)
@@ -185,6 +187,7 @@
self._ondisktiprev = None
self._ondisktipnode = None
self._unsavedsubranges = {}
+ self._createmode = store._calcmode(self._vfs)
def contains(self, repo, revs):
con = self._con
@@ -240,12 +243,19 @@
def _db(self):
try:
- util.makedirs(self._vfs.dirname(self._path))
+ util.makedirs(self._vfs.dirname(self._path), self._createmode)
except OSError:
return None
+ if self._createmode is not None:
+ pre_existed = os.access(self._path, os.R_OK)
con = sqlite3.connect(encoding.strfromlocal(self._path), timeout=30,
isolation_level=r"IMMEDIATE")
con.text_factory = bytes
+ if self._createmode is not None and not pre_existed:
+ try:
+ os.chmod(self._path, self._createmode & 0o666)
+ except OSError:
+ pass
return con
@util.propertycache
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-sqlite3-permissions.t Sun May 03 01:01:19 2020 +0200
@@ -0,0 +1,30 @@
+#require unix-permissions
+
+Test that sqlite3 cache files inherit the permissions of the .hg
+directory like other cache files.
+
+ $ . $TESTDIR/testlib/common.sh
+
+ $ cat << EOF >> $HGRCPATH
+ > [extensions]
+ > evolve =
+ > EOF
+ $ hg init test
+ $ cd test
+ $ chmod 700 .hg
+ $ hg debugupdatecache
+ $ ls -l .hg/cache/evoext_*.sqlite
+ -rw------- * .hg/cache/evoext_obshashrange_v2.sqlite (glob)
+ -rw------- * .hg/cache/evoext_stablerange_v2.sqlite (glob)
+ $ rm -r .hg/cache
+ $ chmod 770 .hg
+ $ hg debugupdatecache
+ $ ls -l .hg/cache/evoext_*.sqlite
+ -rw-rw---- * .hg/cache/evoext_obshashrange_v2.sqlite (glob)
+ -rw-rw---- * .hg/cache/evoext_stablerange_v2.sqlite (glob)
+ $ rm -r .hg/cache
+ $ chmod 774 .hg
+ $ hg debugupdatecache
+ $ ls -l .hg/cache/evoext_*.sqlite
+ -rw-rw-r-- * .hg/cache/evoext_obshashrange_v2.sqlite (glob)
+ -rw-rw-r-- * .hg/cache/evoext_stablerange_v2.sqlite (glob)