There are a zillion pages explaining how umask works from the user’s point of view, usually with great charts and tables explaining all the bits.

However in all my years I’ve never seen umask explained from the developer’s point of view. What do you provide to creat()/open()/mkdir() (in C) or os.OpenFile/os.Mkdir/os.MkdirAll (in Go) so that the user’s umask is in control.

The Linux manpage open(7) says:

The effective mode is modified by the process's umask in the usual
way: in the absence of a default ACL, the mode of the created
file is (mode & ~umask).

Clear as mud.

The FreeBSD manpage open(2) says:

the file is created with mode mode as described in
chmod(2) and modified by the process' umask value (see umask(2)).

umask(2) is not more clear.

Today (yes, after 25+ years of coding on unix systems) I finally fully understood what those mean.

Here’s how I’d describe it in plain English:

“Set the perm to the most permissive permissions you think are appropriate. The user’s umask will shrink that down based on their personal preferences/needs.”

That’s it! It’s that simple!

Examples

When creating a file, the permission are typically:

  • 0600 == This file is just for the user.
  • 0660 == This file is for the user and their group.
  • 0664 == Like the previous but others can view the file.
  • 0640 == This file is for the user; group can read.

Change any 6 to a 7 if the file should be executable (are you writing a compiler?)

Likewise, typically when creating a directory the permissions would be:

  • 0700 == This directory is just for the user.
  • 0770 == This directory is for the user and their group.
  • 0750 == Like 0770 but their group can only view.

Change the 7 to a 6 (or a 5 to a 4) if you want the user, group, or others to be able to open files if they know the name, but can’t ls to find out what files are there. In your entire career you will never do that (unless you are writing a queue submission system that only uses the local file system… at which point you shouldn’t need this blog post.)

Typical umask settings

I typically set my umask to 0027 which strips out the “other” bits completely (why should I lets me collaborate with people in my team in a safe way (they can read, but not change).

I once met a woman who’s name on Yahoo and AIM (this was a long time ago) was her first name followed by 700. Once day I asked if she was trying to tell people, in a very Unix-y way that she was a very private person. Turns out I was the first to ever guess correctly! We can assume her umask is 0077.

Setting/reading the umask

What if you want to override the umask?

Well, you shouldn’t.

However, if you do, remember to set it back to the original value. That’s why the umask system call (which sets the umask) returns the old value: so that you can

In Go, here’s how you temporarily set the umask:

savedUmask := unix.Umask(forcedUmask)
// Do your thing here.
_ = unix.Umask(savedUmask) // Return the umask to the original

Or, if you just want to know the umask, set it then set it back right away:

umaskValue := unix.Umask(0)
_ = unix.Umask(umaskValue)