Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

How does rename() work on read-only files?

by j41r (Beadle)
on Dec 11, 2018 at 00:56 UTC ( #1227075=perlquestion: print w/replies, xml ) Need Help??

j41r has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I'm one of the weird people that hates to ask questions but instead loves to RTFM. This time I'm really stuck with this subject that I'm pretty sure is trivial for all of you but, for me, it's so important that I didn't hesitate to raise my hand to ask about it, so here you go:

I was reading about Why does Perl let me delete read only files? Why does i clobber protected files? Isn't this a bug in Perl?, and while the executive summary was pretty much understandable, that wasn't enough for me and I went the extra mile by reading the elaborately and painstakingly explanation available in the file file-dir-perms

That's an amazing explanation, however how rename() actually works on read-only files just went over my head. Could anyone please help me out understand these two cases?

  1. When running perl -i.bak -pe 1 alpha/foo, foo's inode is preserved and foo.bak gets a new inode.
  2. When running perl -i.bak -pe 1 alpha/bar, bar gets a new inode and bar.bak gets the previous bar's inode.

I also read How can I reliably rename a file?, which says (emphasis mine):

It may be more portable to use the File::Copy module instead. You just copy to the new file to the new name (checking return values), then delete the old one. This isn't really the same semantically as a rename(), which preserves meta-information like permissions, timestamps, inode info, etc.

but it seams to just add insult to injury, so any help would be appreciated.

Replies are listed 'Best First'.
Re: How does rename() work on read-only files?
by ikegami (Pope) on Dec 11, 2018 at 03:47 UTC

    how rename() actually works on read-only files just went over my head.

    rename doesn't modify any files; it modifies a directory or two. As long as you have write access to the source and destination directories, you can rename the file.

    $ touch foo $ chmod 0 foo $ ls -li total 0 11314128152 ---------- 1 ikegami pg1404028 0 Dec 11 10:11 foo $ perl -e'rename("foo", "bar") or die $!' $ ls -li total 0 11314128152 ---------- 1 ikegami pg1404028 0 Dec 11 10:11 bar $ chmod a-w . $ perl -e'rename("bar", "baz") or die $!' Permission denied at -e line 1. $ ls -li total 0 11314128152 ---------- 1 ikegami pg1404028 0 Dec 11 10:11 bar
      ... you can rename the file

      I agree, but I wonder why the foo's inode is not preserved after the rename() step, even though the documentation says so.

        You are mistaken. rename doesn't change what file (inode) the name points to.

        (I've updated my earlier comment to show the inode numbers.)

Re: How does rename() work on read-only files? (updated)
by haukex (Bishop) on Dec 11, 2018 at 11:13 UTC

    Perhaps it helps to think of a "directory" like just another file, but a special file that contains a list of other files. In order to rename a file, you don't need access to that file itself, you need access to the place where it is listed, the directory. So in the following example, you've got a file foo and two directories, one and two. In order to move the entry for the file foo from one directory to the other, you need write access to both one and two's list of files, and if I take that away, I can't move the file.

    $ cd `mktemp -d` $ mkdir one $ mkdir two $ chmod -R 755 . $ touch one/foo $ chmod 0 one/foo $ cat one/foo cat: one/foo: Permission denied $ mv -v one/foo two/ ‘one/foo’ -> ‘two/foo’ $ chmod a-w one $ mv -v two/foo one/ ‘two/foo’ -> ‘one/foo’ mv: cannot move ‘two/foo’ to ‘one/foo’: Permission denied $ chmod u+w one $ chmod a-w two $ mv -v two/foo one/ ‘two/foo’ -> ‘one/foo’ mv: cannot move ‘two/foo’ to ‘one/foo’: Permission denied $ chmod u+w two $ mv -v two/foo one/ ‘two/foo’ -> ‘one/foo’

    By the way, if this is about the -i switch and needing a reliable mechanism for "overwriting" files in-place, my module File::Replace might be of interest to you. Update: See Re: [RFC] File::Replace

Re: How does rename() work on read-only files?
by hippo (Chancellor) on Dec 11, 2018 at 09:10 UTC

    For the benefit of everyone else, here's the description of the foo and bar files in the doc:

    Look at this.  Assume you are the owner of all files and directories
    listed, and that your umask (creation mask) is 022.
    
        directory           filename        file inode          perms
        ----------          ---------       -----------         ------
        alpha               .               100                 0755
        alpha               foo             101                 0644
        alpha               bar             102                 0444
    
        beta                .               200                 0555
        beta                foo             201                 0644
        beta                bar             102                 0444
    
    Consider two directories, alpha and beta, each of which have the same
    two filenames, foo and bar.  alpha/foo is really file #101, but beta/foo
    is really file #201 -- different files.  However, the two bar filenames
    both refer to the same file, because both #102.  It's just two links
    (filenames) to the same file.
    

    When you run the command on foo, you can write to it (0644) so perl puts a copy in foo.bak and then overwrites what was in foo.*

    When you run the command on bar, you cannot write to it (0444) so it requires a different strategy. Instead it renames bar to bar.bak as the backup and then writes to a brand new file bar with what would be the changes.

    Does that help at all?

    * Edit: having tested this myself it is not what I see for foo. I see that foo.bak has the inode of the old foo. Are you sure of your result?

    Edit 2: Both operations proceed in the same way: rename the file to .bak and then create the new file.

      ... having tested this myself it is not what I see for foo. I see that foo.bak has the inode of the old foo. Are you sure of your result?

      Well, maybe I was so tired that I didn't notice it was actually different. Here's what I got:

      [j41r@work perl]$ rm alpha/foo.bak [j41r@work perl]$ stat -c%n:$'\t'%i alpha/foo* alpha/foo: 9353128 [j41r@work perl]$ perl -i.bak -pe 1 alpha/foo [j41r@work perl]$ stat -c%n:$'\t'%i alpha/foo* alpha/foo: 9353126 alpha/foo.bak: 9353128 [j41r@work perl]$ rm alpha/foo.bak [j41r@work perl]$ stat -c%n:$'\t'%i alpha/foo* alpha/foo: 9353126

      Why did we get a different result than the one in file-dir-perms?

        Why did we get a different result

        Perhaps you are running a different Perl version? From perl5280delta:

        Previously in-place editing (perl -i) would delete or rename the input file as soon as you started working on a new file. ... This has changed so that the input file isn't replaced until the output file has been completely written and successfully closed. This works by creating a work file in the same directory, which is renamed over the input file once the output file is complete.

        I expect that the new inode numbers for alpha/foo* in the linked doc are in error. The textual description in the doc is correct:

        So what happens?  Consider this
        
            perl -i.bak -pe 1 alpha/foo
        
        Perl wants to do this
        
            rename alpha/foo to alpha/foo.bak
            create a new alpha/foo
        
        Now, alpha/foo is permission 0644, which means you can write it, *BUT
        THIS IS IRRELEVANT*.  You are not altering the file.  You moved it out
        of the way and created a new one.  Moving and creating are governed by
        the directory's permissions.  Since alpha is mode 0755, this is allowed ...
        

        The fact that tchrist's file-dir-perms doc is now relegated to a tarball in the olddoc tree does suggest that it is only kept for historical interest, despite the link from perlfaq5.

Re: How does rename() work on read-only files?
by eyepopslikeamosquito (Bishop) on Dec 12, 2018 at 00:27 UTC

      Thank you eyepopslikeamosquito, these links actually answer some of the questions I was about to post. Cool! :-)

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1227075]
Approved by kcott
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (2)
As of 2020-07-04 15:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?