[issue42606] Support POSIX atomicity guarantee of O_APPEND on Windows

Eryk Sun report at bugs.python.org
Tue Jan 26 18:48:46 EST 2021


Eryk Sun <eryksun at gmail.com> added the comment:

> So the idea is to delete the file for a brief period, but then 
> undelete it. 

Currently NTFS defaults to using classic delete semantics for delete-on-close. However, it supports POSIX delete semantics for delete-on-close, so the default could change in a future release. When delete-on-close uses POSIX semantics, closing the last handle to the kernel File immediately unlinks the file. The upside is that a filesystem that uses POSIX delete semantics for delete-on-close should support SetFileInformationByHandle: FileDispositionInfoEx, which allows directly removing the delete-on-close flag.

That said, I can't stomach having to manually overwrite, truncate, delete and undelete files in order to avoid side effects if _open_osfhandle() fails. IMO, if opening a file has serious side effects, then we need a public API to allocate the fd beforehand. Or the idea needs to be put on hold until Python divorces itself from the C runtime's low I/O layer.

> If the file type is FILE_TYPE_CHAR and the first byte to write 
> was 26 (Ctrl-Z), it's treated as success. I don't think I 
> understand: it *seems* like it's handling something like writing 
> to the *input* of a console, but I'm not sure it's even possible 
> in this manner.

Writing to the console input buffer is possible, but not supported with WriteFile() or WriteConsoleW(). It requires WriteConsoleInputW(), which writes low-level input records.

ReadFile() on a console input handle is special cased to return that 0 bytes were read if the string read from the console starts with Ctrl+Z. But I don't know of a device that special cases WriteFile() like this. The documentation of _write() says "[w]hen writing to a device, _write treats a CTRL+Z character in the buffer as an output terminator". Whatever this meant in the past in OS/2 or DOS, I doubt that it's meaningful nowadays.

> Anything else is a failure with ENOSPC. This is mysterious too.

Possibly someone picked an error code that was good enough. Maybe it was selected for the case of a full pipe that's in non-blocking mode. The named pipe device doesn't fail an NtWriteFile() system call for a non-blocking pipe when there isn't enough space. It simply succeeds with 0 bytes written. For example:

    >>> fdr, fdw = os.pipe()
    >>> hw = msvcrt.get_osfhandle(fdw)
    >>> win32pipe.SetNamedPipeHandleState(hw, 1, None, None)
    >>> os.write(fdw, b'a'*4096)
    4096
    >>> os.write(fdw, b'a')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [Errno 28] No space left on device

A POSIX developer would expect this case to fail with EAGAIN and raise BlockingIOError. But support for non-blocking mode is poorly implemented by pipes in Windows. Developers are encouraged to use asynchronous I/O instead.

----------

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue42606>
_______________________________________


More information about the Python-bugs-list mailing list