|
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?
- When running perl -i.bak -pe 1 alpha/foo, foo's inode is preserved and foo.bak gets a new inode.
- 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.
Re: How does rename() work on read-only files?
by ikegami (Patriarch) 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
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
|
|
Re: How does rename() work on read-only files? (updated)
by haukex (Archbishop) 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 | [reply] [d/l] [select] |
Re: How does rename() work on read-only files?
by hippo (Archbishop) 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. | [reply] [d/l] |
|
|
[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?
| [reply] [d/l] |
|
|
| [reply] [d/l] |
|
|
|
|
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.
| [reply] [d/l] |
|
|
|
|
|
Re: How does rename() work on read-only files?
by eyepopslikeamosquito (Archbishop) on Dec 12, 2018 at 00:27 UTC
|
| [reply] |
|
|
| [reply] |
|
|