comparison mercurial/dirstate.py @ 42456:87a34c767384

merge: fix race that could cause wrong size in dirstate The problem is that hg merge/update/etc work the following way: 1. figure out what files to update 2. apply the update to disk 3. apply the update to in-memory dirstate 4. write dirstate where step3 looks at the filesystem and assumes it sees the result of step2. If a file is changed between step2 and step3, step3 will record incorrect information in the dirstate. I avoid this by passing the size step3 needs directly from step2, for the common path (not implemented for change/delete conflicts for instance). I didn't fix the same race for the exec bit for now, because it's less likely to be problematic and I had trouble due to the fact that the dirstate stores the permissions differently from the manifest (st_mode vs '' 'l' 'x'), in combination with tests that pretend that symlinks are not supported. However, I moved the lstat from step3 to step2, which should tighten the race window markedly, both for the exec bit and for the mtime. Differential Revision: https://phab.mercurial-scm.org/D6475
author Valentin Gatien-Baron <valentin.gatienbaron@gmail.com>
date Mon, 27 May 2019 16:55:46 -0400
parents a3a8887e4426
children 760a7851e9ba
comparison
equal deleted inserted replaced
42455:5ca136bbd3f6 42456:87a34c767384
389 (pycompat.bytestr(d), pycompat.bytestr(f))) 389 (pycompat.bytestr(d), pycompat.bytestr(f)))
390 self._dirty = True 390 self._dirty = True
391 self._updatedfiles.add(f) 391 self._updatedfiles.add(f)
392 self._map.addfile(f, oldstate, state, mode, size, mtime) 392 self._map.addfile(f, oldstate, state, mode, size, mtime)
393 393
394 def normal(self, f): 394 def normal(self, f, parentfiledata=None):
395 '''Mark a file normal and clean.''' 395 '''Mark a file normal and clean.
396 s = os.lstat(self._join(f)) 396
397 mtime = s[stat.ST_MTIME] 397 parentfiledata: (mode, size, mtime) of the clean file
398 self._addpath(f, 'n', s.st_mode, 398
399 s.st_size & _rangemask, mtime & _rangemask) 399 parentfiledata should be computed from memory (for mode,
400 size), as or close as possible from the point where we
401 determined the file was clean, to limit the risk of the
402 file having been changed by an external process between the
403 moment where the file was determined to be clean and now.'''
404 if parentfiledata:
405 (mode, size, mtime) = parentfiledata
406 else:
407 s = os.lstat(self._join(f))
408 mode = s.st_mode
409 size = s.st_size
410 mtime = s[stat.ST_MTIME]
411 self._addpath(f, 'n', mode, size & _rangemask, mtime & _rangemask)
400 self._map.copymap.pop(f, None) 412 self._map.copymap.pop(f, None)
401 if f in self._map.nonnormalset: 413 if f in self._map.nonnormalset:
402 self._map.nonnormalset.remove(f) 414 self._map.nonnormalset.remove(f)
403 if mtime > self._lastnormaltime: 415 if mtime > self._lastnormaltime:
404 # Remember the most recent modification timeslot for status(), 416 # Remember the most recent modification timeslot for status(),