16

I'm trying to set a umask using the os module. Please note my normal umask set in my ~/.profile is umask 0027.

In a bash shell,

umask 0022

will allow a file to be created with permissions

-rw-r--r--

However, when us import the os module and do this:

os.umask(0022)
[do some other code here that creates a file]

I get permissions of

----------

First, how do I make os.umask(mask) behave like umask in the shell?

Second, what is the logic between the difference of the two?

Note: I tried converting the 0022 to decimal in case it is expecting a decimal by doing:

os.umask(18)

but it gave permissions of

-----w--w-

Also note, I tried

os.umask(00022)

and

os.mask(0o0022)

Which didn't work either.

narnie
  • 1,604
  • 1
  • 17
  • 33
  • Is it possible the file already exists? You will need to use chmod instead in that case – John La Rooy Apr 24 '12 at 02:59
  • @gnibbler: No, it was deleted before hand, but that is a good thought. – narnie Apr 24 '12 at 03:40
  • @Ignacio Vazquez-Abrams: trying setting umask 0027 in the shell first, then run the script or invoke your interpreter. – narnie Apr 24 '12 at 03:41
  • @Ignacio Vazquez-Abrams: ur right, neither could I with a simple test python program separate from my code. Odd as I'm using the same techniques. – narnie Apr 24 '12 at 04:05
  • if anyone wants to see the code, do "git clone git://git.code.sf.net/p/createlauncher/code createlauncher-code" and look at the file_handler.py module and the main program create_launcher.py. The main site is sourceforge.net/projects/createlauncher – narnie Apr 24 '12 at 04:06
  • don't use the master branch, use the fix_other_buttons branch – narnie Apr 24 '12 at 06:41
  • fix_other_branch now merged into master. Use the master branch version create-launcher-0.1.4+alpha commit hash db099a – narnie Apr 24 '12 at 17:53
  • 1
    Using `old_mask = os.umask(0o000)` and then `os.makedirs(./foo/bar)` create directories with `777` as expected. And `oct(old_mask)` gives `022` which is correct (default umask on Unix). – Stan Mar 16 '15 at 08:46
  • Related: https://stackoverflow.com/questions/15607903/python-module-os-chmodfile-664-does-not-change-the-permission-to-rw-rw-r-bu – Ciro Santilli新疆棉花TRUMP BAN BAD Apr 09 '19 at 11:13

4 Answers4

22

Misunderstanding of umask, I think. The umask sets the default denials, not the default permissions. So

import os
oldmask = os.umask (0o022)
fh1 = os.open ("qq1.junk", os.O_CREAT, 0o777)
fh2 = os.open ("qq2.junk", os.O_CREAT, 0o022)
os.umask (oldmask)
os.close (fh1)
os.close (fh2)

should indeed produce files as follows:

-rwxr-xr-x 1 pax pax 0 Apr 24 11:11 qq1.junk
---------- 1 pax pax 0 Apr 24 11:11 qq2.junk

The umask 022 removes write access for group and others, which is exactly the behaviour we see there. I find it helps to go back to the binary that the octal numbers represent:

 usr grp others 
-rwx rwx rwx is represented in octal as 0777, requested for qq1.junk
-000 010 010 umask of 022 removes any permission where there is a 1
-rwx r-x r-x is the result achieved requesting 0777 with umask of 022

---- -w- -w- is represented in octal as 0022, requested for qq2.junk
-000 010 010 umask of 022 removes any permission where there is a 1
---- --- --- is the result achieved requesting 0022 with umask of 022

The program is behaving as you asked it to, not necessarily as you thought it should. Common situation, that, with computers :-)

pstatix
  • 3,052
  • 1
  • 9
  • 27
JimH44
  • 395
  • 2
  • 12
  • 1
    `umask 022` should result in `755`permissions for directories, but not for files. For files it should be `644`. Or in other words, with `umask 022` directories should be `rwxr-xr-x`, but files should be `rw-r--r--`. – Dominik Nov 09 '16 at 23:08
8

You'll probably need to show us the code that constitutes:

[do some other code here that creates a file]

The code you have works fine on my system:

import os
oldmask = os.umask (022)
fh1 = os.open ("qq1.junk", os.O_CREAT, 0777)
fh2 = os.open ("qq2.junk", os.O_CREAT, 0022)
os.umask (oldmask)
os.close (fh1)
os.close (fh2)

