[PATCH STABLE V2] opener: do not unlink symlinks on 'w'rite

Adrian Buehlmann adrian at cadifra.com
Thu Nov 25 18:05:00 CST 2010


On 2010-11-25 23:40, Adrian Buehlmann wrote:
> # HG changeset patch
> # User Adrian Buehlmann <adrian at cadifra.com>
> # Date 1290708399 -3600
> # Branch stable
> # Node ID 0162f745b6ba9f422e22c71f03b0c7d91a17cacb
> # Parent  dd24f3e7ca9e68a49fd7f38803e4d98469cad6e4
> opener: do not unlink symlinks on 'w'rite
> 
> The current code replaces symlinks with a normal file on 'w'rite.
> 
> This patch restores the pre 1.7.1 behavior, which preserves symlinks.
> 
> diff --git a/mercurial/util.py b/mercurial/util.py
> --- a/mercurial/util.py
> +++ b/mercurial/util.py
> @@ -890,10 +890,13 @@ class opener(object):
>                  return atomictempfile(f, mode, self.createmode)
>              try:
>                  if 'w' in mode:
> -                    st_mode = os.lstat(f).st_mode & 0777
> -                    os.unlink(f)
> -                    nlink = 0
> -                else:
> +                    st_mode = os.lstat(f).st_mode
> +                    if stat.S_ISLNK(st_mode):
> +                        st_mode = None
> +                    else:
> +                        os.unlink(f)
> +                        nlink = 0
> +                if st_mode is None:
>                      # nlinks() may behave differently for files on Windows
>                      # shares if the file is open.
>                      fd = open(f)
> @@ -913,7 +916,7 @@ class opener(object):
>              if st_mode is None:
>                  self._fixfilemode(f)
>              else:
> -                os.chmod(f, st_mode)
> +                os.chmod(f, st_mode & 0777)
>          return fp
>  
>      def symlink(self, src, dst):

I think we maybe also hit again another problem with the opener's "unlink on
'w'rite optimization" *on Windows*:

windows.py has in the comment on line 293:

        # On windows, rename to existing file is not allowed, so we
        # must delete destination first. But if a file is open, unlink
        # schedules it for delete but does not delete it. Rename
        # happens immediately even for open files, so we rename
        # destination to a temporary name, then delete that. Then
        # rename is safe to do.

So, if a file is open, unlink will schedule it for deletion, but the
deletion is delayed, until the last process which has the file open
closes it.

This is with the win32 posixfile from osutil.c, which does a
CreateFile win32 API call with FILE_SHARE_DELETE (which allows to
delete the file while it is open, but the delete will be delayed).

Plain open() from python is even worse:

  $ python
  Python 2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit (Intel)] on win32
  Type "help", "copyright", "credits" or "license" for more information.
  >>> f = open('b.txt')
  >>> import os
  >>> os.unlink('b.txt')
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  WindowsError: [Error 32] The process cannot access the file because it is being used by another process: 'b.txt'
  >>>

Maybe this could explain the problems jbmohler recently reported on IRC with 1.7.1
when he pushed to a windows share (though he wasn't yet able to present a small repro
case. But it was reproducible in his environment with his repo, as he said).

He reported a missing 00changelog.i with a leaked 00changelog.i.hgtmp after a push
that failed with:

  pushing to \\bog\hg\repos\ebms-21454-test
  searching for changes
  adding changesets
  abort: \\bog\hg\repos\ebms-21454-test\.hg\store\00changelog.i: Access is denied

there was also a 00changelog.d after the push. So the revlog was non-inlined.



More information about the Mercurial-devel mailing list