Bug 2524 - update loses working copy files on Windows for open files
Summary: update loses working copy files on Windows for open files
Status: RESOLVED FIXED
Alias: None
Product: Mercurial
Classification: Unclassified
Component: Mercurial (show other bugs)
Version: unspecified
Hardware: All All
: normal bug
Assignee: Bugzilla
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-11-27 17:16 UTC by Adrian Buehlmann
Modified: 2012-05-13 05:02 UTC (History)
3 users (show)

See Also:
Python Version: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Adrian Buehlmann 2010-11-27 17:16 UTC
The line os.unlink(f) in util.opener.__call__ fails with an OSError
on Windows, if the file exists and is held open by another process
(e.g. Python's open()).

Which has the effect that the unlink() call is a noop, so the hardlink
is not broken up.

Affected: Mercurial 1.7.1
Comment 1 Adrian Buehlmann 2010-12-01 05:51 UTC
If a file f has been opened for reading by another process with 'posixfile(f)',
calling unlink will send that file into a "scheduled delete" state on Windows.

Scheduled delete has the following characteristics:

  (a) the entry in the directory for f is still kept
  (b) calling 'fd = posixfile(f, 'w')' will raise
        'IOError: [Errno 13] <f>: Access is denied'
  (c) calling 'os.rename(f, f+'.foo')' will raise
        'WindowsError: [Error 5] Access is denied'
  (d) calling 'os.lstat(f)' will raise
        'WindowsError: [Error 5] Access is denied: <f>'

Scheduled delete is left as soon as the other process closes the file.
Comment 2 Adrian Buehlmann 2010-12-02 12:18 UTC
Matters are actually a bit worse. Consider:

(1) Another process opens file f using posixfile(f) (or equivalent).
(2) Mercurial calls fd = opener(f, 'w') on e.g. 'hg update'

As of (now) Mercurial 1.7.2, the os.unlink(f) will send the file into
"scheduled delete" and the subsequent posixfile(f, 'w') fails with
an "Access denied" IOError. But the unlink has already been done.

The mercurial operation is aborted like this:

   $ hg update
   abort: <f>: Access is denied

and the file f Mercurial tried to update is gone. The directory
entry of f will vanish as soon as the other process closes the file.

Adjusting title.
Comment 3 Adrian Buehlmann 2010-12-03 03:37 UTC
(from my posting http://markmail.org/message/kwiciiu5ttmlgytt)

How to reproduce this:

On Windows, in one shell start python and open a working dir file
with posixfile:

  $ 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.
  >>> from mercurial.windows import posixfile
  >>> fd = posixfile('a.txt')
  >>>

then in a second cmd.exe shell instance do:

  $ hg version
  Mercurial Distributed SCM (version 1.7.2)
  (see http://mercurial.selenic.com for more information)
  
  [..]

  $ hg parent -q
  17:e86f3b869e53

  $ hg update 16
  abort: C:\Users\adi\hgrepos\tests\c\a.txt: Access is denied

Now, the directory entry for 'a.txt' still shows up:

  $ dir /w
   Volume in drive C has no label.
   Volume Serial Number is F80E-0A52

   Directory of C:\Users\adi\hgrepos\tests\c

  [.]     [..]    [.hg]   a.txt   b.txt
                 2 File(s)             18 bytes
                 3 Dir(s)  473'414'610'944 bytes free

But the file a.txt is now a ghost in "scheduled delete" state [1].

In that state you can't even read the file ("type" is the Windows equivalent
of "cat" for the Linux fraction):

  $ type a.txt
  Access is denied.

Now, let's close the file handle in the first shell:

  >>> fd.close()
  >>>

Another look in the second command shell now shows the file is gone from
the directory:

  $ dir /w
   Volume in drive C has no label.
   Volume Serial Number is F80E-0A52

   Directory of C:\Users\adi\hgrepos\tests\c

  [.]     [..]    [.hg]   b.txt
                 1 File(s)              6 bytes
                 3 Dir(s)  473'414'606'848 bytes free

And status agrees with that:

  $ hg status
  ! a.txt

Boom.

In short: doing a 'hg update' while another process is reading the file
nukes the file on Windows.

[1] http://mercurial.selenic.com/wiki/UnlinkingFilesOnWindows
Comment 4 HG Bot 2010-12-10 20:00 UTC
Fixed by http://selenic.com/repo/hg/rev/a98a90023261
Adrian Buehlmann <adrian@cadifra.com>
localrepo: remove unneeded os.unlink call in wwrite

(please test the fix)
Comment 5 Adrian Buehlmann 2010-12-11 01:28 UTC
Dear hgbot, a98a90023261 is helpful but not sufficient
Comment 6 Adrian Buehlmann 2011-01-05 04:45 UTC
Changing title from

  opener loses files on 'w'rite for open files on Windows

to

  update loses working copy files on Windows for open files

Confirmed to still be present in release 1.7.3 (as expected).
Comment 7 Adrian Buehlmann 2011-01-05 04:55 UTC
Also, confirmed to still be present current default tip f3058dd05281.
Comment 8 HG Bot 2011-01-21 19:00 UTC
Fixed by http://selenic.com/repo/hg/rev/c01eea6c455b
Adrian Buehlmann <adrian@cadifra.com>
opener: use util.unlink (issue2524)

(please test the fix)
Comment 9 Bugzilla 2012-05-12 09:14 UTC

--- Bug imported by bugzilla@serpentine.com 2012-05-12 09:14 EDT  ---

This bug was previously known as _bug_ 2524 at http://mercurial.selenic.com/bts/issue2524