Why doesn't umask change execute permissions on files?

umask is subtractive, not prescriptive: permission bits set in umask are removed by default from modes specified by programs, but umask can't add permission bits. touch specifies mode 666 by default (the link is to the GNU implementation, but others behave in the same way; this is specified by POSIX), so the resulting file ends up with that masked by the current umask: in your case, since umask doesn't mask anything, the result is 666.

The mode of a file or directory is usually specified by the program which creates it; most system calls involved take a mode (e.g. open(2), creat(2), mkdir(2) all have a mode parameter; but fopen(2) doesn't, and uses mode 666). Unless the parent directory specifies a default ACL, the process's umask at the time of the call is used to mask the specified mode (bitwise mode & ~umask; effectively this subtracts each set of permissions in umask from the mode), so the umask can only reduce a mode, it can't increase it. If the parent directory specifies a default ACL, that's used instead of the umask: the resulting file permissions are the intersection of the mode specified by the creating program, and that specified by the default ACL.

POSIX specifies that the default mode should be 666 for files, 777 for directories; but this is just a documentation default (i.e., when reading POSIX, if a program or function doesn't specify a file or directory's mode, the default applies), and it's not enforced by the system. Generally speaking this means that POSIX-compliant tools specify mode 666 when creating a file, and mode 777 when creating a directory, and the umask is subtracted from that; but the system can't enforce this, because there are many legitimate reasons to use other modes and/or ignore umask:

  • compilers creating an executable try to produce a file with the executable bits set (they do apply umask though);
  • chmod(1) obviously specifies the mode depending on its parameters, and it ignores umask when "who" is specified, or the mode is fully specified (so chmod o+x ignores umask, as does chmod 777, but chmod +w applies umask);
  • tools which preserve permissions apply the appropriate mode and ignore umask: e.g. cp -p, tar -p;
  • tools which take a parameter fully specifying the mode also ignore umask: install --mode, mknod -m...

So you should think of umask as specifying the permission bits you don't want to see set by default, but be aware that this is just a request. You can't use it to specify permission bits you want to see set, only those you want to see unset. Furthermore, any process can change its umask anyway, using the umask(2) system call! The umask(2) system call is also the only POSIX-defined way for a process to find out its current umask (inherited from its parent). On Linux, starting with kernel 4.7, you can see a process's current umask by looking for Umask in /proc/${pid}/status.

(For the sake of completeness, I'll mention that the behaviour regarding setuid, setgid and sticky bits is system-dependent, and remote filesystems such as NFS can add their own twists.)


The formula to calculate your file permission:

default_mode & ~umask

(Read the description of O_CREAT flag)

Also specified by POSIX, the default mode for file is S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP | S_IRUSR | S_IWUSR or 666, the default mode for directory is S_IRWXU | S_IRWXG | S_IRWXO or 777 if it was not specified by application.

Since when the execution bits in file default mode are 0, perform bitwise AND & with 0 is always 0.

That's why whatever value of umask, your newly created files will have execution bits off if your application don't specify the execution bits.


Above description is very good. It provides clear description of umask and permissions. To be more pratical, If you want to see, what are permissions provided by "touch" do

" strace touch /tmp/new.txt "

observe the system call used by touch to create "new.txt"

for ex. o/p of "strace touch /tmp/new.txt" (... -> some content)

...

open("/tmp/new.txt", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = 3

...

for reference strace