If I create a file as an unprivileged user, and change the permissions mode to 400, it's seen by that user as read-only, correctly:

$ touch somefile
$ chmod 400 somefile
$ [ -w somefile ] && echo rw || echo ro

All is well.

But then root comes along:

# [ -w somefile ] && echo rw || echo ro

What the heck? Sure, root can write to read-only files, but it shouldn't make a habit of it: Best Practice would tend to dictate that I should be able to test for the write permission bit, and if it's not, then it was set that way for a reason.

I guess I want to understand both why this is happening, and how can I get a false return code when testing a file that doesn't have the write bit set?

  • btw I'm using both RHEL6 (4.1.2(1)-release) and RHEL7 (4.2.46(2)-release).
    – Rich
    May 3, 2018 at 1:54
  • 16
    "Best Practice would tend to dictate that I should be able to test for the write permission bit, and if it's not, then it was set that way for a reason." - Actually, best practice is "don't run stuff as root." If you're running as root, you've already decided to bypass permission checks. Manually re-implementing those permission checks in userspace is a recipe for disaster.
    – Kevin
    May 3, 2018 at 5:46
  • @Kevin Good for you if you can run stuff unprivileged. This is for manipulating /etc/dhcp/dhcpd.conf, which is owned by root. I'm using the vendor-supplied dhcpd. Total disaster, huh? The file is checked into RCS, I'm automating use of rcsdiff, ci and co because we have operators that need to ... operate. The permission bit check (-w, as detailed by test(1)) was going to be a first line of failure, working on the basis that ci -u leaves a file read-only. I'm ditching that and going straight to rcsdiff -q and checking $?. Undisastrous dhcpd? It would be owned by dhcpd.
    – Rich
    May 3, 2018 at 17:34
  • 1
    It's a potential disaster because you now have two different implementations of permissions checks: one in the kernel and one in userspace. Worse, those implementations are not even intended to produce identical results, so you can't just fuzz test them against each other. So now you have two paths to access which have to be locked down and secured independently of each other.
    – Kevin
    May 3, 2018 at 17:55
  • @Kevin Sure, they don't produce identical results and weren't intended to (despite the paucity of detail in manpages), but I explicitly want to check the write-permission bit; and the manpages for bash and test led me to believe that's what [ -w is for.
    – Rich
    May 3, 2018 at 18:18

3 Answers 3


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...

  • No, "misunderstanding" would be reading the manpages wrong for -w: test(1) is explicit: "FILE exists and write permission is granted", not "file may be written by current user". Nothing about overriding permissions, nor ACLs. bash(1) is cagey: "True if file exists and is writable." ksh(1) makes a subtle hint toward shenanigans: "-w file // True, if file exists and is writable by current process." zsh(1) defers to test(1), zshmisc(1) is worded as ksh(1), and zshexpn(1) details some interesting permissions-based globbing. csh(1) is hilariously brief: "Write access".
    – Rich
    May 3, 2018 at 17:06
  • btw: I accepted Patrick's answer, 1. because you didn't adequately answer the lead-on question: how can I get a false return code when testing a file that doesn't have the write bit set? and 2. because of the snarky lead-in presuming I misunderstood the manpages.
    – Rich
    May 3, 2018 at 17:38
  • 3
    @Rich The way I read this, I think your comments are coming across as a bit snarky as well. I'm not saying that anything you wrote was wrong, just that it would probably go over better if expressed in a more polite way.
    – David Z
    May 3, 2018 at 21:04
  • 3
    "I think you have have misunderstood..." is not snarky. Your response however, 100% snarky.
    – barbecue
    May 3, 2018 at 22:15
  • @Rich Also, while the manpages might not have been crystal clear, Stephen has stated that you may have "misunderstood what -w does", and not what manpages say, where the former looks to be the case, hence the question
    – Sebi
    May 3, 2018 at 23:43

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 (( ... )).

  • Thanks for being concise. Good use of (( ... & ... )). One typo corrected :-)
    – Rich
    May 3, 2018 at 17:42
  • Make that 3... No if required, octal permissions output is %a not %d, and (( ... )) needs a prefixed 0 to interpret the output of stat as octal.
    – Rich
    May 3, 2018 at 17:55
  • @Patrick my man stat says "%d device number in decimal", but what we want are the "access rights", no? Your point about needing the 0 prefix is well-made, but I guess we just have to bodge it in there :).
    – sourcejedi
    May 3, 2018 at 21:37
  • 2
    stat -c 0%a ...
    – sourcejedi
    May 3, 2018 at 21:38
  • @sourcejedi ack, you're right. For some reason I was thinking %d was access rights in decimal. Restored the edit. thanks :-)
    – phemmer
    May 3, 2018 at 21:39

The root user can do as she pleases, "normal" file permissions are no limitation. It won't directly execute a plain file without any eXecute permissions, just for a bit of insurance against foot target practice.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .