Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Is it possible to modify __DATA__ via the DATA file handle or otherwise?

by YenForYang (Beadle)
on Feb 09, 2018 at 17:09 UTC ( #1208841=perlquestion: print w/replies, xml ) Need Help??
YenForYang has asked for the wisdom of the Perl Monks concerning the following question:

I would like to keep persistent data inside a perl script, but this data might be updated every time it's run. This may sound like a strange thing to do, but is there any way I could alter the data after the __DATA__ token? It might be important to mention that the data doesn't need to be accessed after modification by the script process that did the modification--but it is accessed everytime before modification

  • Comment on Is it possible to modify __DATA__ via the DATA file handle or otherwise?

Replies are listed 'Best First'.
Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by Arunbear (Prior) on Feb 09, 2018 at 17:48 UTC
Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by ikegami (Pope) on Feb 09, 2018 at 18:55 UTC

    DATA is a plain, ordinary file handle to the source file, but it's open for reading (not writing). I don't know how to convert a handle opened for reading into a handle opened for writing (if it's even possible), but you can open a new handle to the current file, and use tell(DATA) to find the location of the start of the data.

    die("Not safe for setuid") if ${^TAINT}; open(my $fh, ">>", __FILE__) or die("open: $!\n"); ( my $data_pos = tell(DATA) ) >= 0 or die("tell: $!\n"); truncate($fh, $data_pos) or die("truncate: $!\n"); say($fh "bar") or die("say: $!\n"); __DATA__ foo
      Pretty much what I had in mind, but to avoid race conditions due to parallel starts, I'd rather write to a temporary file and rename it at the end.

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Wikisyntax for the Monastery

        I'd rather write to a temporary file and rename it at the end

        Good thing I wrote a module for that ;-)

        use warnings; use strict; use File::Replace 'replace2'; my ($infh,$outfh) = replace2(__FILE__); while (<$infh>) { last if /^__DATA__$/; print $outfh $_; } print $outfh "__DATA__\n"; print $outfh "New stuff! ".gmtime." UTC\n"; close $infh; close $outfh;

        Or use a locking mechanism (e.g. flock).

      Sorry for this dumb question, but I was wondering what/why the first line (die ...) is used (for).

        setuid allows a user to execute a script as the script's owner.

        I believe it can be attacked as follows:

        1. Create a symbolic link to the setuid script.
        2. Execute the setuid script via that symbolic link.
        3. Immediately replace the symbolic link with a file.
        4. If you get the timing right, the setuid script will read and write the data from your file instead of from the script file. The script trusts the data since it expects to be the only one able to change it, but you actually have full access to the data.

        As a variation of the above, the attack could also replace the volume on which the script resides by unmounting it and mounting a new one.

Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by BrowserUk (Pope) on Feb 09, 2018 at 17:58 UTC

    Take a look at Inline::Files. It can do that and more.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit
Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by Laurent_R (Canon) on Feb 09, 2018 at 22:29 UTC
    Frankly, I wouldn't do that. It's too easy to make a mistake and clobber your entire program. Use a separate file (simple config file, DBM, tied hash or array, whatever is convenient), but don't take the risk of overwriting your program.

    You can shoot yourself in the foot, if you like. I also can (at least in theory), but I just wouldn't.

Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by karlgoethebier (Monsignor) on Feb 10, 2018 at 09:49 UTC
    "...otherwise?"

    If I were in your shoes i would use another file for this task. Please see JSON::Tiny and Path::Tiny for a convenient way how to accomplish this.

    Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by LanX (Bishop) on Feb 10, 2018 at 12:27 UTC
    It's definitely possible, I would have shown you Ikegami's approach.

    But I have trouble imagining a use case where the benefits of this approach outweigh the drawbacks.

    The only thing I could come up with was self reproducing code like a virus. (But then you would alter a copy ...)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Wikisyntax for the Monastery

      The only thing I could come up with was self reproducing code like a virus
      Well, it is used effectively as a comedic device in source filter joke modules - such as Acme::Bleach and Acme::Buffy.

      The Acme::Bleach source is short and amusing (note the use of 0/$0 below to bleach the source file):

      package Acme::Bleach; our $VERSION = '1.150'; my $tie = " \t"x8; sub whiten { local $_ = unpack "b*", pop; tr/01/ \t/; s/(.{9})/$1\n/g; + $tie.$_ } sub brighten { local $_ = pop; s/^$tie|[^ \t]//g; tr/ \t/01/; pack "b* +", $_ } sub dirty { $_[0] =~ /\S/ } sub dress { $_[0] =~ /^$tie/ } open 0 or print "Can't rebleach '$0'\n" and exit; (my $shirt = join "", <0>) =~ s/(.*)^\s*use\s+Acme::Bleach\s*;\n//sm; my $coat = $1; my $pressed = '#line ' . ("$coat\n" =~ tr/\n/\n/) . ' ' . (caller)[1] +. "\n"; local $SIG{__WARN__} = \&dirty; do {eval $coat . brighten $shirt; print STDERR $@ if $@; exit} unless dirty $shirt && not dress $shirt; open 0, ">$0" or print "Cannot bleach '$0'\n" and exit; print {0} "${coat}use Acme::Bleach;\n", whiten $pressed.$shirt and exi +t; __END__

Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by dsheroh (Prior) on Feb 10, 2018 at 09:57 UTC
    This strikes me as a rather bad idea. If you do use version control, you're generating needless churn in the source repository whenever you change something in __DATA__. If you don't, you're taking an unnecessary risk of losing some or all of your program if a file offset is miscalculated or there's a problem while rewriting the file.

    Too much risk, too little benefit, if you ask me.

Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by clueless newbie (Chaplain) on Feb 09, 2018 at 19:30 UTC
    Will this do it for you?
    #!/usr/bin/env perl # Just because you can be a SOB doesn't mean you should # or among the things you probably shouldn't do is this: # modify (read/write) the program's DATA section - # but hey, it was interesting and amusing. use Carp; use English qw(-no_match_vars); use strict; use warnings; open my $READER,'+<',$PROGRAM_NAME or Carp::confess "Can't open '$PROGRAM_NAME'! $OS_ERROR"; open my $WRITER,'+<',$PROGRAM_NAME # Don't us +e '+>' or Carp::confess "Can't open '$PROGRAM_NAME'! $OS_ERROR"; seek $READER,tell DATA ,0; seek $WRITER,tell DATA,0; # The overflow buffer: my @Buffer_a; #print "READER: @{[tell $READER ]} \n"; while (<$READER>) { #print "Read: '$_'\n"; # Read (past tense) a line - buffer its replacement ... { # Modify the line here! my $Update_s="x s $_"; push @Buffer_a,$Update_s; }; #print "WRITER: @{[tell $WRITER ]} \n"; # Write from the overflow buffer if we can ... while (@Buffer_a && tell($READER)-tell($WRITER) > length $Buff +er_a[0]) { # Enough room to write $Buffer_a[0] so write it ... #print "Writing '$Buffer_a[0]'\n"; print {$WRITER} shift @Buffer_a or Carp::confess "Error while writing \$WRITER: $OS_ER +ROR"; #print "WRITER: @{[tell $WRITER]} \n"; }; #print "READER: @{[tell $READER]} \n"; }; # Nothing more to read ... close $READER or Carp::confess "Can't close '$PROGRAM_NAME'! $OS_ERROR"; #print "WRITER: @{[tell $WRITER]} \n"; # If there's anything in the buffer write it ... while (@Buffer_a) { #print "Writing '$Buffer_a[0]'\n"; print {$WRITER} shift @Buffer_a or Carp::confess "Error while writing \$WRITER: $OS_ERROR" +; #print "WRITER: @{[tell $WRITER]} \n"; }; # Truncate the file, in case, what we're writing is shorter than w +hat we read truncate $WRITER,tell$WRITER; # ... and close close $WRITER or Carp::confess "Can't close '$PROGRAM_NAME'! $OS_ERROR"; exit; __END__ 0 1 2 3 4 5 6 7 8 9
Re: Is it possible to modify __DATA__ via the DATA file handle or otherwise?
by sundialsvc4 (Abbot) on Feb 09, 2018 at 19:29 UTC
    I strongly recommend that, if the data can be changed, it belongs in an external file ... not in the Perl source-code.   In many shops, source-code is both version-controlled and write-protected, for very obvious reasons.   Source-code is sacrosanct, and data which occurs within the source-code file is “part of the source-code.”   If, instead, it is “really data,” IMHO it does not belong there.   (To me, the __DATA__ construct serves the very-useful purpose that DATA statements served in the BASIC programs of yesteryear ... as a convenient place to store fixed data that was used for initialization or reference purposes.)

      BASIC programs of yesteryear ...

      So you also pretend to know BASIC?

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1208841]
Approved by johngg
Front-paged by haukex
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (9)
As of 2018-07-16 07:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?















    Results (333 votes). Check out past polls.

    Notices?