tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: NetBSD bug/misbehavior in vdprintf



On Fri, Aug 28, 2020 at 2:50 PM Rob Newberry <robthedude%mac.com@localhost> wrote:

> NetBSD's implementation of vdprintf makes a special check -- if the
> descriptor is in non-blocking mode, it needs to be a regular file (I think
> I read that code correctly).  But it apparently doesn't have this check
> problem for vfprintf.  I think it's been there a long time (since the
> introduction of vdprintf), but it makes vdprintf behave differently than
> vfprintf.  In my view, "vfprintf( FILE, ...)" and "vdprintf( fileno( FILE
> ), ... )" ought to behave the same -- but they don't (on NetBSD) if
> "fileno( FILE )" has been marked non-blocking and it's not a regular file.
>
> Other OSes do not behave this way.
>
> Here's some very simple code that demonstrates:
>
>         #include <stdio.h>
>         #include <fcntl.h>
>         #include <errno.h>
>
>         static void check_result( int n )
>         {
>                 if ( n < 0 ) { fprintf( stderr, "\tfailure (error =
> %d)\n", errno ); }
>                 else { fprintf( stderr, "\tsuccess!\n" ); }
>         }
>
>         int main(int argc, const char * argv[])
>         {
>                 int n;
>                 int flags;
>                 int err;
>
>                 n = fprintf( stdout, "fprintf to stdout (blocking)\n" );
>                 check_result( n );
>
>                 n = dprintf( fileno( stdout ), "dprintf to stdout
> (blocking)\n" );
>                 check_result( n );
>
>                 // now set stdout to non-blocking
>                 flags = fcntl( fileno( stdout ), F_GETFL );
>                 err = fcntl( fileno( stdout ), F_SETFL, flags | O_NONBLOCK
> );
>                 if ( err != 0 )
>                 {
>                         fprintf( stderr, "fcntl( F_SETFL ) failure
> (%d)\n", errno );
>                 }
>
>                 n = fprintf( stdout, "fprintf to stdout (non-blocking)\n"
> );
>                 check_result( n );
>
>                 n = dprintf( fileno( stdout ), "dprintf to stdout
> (non-blocking)\n" );
>                 check_result( n );
>
>                 return 0;
>         }
>
> Here's the output on mac OS, FreeBSD and Linux:
>
>         > ./dprintf_test
>         fprintf to stdout (blocking)
>                 success!
>         dprintf to stdout (blocking)
>                 success!
>         fprintf to stdout (non-blocking)
>                 success!
>         dprintf to stdout (non-blocking)
>                 success!
>
> But here's the output on NetBSD:
>
>         > ./dprintf_test
>         fprintf to stdout (blocking)
>                 success!
>         dprintf to stdout (blocking)
>                 success!
>         fprintf to stdout (non-blocking)
>                 success!
>                 failure (error = 79)
>
> The behavior is caused by code in libc/stdio/vdprintf.c -- around line 92:
>
>         if (fdflags & O_NONBLOCK) {
>                 struct stat st;
>                 if (fstat(fd, &st) == -1)
>                         return -1;
>                 if (!S_ISREG(st.st_mode)) {
>                         errno = EFTYPE;
>                         return EOF;
>                 }
>         }
>
> Why is this done on NetBSD (when it isn't on other OSes)?


(fwiw, i didn't check FreeBSD but Android [via OpenBSD] doesn't have this
either.)


> And why does vfprintf not have the same restriction/limitation?
>
> Thanks!
>
> Rob
>
>


Home | Main Index | Thread Index | Old Index