Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/posix.py @ 30455:b324b4e431e5
posix: give checkexec a fast path; keep the check files and test read only
Before, Mercurial would create a new temporary file every time, stat it, change
its exec mode, stat it again, and delete it. Most of this dance was done to
handle the rare and not-so-essential case of VFAT mounts on unix. The cost of
that was paid by the much more common and important case of using normal file
systems.
Instead, try to create and preserve .hg/cache/checkisexec and
.hg/cache/checknoexec with and without exec flag set. If the files exist and
have correct exec flags set, we can conclude that that file system supports the
exec flag. Best case, the whole exec check can thus be done with two stat
calls. Worst case, we delete the wrong files and check as usual. That will be
because temporary loss of exec bit or on file systems without support for the
exec bit. In that case we check as we did before, with the additional overhead
of one extra stat call.
It is possible that this different test algorithm in some cases on odd file
systems will give different behaviour. Again, I think it will be rare and
special cases and I think it is worth the risk.
test-clone.t happens to show the situation where checkisexec is left behind
from the old style check, while checknoexec only will be created next time a
exec check will be performed.
author | Mads Kiilerich <madski@unity3d.com> |
---|---|
date | Wed, 14 Jan 2015 01:15:26 +0100 |
parents | 1ce4c2062ab0 |
children | 0d87b1caed92 |
comparison
equal
deleted
inserted
replaced
30454:1ce4c2062ab0 | 30455:b324b4e431e5 |
---|---|
159 # with exec bit on. | 159 # with exec bit on. |
160 | 160 |
161 try: | 161 try: |
162 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | 162 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
163 cachedir = os.path.join(path, '.hg', 'cache') | 163 cachedir = os.path.join(path, '.hg', 'cache') |
164 if not os.path.isdir(cachedir): | 164 if os.path.isdir(cachedir): |
165 cachedir = path | 165 checkisexec = os.path.join(cachedir, 'checkisexec') |
166 fh, fn = tempfile.mkstemp(dir=cachedir, prefix='hg-checkexec-') | 166 checknoexec = os.path.join(cachedir, 'checknoexec') |
167 | |
168 try: | |
169 m = os.stat(checkisexec).st_mode | |
170 except OSError as e: | |
171 if e.errno != errno.ENOENT: | |
172 raise | |
173 # checkisexec does not exist - fall through ... | |
174 else: | |
175 # checkisexec exists, check if it actually is exec | |
176 if m & EXECFLAGS != 0: | |
177 # ensure checkisexec exists, check it isn't exec | |
178 try: | |
179 m = os.stat(checknoexec).st_mode | |
180 except OSError as e: | |
181 if e.errno != errno.ENOENT: | |
182 raise | |
183 file(checknoexec, 'w').close() # might fail | |
184 m = os.stat(checknoexec).st_mode | |
185 if m & EXECFLAGS == 0: | |
186 # check-exec is exec and check-no-exec is not exec | |
187 return True | |
188 # checknoexec exists but is exec - delete it | |
189 os.unlink(checknoexec) | |
190 # checkisexec exists but is not exec - delete it | |
191 os.unlink(checkisexec) | |
192 | |
193 # check using one file, leave it as checkisexec | |
194 checkdir = cachedir | |
195 else: | |
196 # check directly in path and don't leave checkisexec behind | |
197 checkdir = path | |
198 checkisexec = None | |
199 fh, fn = tempfile.mkstemp(dir=checkdir, prefix='hg-checkexec-') | |
167 try: | 200 try: |
168 os.close(fh) | 201 os.close(fh) |
169 m = os.stat(fn).st_mode | 202 m = os.stat(fn).st_mode |
170 if m & EXECFLAGS: | 203 if m & EXECFLAGS == 0: |
171 return False | 204 os.chmod(fn, m & 0o777 | EXECFLAGS) |
172 os.chmod(fn, m & 0o777 | EXECFLAGS) | 205 if os.stat(fn).st_mode & EXECFLAGS != 0: |
173 return os.stat(fn).st_mode & EXECFLAGS | 206 if checkisexec is not None: |
207 os.rename(fn, checkisexec) | |
208 fn = None | |
209 return True | |
174 finally: | 210 finally: |
175 os.unlink(fn) | 211 if fn is not None: |
212 os.unlink(fn) | |
176 except (IOError, OSError): | 213 except (IOError, OSError): |
177 # we don't care, the user probably won't be able to commit anyway | 214 # we don't care, the user probably won't be able to commit anyway |
178 return False | 215 return False |
179 | 216 |
180 def checklink(path): | 217 def checklink(path): |