Bug 5993

Summary: O_ASYNC broken/behaves anomalously
Product: File System Reporter: Michael Kerrisk (michael.kerrisk)
Component: VFSAssignee: Alan (alan)
Status: ASSIGNED ---    
Severity: normal CC: dustwolfy, protasnb, szg00000, xerofoify
Priority: P2    
Hardware: i386   
OS: Linux   
Kernel Version: 3.15 Subsystem:
Regression: No Bisected commit-id:

Description Michael Kerrisk 2006-02-01 16:50:12 UTC
Most recent kernel where this bug did not occur: the bug is current
Distribution: SUSE 10.0
Hardware Environment: x86
Software Environment:
Problem Description:

This is either a bug report or an attempt at clarification of 
expected behaviour.

I do not have a 2.6.15 vanilla kernel to hand, but when I did last 
test that kernel, the problems described below also existed there.

Recently (I'm the man-pages maintainer), I added text to the open(2) 
man page to document the fact that O_ASYNC does not work when 
supplied to open(2).  In the process I observed some anomalous 
semantics for O_ASYNC, detailed below.  

Steps to reproduce:

On Linux, specifying O_ASYNC when open()ing a FIFO or terminal 
does not enable signal-driven I/O for the file descriptor/open 
file description.   This behaviour is sort of expected:
O_ASYNC is not specified in POSIX/SUSv3, but is implemented 
on many Unix systems.  And most Unix implementations that I've 
tested don't honour O_ASYNC on open(): as on Linux, the flag must 
be set using fcntl(F_SETFL).  (It would be nice if Linux did 
honour the flag on open(), but that isn't essential for
consistency with other implementations.)

But there is some anomalous behaviour...  If we do 
the following:

1. open() the file with O_ASYNC (aka FASYNC).  We can then see 
   that the flag is set for the open file (it can be retrieved 
   using fcntl(F_GETFL)).

2. Set the file descriptor owner using fcntl(F_SETOWN).

3. Although O_ASYNC is set, signal-driven I/O is not enabled -- 
   no signal is generated when I/O (e.g., input) becomes possible.
   (As noted above, this is more or less expected behavior.)

4. If we want to enable signal-driven I/O on the file, then we 
   must:

        a) Disable O_ASYNC using fcntl(F_SETFL);
        b) Enable O_ASYNC using fcntl(F_SETFL);

However, I see that at least some other implementations 
(e.g., FreeBSD 6.0) do not require step 4a in the scenario above.  

In other words, if we ("accidentally") specify O_ASYNC when opening 
a FIFO, then going on to simply enable O_ASYNC using fcntl(F_SETFL) 
is sufficient to enable signal-driven I/O on FreeBSD.  By contrast 
Linux requires that we explicitly disable the flag first.  The Linux 
behaviour seems a bit bizarre, and it seems to me that it should 
be fixed.

I've appended a test program below.  

Running the program with the following arguments demonstrates 
that simply opening a FIFO using O_ASYNC is not enough to get 
signal-driven I/O semantics:

$ mkfifo p
$ ./test_O_ASYNC ./p &
$ cat > p
[type newline]

Running the program with the following arguments excludes step 4a 
above, but does perform step 4b.  This does not permit 
signal-driven I/O (but on FreeBSD 6.0 it does):

$ mkfifo p
$ ./test_O_ASYNC ./p f &
$ cat > p
[type newline]

The following run implements step 4a and 4b, and permits 
signal-driven I/O:

$ mkfifo p
$ ./test_O_ASYNC ./p cf
$ cat > p
[type newline]

Cheers,

Michael

PS The following LKML posts have some relevance:

http://marc.theaimsgroup.com/?t=100572281200001&r=1&w=2
http://marc.theaimsgroup.com/?l=linux-kernel&m=100580818429730&w=2


/* test_O_ASYNC.c */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

#define usageErr(msg, progName) \
                        do { fprintf(stderr, "Usage: "); \
                             fprintf(stderr, msg, progName); \
                             exit(EXIT_FAILURE); } while (0)

static void
handler(int sig)
{
    printf("Caught signal %d\n", sig);
} /* handler */

int
main(int argc, char *argv[])
{
    int fd, flags;
    int no_open_O_ASYNC, fcntl_O_ASYNC, clear_O_ASYNC, no_F_SETOWN;
    struct sigaction sa;

    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGIO, &sa, NULL) == -1) errExit("sigaction");

    if (argc < 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s FIFO-path [nf]\n", argv[0]);

    no_open_O_ASYNC = argc > 2 && strchr(argv[2], 'O') != NULL;
    no_F_SETOWN = argc > 2 && strchr(argv[2], 'S') != NULL;
    fcntl_O_ASYNC = argc > 2 && strchr(argv[2], 'f') != NULL;
    clear_O_ASYNC = argc > 2 && strchr(argv[2], 'c') != NULL;

#if 0
    printf("no_open_O_ASYNC = %d\n", no_open_O_ASYNC);
    printf("no_F_SETOWN = %d\n", no_F_SETOWN);
    printf("fcntl_O_ASYNC = %d\n", fcntl_O_ASYNC);
    printf("clear_O_ASYNC = %d\n", clear_O_ASYNC);
#endif

    fd = open(argv[1], O_RDONLY | (no_open_O_ASYNC ? 0 : O_ASYNC));
    if (fd == -1) errExit("open");

    if (!no_F_SETOWN) {
        if (fcntl(fd, F_SETOWN, getpid()) == -1)
            errExit("fcntl - F_SETOWN");
    } 

    if (clear_O_ASYNC) {
        flags = fcntl(fd, F_GETFL);
        if (flags == -1) errExit("open");
        if (fcntl(fd, F_SETFL, flags & ~O_ASYNC) == -1) errExit("fcntl");
    } 

    if (fcntl_O_ASYNC) {
        flags = fcntl(fd, F_GETFL);
        if (flags == -1) errExit("open");
        if (fcntl(fd, F_SETFL, flags | O_ASYNC) == -1) errExit("fcntl");
    } 

    for (;;)
        pause();

    exit(EXIT_SUCCESS);
} /* main */
Comment 1 Natalie Protasevich 2007-08-25 22:48:34 UTC
Michael has anything changed in the kernel with regard to behavior of open that you reported about?
Thanks.
Comment 2 Anonymous Emailer 2007-08-28 14:22:41 UTC
Reply-To: michael.kerrisk@googlemail.com

bugme-daemon@bugzilla.kernel.org wrote:
> http://bugzilla.kernel.org/show_bug.cgi?id=5993
> 
> 
> protasnb@gmail.com changed:
> 
>            What    |Removed                     |Added
> ----------------------------------------------------------------------------
>                  CC|                            |protasnb@gmail.com
>           Component|Other                       |Other
>             Product|Other                       |File System
> 
> 
> 
> 
> ------- Comment #1 from protasnb@gmail.com  2007-08-25 22:48 -------
> Michael has anything changed in the kernel with regard to behavior of open
> that
> you reported about?
> Thanks.
> 
> 
Not as far as I know.
Comment 3 Jure Sah 2009-06-28 21:29:52 UTC
Confirm bug.

Tested on:
* Ubuntu 8.04.2 AMD64 2.6.24-24-generic
* Xandos i386 2.6.21.4-eeepc

Further details with FASM source code for testing:
http://board.flatassembler.net/topic.php?p=96629#96629