Read-only files are only protecting the data from being changed, and
that a file and a filename are different.
file readable may examine file contents
file writable may alter file contents
file executable may run file contents
directory readable may examine directory contents (list filenames
in directory)
directory writable may alter directory contents (remove or
rename filenames)
direcory executable may use directory as component in
pathnames, or chdir to that directory
Permissions are on files, not filenames. Control of filenames is left up
to the directory. Directories contain filenames (not files!), that
themselves refer to files.
Look at this. Assume you are the owner of all files and directories
listed, and that your umask (creation mask) is 022.
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.
The unlink() (remove filename) call does not delete a file -- it deletes
a filename. When you link() a file into a directory, or creat() a new
file there, it is the directory's permissions that matter. Likewise,
a rename() is not changing a file -- it's changing its name.
Now, since the ability to modify something is controlled by the
write-permission bit, and THE THING YOU ARE MODIFYING WHEN RENAME A
FILE(NAME) IS A DIRECTORY, all you look at to rename or remove a filename
is the permission of THE DIRECTORY, because it is the DIRECTORY that
you are modifying, not the file. rename does not
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,
now giving us the following:
Well, you seem to think you shouldn't be able to rename it because
it's mode 0555, readonly. But that's irrelevant. It's the diretory
that matters. So it succeeds. Afterwards, we have this setup:
Notice that inode 102, alpha/bar.bak and beta/bar, have never
been touched. We haven't modified the contents of the file. We can't.
It's read only.
What happens when you do this:
perl -i.bak -pe 1 beta/foo
The answer is that you get permission denied. Ah, you say, this is a bug:
the file (inode 201) is mode 0644, so I should be able to write it. And
you *CAN* write it. If you opened it for writing (clobber), or appending,
or update (read/write, no clobber), you would have succeeded. But you
didn't try to do that. You tried to do this:
rename beta/foo to beta/foo.bak
create a new beta/foo
But you can't do that, because to rename a file, or to create or unlink a file
in a dirctory, you must be able to modify the directory. But beta is
itself mode 0555, so you don't have permissions to alter it. So you lose.
Likewise, if you try
perl -i.bak -pe 1 beta/bar
You lose, but it is not because beta/bar (currently pointing to file number
102, which is the same file that is pointed to by filename alpha/bar.bak)
is readonly. That fact is irrelevant to this case. You may not run that
command because you cannot rename the file to beta/bar.bak, which is
illegal due to the readonly *DIRECTORY*.
On the other hand, beta/bar will also stop you from changing the file
itself, because unlike beta/foo, which is mode 0644, beta/bar is mode 0444
and may not be altered.
The file indicated by alpha/foo, inode #101, may be changed due
to its permissions of 0644, and that filename may be renamed
and unlinked due to its directory's permissions of 0755.
The file indicated by alpha/bar, inode #102, may *NOT* be changed due
to its permissions of 0444, but that filename may be renamed
and unlinked due to its directory's permissions of 0755.
The file indicated by beta/foo, inode #201, may be changed due
to its permissions of 0644, but that filename may be *NOT* renamed
or unlinked due to its directory's permissions of 0555.
The file indicated by beta/bar, inode #102, may *NOT* be changed due
to its permissions of 0444, and that filename may be neither renamed
nor unlinked due to its directory's permissions of 0555.