permissions – Why do some umask values not take effect?

permissionsumask

I'm trying to understand permissions better, so I'm doing some "exercises". Here's a sequence of commands that I'm using with their respective output:

$ umask
0022
$ touch file1
$ ls -l file1
-rw-r--r-- 1 user group 0 Mar 16 12:55 file1
$ mkdir dir1
$ ls -ld dir1
drwxr-xr-x 2 user group 4096 Mar 16 12:55 dir1

That makes sense because we know that the default file permissions are 666 (rw-rw-rw-) and directories default permissions are 777 (rwxrwxrwx).
If I subtract the umask value from these default permissions I have
666-022=644, rw-r--r--, for the file1, so it's coherent with the previous output;
777-022=755, rwx-r-x-r-x, for the dir1, also coherent.

But if I change the umask from 022 to 021 it isn't any more.

Here is the example for the file:

$ umask 0021
$ touch file2
$ ls -l file2
-rw-r--rw- user group 0 Mar 16 13:33 file2

-rw-r--rw- is 646 but it should be 666-021=645. So it doesn't work according to the previous computation.

Here is the example for the directory:

$ touch dir2
$ ls -ld dir2
drwxr-xrw- 2 user group 4096 Mar 16 13:35 dir2

drwxr-xrw- is 756, 777-021=756. So in this case the result is coherent with the previous computation.

I've read the man but I haven't found anything about this behaviour.

Can somebody explain why?

EXPLANATION

As pointed out in the answers: umask's value is not mathematically subtracted from default directory and file's permissions.

The operation effectively involved is a combination of AND (&) and NOT (!) boolean operators. Given:

R = resulting permissions
D = default permissions
U = current umask

R = D & !U

For example:

666& !0053 = 110 110 110 & 
            !000 101 011 
             110 110 110 &  
             111 010 100
           = 110 010 100 = 624 = rw--w-r--
 
777& !0022 = 111 111 111 & 
            !000 010 010
             111 111 111 &  
             111 101 101
           = 111 101 101 = 755 = rwxr--xr-x 

TIP

An easy way to quickly know the resulting permissions (at least it helped me) is to think that we can use just 3 decimal values:

r = 100 = 4 
w = 010 = 2
x = 001 = 1

Permissions will be a combination of these 3 values.
" " is used to indicate that the relative permission is not given.

666 = 4+2+" "   4+2+" "   4+2+" "  = rw rw rw  

So if my current umask is 0053 I know I'm removing read and execution (4+1) permission from group and write and execution (2+1) from other resulting in

 4+2     " "+2+" "     4+" "+" "   = 624 = rw--w-r--  

(group and other already hadn't execution permission)

Best Answer

umask is a mask, it’s not a subtracted value. Thus:

  • mode 666, mask 022: the result is 666 & ~022, i.e. 666 & 755, which is 644;
  • mode 666, mask 021: the result is 666 & ~021, i.e. 666 & 756, which is 646.

Think of the bits involved. 6 in a mode means bits 1 and 2 are set, read and write. 2 in a mask masks bit 1, the write bit. 1 in a mask masks bit 0, the execute bit.

Another way to represent this is to look at the permissions in text form. 666 is rw-rw-rw-; 022 is ----w--w-; 021 is ----w---x. The mask drops its set bits from the mode, so rw-rw-rw- masked by ----w--w- becomes rw-r--r--, masked by ----w---x becomes rw-r--rw-.

Related Question