producing files as follows:

-rwxr-xr-x 1 pax pax 0 Apr 24 11:11 qq1.junk
---------- 1 pax pax 0 Apr 24 11:11 qq2.junk

You should also note the restoration of the old umask value which minimises the impact of changing it to the local operation.

As you can see from the results above, you also need to be aware that the umask value is "subtracted" from the mode you're using to create the file and we don't know what that mode is in your particular case.

That's evident even in your bash sample since a umask value of 022 when creating a file of mode 777 would result in r-xr-xr-x, not rw-r--r-- as you have it.


Based on your comments below where you indicate you're using open rather than os.open, a cursory glance of the Python source seems to indicate that this translates to a C fopen call which uses 0666 as the initial mode. This is supported by the slightly modified code:

import os
oldmask = os.umask (022)
fh3 = open ("qq3.junk", "w")
os.umask (0)
fh4 = open ("qq4.junk", "w")
os.umask (oldmask)
fh3.close()
fh4.close()

which gives us:

-rw-r--r-- 1 pax pax 0 Apr 24 11:44 qq3.junk
-rw-rw-rw- 1 pax pax 0 Apr 24 11:44 qq4.junk

So I'm not entirely certain why you're getting 0000 permissions in your case.

It would be worth seeing what the results are when you run that above program in your environment. If it's the same as I get then the problem may well lie somewhere else.

paxdiablo
  • 772,407
  • 210
  • 1,477
  • 1,841
  • I did in the bash shell, umask 0022 then did touch /tmp/test then did ls -l /tmp/test they copied and pasted the permissions directly above. I think you are confusing the differential between creating files (where execute is assumed not desired) and creating directories (where execute permissions desired is assumed). – narnie Apr 24 '12 at 03:15
  • @narnie, this depends _entirely_ on what code is creating the file. Obviously `touch` has different rules to, say, `ld` in terms of the mode it uses. All I was saying is that the permissions of the file depend on the `umask` value _and_ the mode, and the missing code is the bit that would tell us the mode. – paxdiablo Apr 24 '12 at 03:17
  • the code I'm using to generate the file is rather complex as I'm passing file handlers around. I want the umask to be 0022 no matter what the .profile umask might be set to (mine is set to 0027). The jist is I'm just doing f = open(filename, 'w') then passing the file object on to a class that controls an overall object which has a write method that uses the f object to write out the object, returns, where f.close() is run. – narnie Apr 24 '12 at 03:18
  • @paxdiable, I think you are on to what the problem is. Most users will have a umask defaulting to 0022. I want have my code be able to have different umask scenarios be able to be handled within the code. I like to have my umask set to be readable only by me and my primary group and not readable by any other system user. However, it may also be the open(filename, 'w') doesn't care what the system umask is. I'm just now sure hope python handles this. – narnie Apr 24 '12 at 03:23
  • 1
    @narnie, I think at the lower levels, Python just calls fopen for this and that should use a mode of 0666 (prior to umask subtraction). That wouldn't explain why you're getting 0000. It may be that it will turn out easier to use os.open (rather than open) with a specific 0777 mode and whatever umask you want. – paxdiablo Apr 24 '12 at 03:41
  • if anyone wants to see the code, do "git clone git://git.code.sf.net/p/createlauncher/code createlauncher-code" and look at the file_handler.py module and the main program create_launcher.py. The main site is http://sourceforge.net/projects/createlauncher/ – narnie Apr 24 '12 at 04:04
  • I think you are right and that will be the way to go. It sure it strange. Thanks for the help. – narnie Apr 24 '12 at 16:41
7

Being picky/careful, and Python 3k-compatible, here is my slightly different answer (that still doesn't explain what the OP's original issue was):

old_umask = os.umask(0o022) # u=rwx,g=rx,o=rx
try:
    # do stuff

finally:
    os.umask(old_umask)
George Lund
  • 1,178
  • 10
  • 14
0

Even though this would seem to be a straight system call, in this case it does seems to matter what Python version you are using:

It appears that os.open handles the pre-existing umask differently in Python 2.x and Python 3.x, possibly because 2.x is closer to the OS and 3.x does a bit more abstraction.

https://docs.python.org/2/library/os.html "The default mode is 0777 (octal), and the current umask value is first masked out."

There is no similar statement in https://docs.python.org/3/library/os.html

fatal_error
  • 3,985
  • 2
  • 16
  • 16