2

I wrote following code to try to create a directory with 0777 mode on Linux:

#include <sys/stat.h>
#include <sys/types.h>

int main () {

    mkdir("/tmp/mkdir-test", 0777);
    return 0;

}

But actually, the new directory has 0755 mode.

# stat /tmp/mkdir-test
  File: `/tmp/mkdir-test'
  Size: 4096        Blocks: 8          IO Block: 4096   directory
Device: 802h/2050d  Inode: 1772304     Links: 2
Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2016-09-27 20:23:54.000000000 -0700
Modify: 2016-09-27 20:23:54.000000000 -0700
Change: 2016-09-27 20:23:54.000000000 -0700

Can someone explain this? And how can the program create a real 0777 mode directory?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
cartman
  • 732
  • 2
  • 8
  • 20

1 Answers1

10

Run umask in the shell; it will report 022. The bits set in the umask value are removed from permissions when creating files or directories.

In a single-threaded program, one way to really set 0777 permissions is to dink with umask():

mode_t old_mask = umask(0);
mkdir("/tmp/mkdir-test", 0777);
umask(old_mask);

That preserves the current setting except when you are sure you need to override it. However, this is dangerous in multi-threaded programs because the umask value is global per-process, not per-thread (and thanks to Peter Cordes for pointing this out).

The other option for setting the permissions is to use chmod(), and doing so is safe with multi-threaded programs too:

const char *dirname = "/tmp/mkdir-test";
mode_t target_mode = 0777;
if (mkdir(dirname, 0) == 0)
    chmod(dirname, target_mode);

The permissions set by the chmod() function are not affected by umask values.

The permissions on the call to mkdir() are probably best set to 0 as shown; it will always work reliably and doesn't risk affecting other threads by modifying the umask value. Alternatively, you could use the desired target permissions in the call to mkdir() if you wanted to (and a previous version of this answer suggested doing so, using 0777 as the hard-coded target permissions).

const char *dirname = "/tmp/mkdir-test";
mode_t target_mode = 0777;
if (mkdir(dirname, target_mode) == 0)
    chmod(dirname, target_mode);

If you use this idiom, it is important that the mode passed to mkdir() is the same as, or less permissive than, the mode passed to chmod() — and 0 is less permissive than any other mode (and always works). If you use a more relaxed mode in the call to mkdir(), there's a TOCTOU (Time-of-Check, Time-of-Use) style vulnerability between the mkdir() and chmod() calls during which someone (some process) could get into the directory with relaxed permissions and wreak havoc; this could be a security risk.

Note that umask() is a very simple system call and therefore very quick too (as system calls go, compared with heavyweights such as open(), mkdir() or chmod()).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • And it is even documented, @walker, in the docs for `mkdir(3)`. For example, the Linux version says "The file permission bits of the new directory shall be initialized from `mode`. **These file permission bits of the `mode` argument shall be modified by the process' file creation mask**" (emphasis added). – John Bollinger Sep 28 '16 at 04:37
  • @JohnBollinger I just tried to call chmod after mkdir to solve the issue. it also works. My question is what's the different between two ways? Which way is more recommended? – cartman Sep 28 '16 at 06:44
  • 1
    I think `umask()`, `mkdir()`, `umask()` is probably better, unless you need to set the `S_ISVTX` (sticky bit) or the `S_ISGUID` bit on the directory. For those, on macOS Sierra (and Mac OS X before it), you need to use `chmod()` because `mkdir(2)` says: _Note: the behavior of mkdir() is undefined when mode bits other than the low 9 bits are used. Use chmod(2) after mkdir() to explicitly set the other bits._. So, if you need to call `chmod()` anyway, use `mkdir()` plus `chmod()` — otherwise use `umask()`, `mkdir()`, `umask()`. – Jonathan Leffler Sep 28 '16 at 06:52
  • Yes, wherever possible, it is best to create files / directories with the intended mode from the beginning, instead of changing the mode after creation. Additionally, if you need to create multiple directories and / or files, then modifying the umask once for all (and maybe restoring it later) saves you from performing a `chmod()` on each. – John Bollinger Sep 28 '16 at 13:47
  • 1
    Contrary opinion: [File Permission Problems In Unix](https://stackoverflow.com/a/12391417) - umask is global, not per thread; treat it as a user configuration that you don't mess with. You can always start with very restricted permissions and relax them with `chmod`. Unless I'm mistaken, a `umask` setting can't give more permissions that `mkdir()` specified so the TOCTOU-like vulnerability is impossible. – Peter Cordes Jun 19 '20 at 22:19
  • @PeterCordes — thread-safety is an important point; thank you. I've revised the answer to account for that. Using 0 for the mode in the call to `mkdir()` and then using `chmod()` should always be safe. No `umask` setting adds bits to the value specified in `mkdir()` or any other 'create file' operation; it can only remove bits. – Jonathan Leffler Jun 19 '20 at 22:59
  • I don't know what I'm doing wrong, but the permission wasn't set to 0777. –  Sep 03 '21 at 09:52
  • @RichardMcFriendOluwamuyiwa — without seeing your code and knowing your `umask` value and knowing what you _do_ get as the result, it is not possible to help you. It may be best to ask a new question cross-referencing this one. Don't forget to check the return value from system calls. – Jonathan Leffler Sep 03 '21 at 12:37
  • I resolved it already. I'll post my solution as an answer so that it can help anyone else coming across this later –  Sep 03 '21 at 13:27