19

How can I reset permission to default according to the mask, so they will have permissions set as the file was just created

Example of what I want to achieve: umask is set to 0022 so

touch file
mkdir directory

file's permissions now are rw-r--r--

directory's permissons now are rwxr-xr-x

chmod 777 file
chmod 777 directory

file's permissions now are rwxrwxrwx

directory's permissions now are rwxrwxrwx

is there a way to reset perms back to default so file will be rw-r--r-- and directory rwxr-xr-x using chmod?

4 Answers 4

19

Using subtraction is only correct for some umask values - it's better to use mode & ~umask, like in PSkocik's answer, or chmod =rw (or chmod =rwx for directories) which applies the umask as specified by the standard, as noted by ilkkachu in the comments.


In some cases, you can just subtract the umask from 0666 for files and 0777 for directories to get default permissions:

$ printf "%04d\n" "$((0777 - $(umask)))"
0755
$ printf "%04d\n" "$((0666 - $(umask)))"
0644

Accordingly, you can apply chmod:

chmod $((0666 - $(umask))) file
chmod $((0777 - $(umask))) directory

In bash, you have to use printf to force the output in octal:

$ printf "%04d\n" "$((0777 - $(umask)))"
0493
$ printf "%04o\n" "$((0777 - $(umask)))"
0755

Another way would be to create a new file and directory, and use them as reference:

touch file2
mkdir directory2 
chmod --reference=directory2 directory
chmod --reference=file2 file
7
  • 3
    also I found something like chmod =rwx file, not sure what exactly it does but looks like it resets permissions to default too but only for directories
    – lllook
    Dec 16, 2015 at 23:08
  • 1
    Note that Bash, unlike ZSH, treats values beginning with 0 as octal values. For these examples to work in Bash, the numbers need to be prefixed with 10#. For example: chmod $((10#0666 - 10#$(umask))) file Jan 5, 2021 at 20:46
  • The produces incorrect results for files if the umask has “1” anywhere (uncommon perhaps but still possible). umask isn’t subtracted. May 25, 2021 at 10:59
  • @JohnKarahalis, as long as there's no carry, it doesn't matter if the numbers are taken as octal or decimal. E.g. 6 - 4 = 2 in either. 4 - 6 would differ, though. But like Stephen says, the umask shouldn't be subtracted but bitwise modified. So we need the numbers to be interpreted correctly. In Bash, Dash, and Ksh, printf "%o\n" "$(( 0666 & ~$(umask) ))" should work. Zsh needs printf "%o\n" "$(( 8#0666 & ~8#$(umask) ))", but then that doesn't work in Dash.
    – ilkkachu
    May 25, 2021 at 14:49
  • 2
    chmod =rwx file also works, it sets the permissions to 0777 modified by the umask. See the sentences with "if who is not specified" in pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/…
    – ilkkachu
    May 25, 2021 at 14:52
9

The masks are applied via bitwise AND with the bitwise negated mask so if you want to create your own final permission mode, you can do:

$((mode & ~umask))

You'll need to print that back in octal so that you can pass it to chmod:

$ chmod `printf '%o' $((0777 & ~$(umask)))` directory
$ chmod `printf '%o' $((0777 & ~0111 & ~$(umask)))` file
#^additional implicit mask of 0111 for files

where 0777 is the permission mode you want to apply the mask to (you can obtain it with stat -c %a file or stat -c %a directory).

You can echo the above to see what the process substitution will evaluate to (for an umask of 0022, you will get 755 and 644).

You could make a generic function out of it:

#takes a umask as first param and applies it to each folowing param (files)
maskMode(){
  local mask="$1" dmask mode a
  dmask="$((mask & ~0111))"; shift
  for a; do
    mode=0`stat -c "%a" "$a"`
    chmod `printf "%0.4o\n" $(($mode & ~mask))` "$a"
  done
}

For your particular use, chmod reference files are another option.

0
3

If you want to do it recursively through the current directory -

chmod -R $((777 - `umask`)) .
chmod -R -x+X .

You can replace `umask` with $(umask).

2
  • Backquotes, not single quotes
    – nomad
    May 15, 2021 at 6:21
  • I don't think chmod -R -x+X . works. It did not seem to have any effect when I tried it.
    – Andrew
    Feb 17, 2022 at 17:52
0

Recursive files and directories

For anyone looking to reset all files and directories recursively within a parent directory:

sudo find . -type f -print0 | xargs -0 sudo chmod $((0666 - $(umask)))
sudo find . -type d -print0 | xargs -0 sudo chmod $((0777 - $(umask)))

Explanation

The other answers showed how to reset individual files and directories (or apply one rule to both recursively) with chmod -R $((0666 - $(umask))) and chmod -R $((0777 - $(umask))) respectively. (Great answers, thanks!)

I then found this answer showing how to apply permissions selectively using find throughout the directory:

find . -type f -print0 | xargs -0 chmod 644

This will change files recursively, and therefore

find . -type d -print0 | xargs -0 chmod 755

will apply to directories.

Stringing these answers together provides the above solution. Note that this requires the GNU version of find and xargs for the -print0/-0 options, respectively.

Hope it helps others!

You must log in to answer this question.

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