develooper Front page | perl.perl5.porters | Postings from August 2010

Re: What do we do about perl -pi on Windows?

Thread Previous | Thread Next
From:
Ben Morrow
Date:
August 9, 2010 20:40
Subject:
Re: What do we do about perl -pi on Windows?
Message ID:
20100810034018.GA16479@osiris.mauzo.dyndns.org
Quoth jand@activestate.com ("Jan Dubois"):
> On Mon, 09 Aug 2010, Ben Morrow wrote:
> > IMHO -i on all platforms should open ARGVOUT to a tmpfile, and rename(2)
> > it over the original when it's done.
> 
> Sounds sensible.  You would still want to find a non-conflicting filename
> in the same directory as the original filename to make sure they are on
> the same device.

Yes, of course. My main point was that this should be universal
behaviour, not just Win32. I actually think the right sequence for -i on
Unix is

    open original
    if we have a backup suffix, link(original, backup)
    open tempfile, with O_TEMPORARY if we've got it
    set up atexit handler to unlink tempfile, just in case
    do work
    rename(tempfile, original)

This will leave tempfiles lying about if we don't have O_TEMPORARY and
perl crashes without calling the atexit handler, but that's unavoidable.

> > IIRC the equivalent of Unix' close-behind on Win32 is a flag that will
> > cause the OS to delete the file when it gets closed. That sounds exactly
> > right for this situation (better, in fact, than close-behind, since the
> > file needs a name to get renamed). The flag would have to be cleared
> > just before renaming, of course, which still leaves a small race
> 
> I believe there is no API that lets you revert the FILE_FLAG_DELETE_ON_CLOSE
> bit.  But I'm not sure why you would need to do this.

I was assuming we would set it on the tempfile, not the original. That
way the tempfile automatically vanishes if the process crashes. (You
absolutely don't want it set on the original: the whole point here is to
leave the original intact in case of disaster.)

> You close all handles
> to the file, hope nobody else has it open at that moment, so it actually
> goes away, and then rename the temp file back to the original name.

Ideally you don't do that, because there's a window there where the
original file doesn't exist and the new file isn't there yet. If I'm
reading MSDN right this should work:

    open the original file
    open a tempfile with FILE_SHARE_DELETE and DELETE_ON_CLOSE
    do work
    close original file
    MoveFileEx(tempfile, original, MOVEFILE_REPLACE_EXISTING)
    close tempfile

(Possibly ReplaceFile could be used instead of MoveFileEx.)

I *think* (though I haven't tested it) that the MoveFile is considered
to be a deletion, so this won't result in the new file being removed
when the handle to it is closed. (The temporary name will hang around
until the handle is closed, of course, unlike on Unix.) I also think
that the FILE_SHARE_DELETE will allow you to rename the file while it's
open (with delayed deletion), but I could be misinterpreting.

> You may want to Sleep() and retry a few times to deal with on-access
> virus scanners that keep files around until they manage to scan them.
> 
> It is kind of a stupid thing: the virus scanner keeps the file alive,
> just to either delete it silently later, or telling you that it found
> a virus and then deleting it anyways. :)

Yes. I don't have much experience dealing with that sort of stupidity;
it's entirely possible it would wreck any chance of doing this properly
(that is, with the file getting replaced atomically).

> > condition. (Taking a quick look at MSDN, it seems to me that -i should
> > in any case be using ReplaceFile, which handles making a backup while
> > also preserving the attributes of the replaced file.)
> 
> Yes ReplaceFile() would be a good idea to keep ACLs etc.  But if we really
> cared about this, wouldn't we also try to preserve soft- and hard-links?

The Unix code doesn't attempt to do anything special here, so I don't
think the Win32 code needs to either.

> Some quick browsing on MSDN also lets me believe that you cannot combine
> the DELETE access mode with any other kind of access if you are on
> Windows 2000/XP/2003 and are opening a remote file (i.e. by UNC name).
> So in that case we would still have to manually delete the file ourselves
> once we are done with it (and leaking it in case we are crashing).

The way I read that is that if you want both DELETE and some other
access on a remote file, you need to specify FILE_SHARE_DELETE: that is,
it's effectively considered two separate openings of the file, which
must have compatible share modes. I could well be wrong, though.

> Maybe everything should just stay the way it is right now. :)

That's always a valid option...

Ben


Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About