Why is a file with 400 permissions seen writable by root but read-only by user?

I think you have have misunderstood what -w does. It does not check to see if the file has "Write permissions", it checks to see if the file is writable by the invoking user.

More specifically, it calls access(2) or similar.

eg if a script has if [ -w /etc/shadow ] then if you run strace on the script you may see a line similar to

faccessat(AT_FDCWD, "/etc/shadow", W_OK)

Since root can write to the file then it returns 0.

eg as a normal user:

faccessat(AT_FDCWD, "/etc/shadow", W_OK) = -1 EACCES (Permission denied)

As root

faccessat(AT_FDCWD, "/etc/shadow", W_OK) = 0

This, despite the fact that /etc/shadow has permission 000 on my machine.

---------- 1 root root 4599 Jan 29 20:08 /etc/shadow

Now what you want to do gets interesting and isn't so simple.

If you want to check the simple permissions then check the ls output, or call stat or similar. But realize that ACLs can over-ride these permissions. Just because a file is permission 400 doesn't stop it from being writable...


test -w aka [ -w doesn't check the file mode. It checks if it's writable. For root, it is.

$ help test | grep '\-w'
  -w FILE        True if the file is writable by you.

The way I would test would be to do a bitwise comparison against the output of stat(1) ("%a Access rights in octal").

(( 0$(stat -c %a somefile) & 0200 )) && echo rw || echo ro

Note the subshell $(...) needs a 0 prefixed so that the output of stat is interpreted as octal by (( ... )).