classification
Title: shutil.copystat fails with PermissionError in WSL
Type: behavior Stage:
Components: Library (Lib), Windows Versions: Python 3.8, Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: benspiller, paul.moore, pspeter, steve.dower, tim.golden, zach.ware
Priority: normal Keywords:

Created on 2019-10-29 14:34 by pspeter, last changed 2020-03-31 11:31 by benspiller.

Messages (2)
msg355655 - (view) Author: Peter (pspeter) Date: 2019-10-29 14:34
Using shutil.copystat (and therefore also shutil.copytree) in WSL on any directories from a mounted Windows drive (i.e. /mnt/c/...) raises an shutil.Error "[Errno 13] Permission denied". 

It seems to fail when copying the extended filesystem attribute "system.wsl_case_sensitive" using os.setxattr() here: 
https://github.com/python/cpython/blob/da6ce58dd5ac109485af45878fca6bfd265b43e9/Lib/shutil.py#L322

Note that this only happens when both src and dst are on the mounted drive. If only one is on the drive and the other is e.g. in the home directory /home/username/, it will not raise an error. 

Quick way to test: 

cd /mnt/c/ && mkdir test1 && mkdir test2 && python -c "import shutil; shutil.copystat('test1', 'test2')"
msg365377 - (view) Author: Ben Spiller (benspiller) * Date: 2020-03-31 11:31
Looks like on WSL the errno is errno.EACCES rather than EPERM, so we just need to change the shutil._copyxattr error handler to also cope with that error code:

             except OSError as e:
-                 if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA):
+                 if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA, errno.EACCES):
                     raise

If anyone needs a workaround until this is fixed in shutil itself, you can do it by monkey-patching _copyxattr:

import errno, shutil
# have to monkey patch to work with WSL as workaround for https://bugs.python.org/issue38633
orig_copyxattr = shutil._copyxattr
def patched_copyxattr(src, dst, *, follow_symlinks=True):
	try:
		orig_copyxattr(src, dst, follow_symlinks=follow_symlinks)
	except OSError as ex:
		if ex.errno != errno.EACCES: raise
shutil._copyxattr = patched_copyxattr
History
Date User Action Args
2020-03-31 11:31:03benspillersetnosy: + benspiller
messages: + msg365377
2019-10-29 14:34:38pspetercreate