Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"open FH, '<:raw', $filehandle" makes filehandle unbuffered #10904

Closed
p5pRT opened this issue Dec 14, 2010 · 15 comments
Closed

"open FH, '<:raw', $filehandle" makes filehandle unbuffered #10904

p5pRT opened this issue Dec 14, 2010 · 15 comments

Comments

@p5pRT
Copy link

p5pRT commented Dec 14, 2010

Migrated from rt.perl.org#80764 (status was 'resolved')

Searchable as RT80764$

@p5pRT
Copy link
Author

p5pRT commented Dec 14, 2010

From leon@leon-laptop.

This is a bug report for perl from leon@​leon-laptop.(none),
generated with the help of perlbug 1.39 running under perl 5.12.1.


Opening a file with :raw doesn't work as documented. It opens the file with only the :unix layer, without a perlio layer, hence it will be unbuffered. Opening a file without a specific mode and using binmode later does give the correct result. I've could replicate this bug on 5.8.9, 5.10.1, 5.12.1 and blead.

Example code​:

use Data​::Dumper;
use PerlIO;

#works incorrectly
open my $fh, '<​:raw', $0;
print Dumper([ PerlIO​::get_layers($fh) ]);
#got​: [ 'unix' ]
#expected​: [ 'unix', 'perlio' ]

#works correctly
open my $fh2, '<', $0;
binmode $fh2, '​:raw';
print Dumper([ PerlIO​::get_layers($fh2) ]);
#got​: [ 'unix', 'perlio' ]
#expected​: [ 'unix', 'perlio' ]



Flags​:
  category=core
  severity=medium


Site configuration information for perl 5.12.1​:

Configured by leon at Fri Aug 20 20​:56​:31 EEST 2010.

Summary of my perl5 (revision 5 version 12 subversion 1) configuration​:
 
  Platform​:
  osname=linux, osvers=2.6.32-24-generic, archname=x86_64-linux
  uname='linux leon-laptop 2.6.32-24-generic #41-ubuntu smp thu aug 19 01​:38​:40 utc 2010 x86_64 gnulinux '
  config_args='-de -Dprefix=/home/leon/perl5/perlbrew/perls/perl-5.12.1'
  hint=recommended, useposix=true, d_sigaction=define
  useithreads=undef, usemultiplicity=undef
  useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
  use64bitint=define, use64bitall=define, uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='cc', ccflags ='-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
  optimize='-O2',
  cppflags='-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
  ccversion='', gccversion='4.4.3', gccosandvers=''
  intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
  d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
  ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
  alignbytes=8, prototype=define
  Linker and Libraries​:
  ld='cc', ldflags =' -fstack-protector -L/usr/local/lib'
  libpth=/usr/local/lib /lib /usr/lib /lib64 /usr/lib64
  libs=-lnsl -ldl -lm -lcrypt -lutil -lc
  perllibs=-lnsl -ldl -lm -lcrypt -lutil -lc
  libc=/lib/libc-2.11.1.so, so=so, useshrplib=false, libperl=libperl.a
  gnulibc_version='2.11.1'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
  cccdlflags='-fPIC', lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector'

Locally applied patches​:
 


@​INC for perl 5.12.1​:
  /home/leon/perl5/perlbrew/perls/perl-5.12.1/lib/site_perl/5.12.1/x86_64-linux
  /home/leon/perl5/perlbrew/perls/perl-5.12.1/lib/site_perl/5.12.1
  /home/leon/perl5/perlbrew/perls/perl-5.12.1/lib/5.12.1/x86_64-linux
  /home/leon/perl5/perlbrew/perls/perl-5.12.1/lib/5.12.1
  .


Environment for perl 5.12.1​:
  HOME=/home/leon
  LANG=en_US.utf8
  LANGUAGE=en_US​:en_GB​:en
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=/home/leon/perl5/perlbrew/bin​:/home/leon/perl5/perlbrew/perls/current/bin​:/home/leon/bin​:/usr/local/sbin​:/usr/local/bin​:/usr/sbin​:/usr/bin​:/sbin​:/bin​:/usr/games
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Jan 3, 2011

From @Leont

The attached patch makes «open FH, '​:raw' $filename» do what it
advertises to do​: open the file, and then call «binmode FH». Previously
a shortcut was taken that simply wasn't correct, as previously explained.

I have only tested it on Linux though, testing on Windows is required.

@p5pRT
Copy link
Author

p5pRT commented Jan 3, 2011

From @Leont

0001-Fixes-raw-layer.patch
From 4977fc22afd079b28032afbd5b5257b4772eb0c2 Mon Sep 17 00:00:00 2001
From: Leon Timmermans <fawaka@gmail.com>
Date: Mon, 3 Jan 2011 22:48:47 +0100
Subject: [PATCH] Fixes 'raw' layer for perl#80764

Made a ':raw' open do what it advertises to do (first open the file,
then binmode it), instead of leaving off the top layer.
---
 perlio.c |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/perlio.c b/perlio.c
index cd58448..9b3f37b 100644
--- a/perlio.c
+++ b/perlio.c
@@ -2008,11 +2008,15 @@ PerlIORaw_open(pTHX_ PerlIO_funcs *self, PerlIO_list_t *layers,
 	       IV n, const char *mode, int fd, int imode, int perm,
 	       PerlIO *old, int narg, SV **args)
 {
-    PerlIO_funcs * const tab = PerlIO_default_btm();
+    PerlIO_funcs * const tab = PerlIO_default_top();
     PERL_UNUSED_ARG(self);
-    if (tab && tab->Open)
-	 return (*tab->Open) (aTHX_ tab, layers, n - 1, mode, fd, imode, perm,
-			      old, narg, args);
+    if (tab && tab->Open) {
+	PerlIO* ret = (*tab->Open) (aTHX_ tab, layers, n - 1, mode, fd, imode,
+				    perm, old, narg, args);
+	PerlIO_push(aTHX_ ret, PERLIO_FUNCS_CAST(&PerlIO_raw), NULL, NULL);
+	return ret;
+    }
+
     SETERRNO(EINVAL, LIB_INVARG);
     return NULL;
 }
-- 
1.7.1

@p5pRT
Copy link
Author

p5pRT commented Jan 3, 2011

From [Unknown Contact. See original ticket]

The attached patch makes «open FH, '​:raw' $filename» do what it
advertises to do​: open the file, and then call «binmode FH». Previously
a shortcut was taken that simply wasn't correct, as previously explained.

I have only tested it on Linux though, testing on Windows is required.

@p5pRT
Copy link
Author

p5pRT commented Jan 3, 2011

@Leont - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Jan 4, 2011

From @ikegami

On Mon, Jan 3, 2011 at 5​:12 PM, fawaka@​gmail.com via RT <
perlbug-comment@​perl.org> wrote​:

The attached patch makes «open FH, '​:raw' $filename» do what it
advertises to do​: open the file, and then call «binmode FH». Previously
a shortcut was taken that simply wasn't correct, as previously explained.

I have only tested it on Linux though, testing on Windows is required.

Does it fix the following?

use Test​::More tests => 1;
my $buf;
{
  open(my $fh, '>​:raw', \$buf);
  print $fh 'foo';
}
is($buf, 'foo');
1

If so, that would be worth mentioning in the release notes and a test should
be added to prevent regression.

Does the following still work on Windows?

open(my $fh, ">​:raw​:perlio​:encoding(UTF-16le)​:crlf", $qfn) or die $!;
print($fh "abc\n"); # 61 00 62 00 63 00 0D 00 0A 00

- Eric

@p5pRT
Copy link
Author

p5pRT commented Jan 4, 2011

From @Leont

On Tue, Jan 4, 2011 at 3​:18 AM, Eric Brine <ikegami@​adaelis.com> wrote​:

Does it fix the following?

use Test​::More tests => 1;
my $buf;
{
   open(my $fh, '>​:raw', \$buf);
   print $fh 'foo';
}
is($buf, 'foo');
1

If so, that would be worth mentioning in the release notes and a test should
be added to prevent regression.

Apparently it doesn't work with my patch either, but I just fixed that
in my local repository. I should write tests anyway, so I'll post the
fix along with the tests.

Does the following still work on Windows?

open(my $fh, ">​:raw​:perlio​:encoding(UTF-16le)​:crlf", $qfn) or die $!;
 print($fh "abc\n");  # 61 00 62 00 63 00 0D 00 0A 00

It works on Linux and I see no reason why it shouldn't work on
Windows​: stacking on a :raw layer still works. You would end up with
an extra layer of buffering (crlf or perlio, depending on your
platform) beneath the :perlio layer though. I had expected replacing
:raw by :pop to work, but it doesn't due to another bug (it apparently
refuses to stack during open), I'll fix that once this bug is fixed.
With this patch you can safely leave out the :perlio layer (since a
buffered layer will be included by default as it should), so you can
use the saner ">​:raw​:encoding(UTF-16le)​:crlf" to achieve the same
result. I was planning to release a :binary layer to CPAN so that
older Perl's can also get the new behavior.

Leon

@p5pRT
Copy link
Author

p5pRT commented Jan 4, 2011

From @ikegami

On Mon, Jan 3, 2011 at 10​:59 PM, Leon Timmermans <fawaka@​gmail.com> wrote​:

On Tue, Jan 4, 2011 at 3​:18 AM, Eric Brine <ikegami@​adaelis.com> wrote​:

Does it fix the following?

use Test​::More tests => 1;
my $buf;
{
open(my $fh, '>​:raw', \$buf);
print $fh 'foo';
}
is($buf, 'foo');
1

If so, that would be worth mentioning in the release notes and a test
should
be added to prevent regression.

Apparently it doesn't work with my patch either, but I just fixed that
in my local repository. I should write tests anyway, so I'll post the
fix along with the tests.

sweet :)

Does the following still work on Windows?

open(my $fh, ">​:raw​:perlio​:encoding(UTF-16le)​:crlf", $qfn) or die $!;
print($fh "abc\n"); # 61 00 62 00 63 00 0D 00 0A 00

It works on Linux and I see no reason why it shouldn't work on
Windows​: stacking on a :raw layer still works. You would end up with
an extra layer of buffering (crlf or perlio, depending on your
platform) beneath the :perlio layer though.

If you end up with an extra :crlf below, then it's not working. The explicit
:crlf will reenable the lower :crlf instead of adding a new layer, so the
stack will end up being

:perlio
:crlf (enabled)
:encoding
:utf8

instead of the desired

:perlio
:crlf (disabled)
:encoding
:crlf (enabled)
:utf8

or

:perlio
:encoding
:crlf (enabled)
:utf8

It seems to me we should have a solution to the :crlf and :encoding ordering
problem before fixing the bug. The simplest could be to simply add a new
:crlf layer instead of enabling the existing one when pushing a :crlf layer.

- Eric

@p5pRT
Copy link
Author

p5pRT commented Jan 20, 2011

From @Leont

On Tue, Jan 4, 2011 at 9​:15 AM, Eric Brine <ikegami@​adaelis.com> wrote​:

If you end up with an extra :crlf below, then it's not working. The explicit
:crlf will reenable the lower :crlf instead of adding a new layer, so the
stack will end up being

I've attached a second patch that I think will solve that issue.

Leon

@p5pRT
Copy link
Author

p5pRT commented Jan 20, 2011

From @Leont

0001-Fixes-raw-layer-for-perl-80764.patch
From 2972bd1ff9f9773dc5c197aa88e4010a40d430ab Mon Sep 17 00:00:00 2001
From: Leon Timmermans <fawaka@gmail.com>
Date: Mon, 3 Jan 2011 22:48:47 +0100
Subject: [PATCH 1/2] Fixes 'raw' layer for perl#80764

Made a ':raw' open do what it advertises to do (first open the file,
then binmode it), instead of leaving off the top layer.
---
 perlio.c |   14 +++++++++-----
 1 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/perlio.c b/perlio.c
index 0eee430..4030180 100644
--- a/perlio.c
+++ b/perlio.c
@@ -2008,11 +2008,15 @@ PerlIORaw_open(pTHX_ PerlIO_funcs *self, PerlIO_list_t *layers,
 	       IV n, const char *mode, int fd, int imode, int perm,
 	       PerlIO *old, int narg, SV **args)
 {
-    PerlIO_funcs * const tab = PerlIO_default_btm();
-    PERL_UNUSED_ARG(self);
-    if (tab && tab->Open)
-	 return (*tab->Open) (aTHX_ tab, layers, n - 1, mode, fd, imode, perm,
-			      old, narg, args);
+    PerlIO_funcs * const tab = 
+	PerlIO_layer_fetch(aTHX_ layers, n - 1, PerlIO_default_btm());
+    if (tab && tab->Open) {
+	PerlIO* ret = (*tab->Open) (aTHX_ tab, layers, n - 1, mode, fd, imode,
+				    perm, old, narg, args);
+	PerlIO_push(aTHX_ ret, PERLIO_FUNCS_CAST(&PerlIO_raw), NULL, NULL);
+	return ret;
+    }
+
     SETERRNO(EINVAL, LIB_INVARG);
     return NULL;
 }
-- 
1.7.1

@p5pRT
Copy link
Author

p5pRT commented Jan 20, 2011

From @Leont

0002-binmode-FH-crlf-only-modifies-top-crlf-layer.patch
From 78cc11c9ad89f424cb97b2805a763c53d60a4a83 Mon Sep 17 00:00:00 2001
From: Leon Timmermans <fawaka@gmail.com>
Date: Thu, 20 Jan 2011 01:59:56 +0100
Subject: [PATCH 2/2] binmode FH, ":crlf" only modifies top crlf layer

When pushed on top of the stack, crlf will no longer enable crlf layers
lower in the stack. This will prevent unexpected results.
---
 perlio.c      |    5 ++---
 t/io/layers.t |   18 +++++++++++++++---
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/perlio.c b/perlio.c
index 4030180..48c32ff 100644
--- a/perlio.c
+++ b/perlio.c
@@ -4513,7 +4513,7 @@ PerlIOCrlf_pushed(pTHX_ PerlIO *f, const char *mode, SV *arg, PerlIO_funcs *tab)
        * any given moment at most one CRLF-capable layer being enabled
        * in the whole layer stack. */
 	 PerlIO *g = PerlIONext(f);
-	 while (PerlIOValid(g)) {
+	 if (PerlIOValid(g)) {
 	      PerlIOl *b = PerlIOBase(g);
 	      if (b && b->tab == &PerlIO_crlf) {
 		   if (!(b->flags & PERLIO_F_CRLF))
@@ -4521,8 +4521,7 @@ PerlIOCrlf_pushed(pTHX_ PerlIO *f, const char *mode, SV *arg, PerlIO_funcs *tab)
 		   S_inherit_utf8_flag(g);
 		   PerlIO_pop(aTHX_ f);
 		   return code;
-	      }		  
-	      g = PerlIONext(g);
+	      }
 	 }
     }
     S_inherit_utf8_flag(f);
diff --git a/t/io/layers.t b/t/io/layers.t
index dea3d09..b0bcf1e 100644
--- a/t/io/layers.t
+++ b/t/io/layers.t
@@ -43,7 +43,7 @@ if (${^UNICODE} & 1) {
 } else {
     $UTF8_STDIN = 0;
 }
-my $NTEST = 45 - (($DOSISH || !$FASTSTDIO) ? 7 : 0) - ($DOSISH ? 5 : 0)
+my $NTEST = 55 - (($DOSISH || !$FASTSTDIO) ? 7 : 0) - ($DOSISH ? 7 : 0)
     + $UTF8_STDIN;
 
 sub PerlIO::F_UTF8 () { 0x00008000 } # from perliol.h
@@ -105,7 +105,7 @@ SKIP: {
 	    # 5 tests potentially skipped because
 	    # DOSISH systems already have a CRLF layer
 	    # which will make new ones not stick.
-	    @$expected = grep { $_ ne 'crlf' } @$expected;
+	    splice @$expected, 1, 1 if $expected->[1] eq 'crlf';
 	}
 	my $n = scalar @$expected;
 	is(scalar @$result, $n, "$id - layers == $n");
@@ -132,13 +132,25 @@ SKIP: {
 	  [ qw(stdio crlf) ],
 	  "open :crlf");
 
+    binmode(F, ":crlf");
+
+    check([ PerlIO::get_layers(F) ],
+	  [ qw(stdio crlf) ],
+	  "binmode :crlf");
+
     binmode(F, ":encoding(cp1047)"); 
 
     check([ PerlIO::get_layers(F) ],
 	  [ qw[stdio crlf encoding(cp1047) utf8] ],
 	  ":encoding(cp1047)");
+
+    binmode(F, ":crlf");
+
+    check([ PerlIO::get_layers(F) ],
+	  [ qw[stdio crlf encoding(cp1047) utf8 crlf utf8] ],
+	  ":encoding(cp1047):crlf");
     
-    binmode(F, ":pop");
+    binmode(F, ":pop:pop");
 
     check([ PerlIO::get_layers(F) ],
 	  [ qw(stdio crlf) ],
-- 
1.7.1

@p5pRT
Copy link
Author

p5pRT commented Jan 21, 2011

From @cpansprout

This has now been fixed by one of the patches from ticket #82484.

@p5pRT
Copy link
Author

p5pRT commented Jan 21, 2011

@cpansprout - Status changed from 'open' to 'resolved'

@p5pRT p5pRT closed this as completed Jan 21, 2011
@p5pRT
Copy link
Author

p5pRT commented Jan 21, 2011

From @ikegami

On Thu, Jan 20, 2011 at 10​:30 AM, Leon Timmermans <fawaka@​gmail.com> wrote​:

On Tue, Jan 4, 2011 at 9​:15 AM, Eric Brine <ikegami@​adaelis.com> wrote​:

If you end up with an extra :crlf below, then it's not working. The
explicit
:crlf will reenable the lower :crlf instead of adding a new layer, so the
stack will end up being

I've attached a second patch that I think will solve that issue.

Indeed, thanks!

1 similar comment
@p5pRT
Copy link
Author

p5pRT commented Jan 30, 2011

From @ikegami

On Thu, Jan 20, 2011 at 10​:30 AM, Leon Timmermans <fawaka@​gmail.com> wrote​:

On Tue, Jan 4, 2011 at 9​:15 AM, Eric Brine <ikegami@​adaelis.com> wrote​:

If you end up with an extra :crlf below, then it's not working. The
explicit
:crlf will reenable the lower :crlf instead of adding a new layer, so the
stack will end up being

I've attached a second patch that I think will solve that issue.

Indeed, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant