Mercurial > public > mercurial-scm > hg
comparison mercurial/util.py @ 12938:bf826c0b9537 stable
opener: check hardlink count reporting (issue1866)
The Linux CIFS kernel driver (even in 2.6.36) suffers from a hardlink
count blindness bug (lstat() returning 1 in st_nlink when it is expected
to return >1), which causes repository corruption if Mercurial running
on Linux pushes or commits to a hardlinked repository stored on a Windows
share, if that share is mounted using the CIFS driver.
This patch works around issue1866 and improves the workaround done in
50523b4407f6 to fix issue761, by teaching the opener to lazily execute a
runtime check (new function checknlink) to see if the hardlink count
reported by nlinks() can be trusted.
Since nlinks() is also known to return varying count values (1 or >1)
depending on whether the file is open or not and depending on what client
and server software combination is being used for accessing and serving
the Windows share, we deliberately open the file before calling nlinks() in
order to have a stable precondition. Trying to depend on the precondition
"file closed" would be fragile, as the file could have been opened very
easily somewhere else in the program.
author | Adrian Buehlmann <adrian@cadifra.com> |
---|---|
date | Sun, 07 Nov 2010 18:21:29 +0100 |
parents | 6ff784de7c3a |
children | 9f2ac318b92e 670f4e98276d |
comparison
equal
deleted
inserted
replaced
12937:6ff784de7c3a | 12938:bf826c0b9537 |
---|---|
714 os.unlink(name) | 714 os.unlink(name) |
715 return True | 715 return True |
716 except (OSError, AttributeError): | 716 except (OSError, AttributeError): |
717 return False | 717 return False |
718 | 718 |
719 def checknlink(testfile): | |
720 '''check whether hardlink count reporting works properly''' | |
721 f = testfile + ".hgtmp" | |
722 | |
723 try: | |
724 os_link(testfile, f) | |
725 except OSError, inst: | |
726 if inst.errno == errno.EINVAL: | |
727 # FS doesn't support creating hardlinks | |
728 return True | |
729 return False | |
730 | |
731 try: | |
732 # nlinks() may behave differently for files on Windows shares if | |
733 # the file is open. | |
734 fd = open(f) | |
735 return nlinks(f) > 1 | |
736 finally: | |
737 fd.close() | |
738 os.unlink(f) | |
739 | |
740 return False | |
741 | |
719 def endswithsep(path): | 742 def endswithsep(path): |
720 '''Check path ends with os.sep or os.altsep.''' | 743 '''Check path ends with os.sep or os.altsep.''' |
721 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep) | 744 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep) |
722 | 745 |
723 def splitpath(path): | 746 def splitpath(path): |
838 if audit: | 861 if audit: |
839 self.auditor = path_auditor(base) | 862 self.auditor = path_auditor(base) |
840 else: | 863 else: |
841 self.auditor = always | 864 self.auditor = always |
842 self.createmode = None | 865 self.createmode = None |
866 self._trustnlink = None | |
843 | 867 |
844 @propertycache | 868 @propertycache |
845 def _can_symlink(self): | 869 def _can_symlink(self): |
846 return checklink(self.base) | 870 return checklink(self.base) |
847 | 871 |
871 if 'w' in mode: | 895 if 'w' in mode: |
872 st_mode = os.lstat(f).st_mode & 0777 | 896 st_mode = os.lstat(f).st_mode & 0777 |
873 os.unlink(f) | 897 os.unlink(f) |
874 nlink = 0 | 898 nlink = 0 |
875 else: | 899 else: |
900 # nlinks() may behave differently for files on Windows | |
901 # shares if the file is open. | |
902 fd = open(f) | |
876 nlink = nlinks(f) | 903 nlink = nlinks(f) |
877 except OSError: | 904 fd.close() |
905 except (OSError, IOError): | |
878 nlink = 0 | 906 nlink = 0 |
879 if not os.path.isdir(dirname): | 907 if not os.path.isdir(dirname): |
880 makedirs(dirname, self.createmode) | 908 makedirs(dirname, self.createmode) |
881 if nlink > 1: | 909 if nlink > 0: |
882 rename(mktempcopy(f), f) | 910 if self._trustnlink is None: |
911 self._trustnlink = nlink > 1 or checknlink(f) | |
912 if nlink > 1 or not self._trustnlink: | |
913 rename(mktempcopy(f), f) | |
883 fp = posixfile(f, mode) | 914 fp = posixfile(f, mode) |
884 if nlink == 0: | 915 if nlink == 0: |
885 if st_mode is None: | 916 if st_mode is None: |
886 self._fixfilemode(f) | 917 self._fixfilemode(f) |
887 else: | 918 else: |