Repository: conda/conda

Issue:
PermissionError writing to environments.txt
    "command": "conda install jupyter_core --yes"

```
Traceback (most recent call last):
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/exceptions.py\", line 721, in __call__
    return func(*args, **kwargs)
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/cli/main.py\", line 78, in _main
    exit_code = do_call(args, p)
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/cli/conda_argparse.py\", line 75, in do_call
    exit_code = getattr(module, func_name)(args, parser)
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/cli/main_install.py\", line 11, in execute
    install(args, parser, 'install')
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/cli/install.py\", line 239, in install
    handle_txn(progressive_fetch_extract, unlink_link_transaction, prefix, args, newenv)
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/cli/install.py\", line 269, in handle_txn
    unlink_link_transaction.execute()
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/core/link.py\", line 220, in execute
    self.verify()
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/common/io.py\", line 402, in decorated
    return f(*args, **kwds)
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/core/link.py\", line 207, in verify
    exceptions = self._verify(self.prefix_setups, self.prefix_action_groups)
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/core/link.py\", line 460, in _verify
    cls._verify_transaction_level(prefix_setups),
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/core/link.py\", line 455, in <genexpr>
    exceptions = tuple(exc for exc in concatv(
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/core/link.py\", line 323, in _verify_individual_level
    error_result = axn.verify()
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/core/path_actions.py\", line 870, in verify
    touch(USER_ENVIRONMENTS_TXT_FILE, mkdir=True, sudo_safe=True)
  File \"/home/nbcommon/anaconda3_420/lib/python3.5/site-packages/conda/gateways/disk/update.py\", line 89, in touch
    os.chown(path, uid, gid)
PermissionError: [Errno 1] Operation not permitted: '/home/nbcommon/.conda/environments.txt'
```


Discussion:

Resolving patch:
diff --git a/conda/base/context.py b/conda/base/context.py
--- a/conda/base/context.py
+++ b/conda/base/context.py
@@ -982,7 +982,7 @@ def _first_writable_envs_dir():
             return envs_dir
 
     from ..exceptions import NotWritableError
-    raise NotWritableError(context.envs_dirs[0])
+    raise NotWritableError(context.envs_dirs[0], None)
 
 
 # backward compatibility for conda-build
diff --git a/conda/exceptions.py b/conda/exceptions.py
--- a/conda/exceptions.py
+++ b/conda/exceptions.py
@@ -591,12 +591,13 @@ def __init__(self, message):
         super(SafetyError, self).__init__(message)
 
 
-class NotWritableError(CondaError):
+class NotWritableError(CondaError, OSError):
 
-    def __init__(self, path):
-        kwargs = {
+    def __init__(self, path, errno, **kwargs):
+        kwargs.update({
             'path': path,
-        }
+            'errno': errno,
+        })
         if on_win:
             message = dals("""
             The current user does not have write permissions to a required path.
diff --git a/conda/gateways/disk/update.py b/conda/gateways/disk/update.py
--- a/conda/gateways/disk/update.py
+++ b/conda/gateways/disk/update.py
@@ -12,6 +12,7 @@
 from .link import lexists
 from ...common.compat import on_win
 from ...common.path import expand
+from ...exceptions import NotWritableError
 
 log = getLogger(__name__)
 
@@ -62,29 +63,32 @@ def touch(path, mkdir=False, sudo_safe=False):
     #   True if the file did not exist but was created
     #   False if the file already existed
     # raises: permissions errors such as EPERM and EACCES
-    path = expand(path)
-    log.trace("touching path %s", path)
-    if lexists(path):
-        utime(path, None)
-        return True
-    else:
-        dirpath = dirname(path)
-        if not isdir(dirpath) and mkdir:
-            if sudo_safe:
-                mkdir_p_sudo_safe(dirpath)
-            else:
-                mkdir_p(dirpath)
-        else:
-            assert isdir(dirname(path))
-        try:
-            fh = open(path, 'a')
-        except:
-            raise
+    try:
+        path = expand(path)
+        log.trace("touching path %s", path)
+        if lexists(path):
+            utime(path, None)
+            return True
         else:
-            fh.close()
-            if sudo_safe and not on_win and os.environ.get('SUDO_UID') is not None:
-                uid = int(os.environ['SUDO_UID'])
-                gid = int(os.environ.get('SUDO_GID', -1))
-                log.trace("chowning %s:%s %s", uid, gid, path)
-                os.chown(path, uid, gid)
-            return False
+            dirpath = dirname(path)
+            if not isdir(dirpath) and mkdir:
+                if sudo_safe:
+                    mkdir_p_sudo_safe(dirpath)
+                else:
+                    mkdir_p(dirpath)
+            else:
+                assert isdir(dirname(path))
+            try:
+                fh = open(path, 'a')
+            except:
+                raise
+            else:
+                fh.close()
+                if sudo_safe and not on_win and os.environ.get('SUDO_UID') is not None:
+                    uid = int(os.environ['SUDO_UID'])
+                    gid = int(os.environ.get('SUDO_GID', -1))
+                    log.trace("chowning %s:%s %s", uid, gid, path)
+                    os.chown(path, uid, gid)
+                return False
+    except (IOError, OSError) as e:
+        raise NotWritableError(path, e.errno, caused_by=e)


Test patch: