Patchel shutil.copystat to avoid PermissionError on Lustre (#27247)
This commit is contained in:
		| @@ -84,6 +84,74 @@ | ||||
|     "visit_directory_tree", | ||||
| ] | ||||
| 
 | ||||
| if sys.version_info < (3, 7, 4): | ||||
|     # monkeypatch shutil.copystat to fix PermissionError when copying read-only | ||||
|     # files on Lustre when using Python < 3.7.4 | ||||
| 
 | ||||
|     def copystat(src, dst, follow_symlinks=True): | ||||
|         """Copy file metadata | ||||
|         Copy the permission bits, last access time, last modification time, and | ||||
|         flags from `src` to `dst`. On Linux, copystat() also copies the "extended | ||||
|         attributes" where possible. The file contents, owner, and group are | ||||
|         unaffected. `src` and `dst` are path names given as strings. | ||||
|         If the optional flag `follow_symlinks` is not set, symlinks aren't | ||||
|         followed if and only if both `src` and `dst` are symlinks. | ||||
|         """ | ||||
| 
 | ||||
|         def _nop(args, ns=None, follow_symlinks=None): | ||||
|             pass | ||||
| 
 | ||||
|         # follow symlinks (aka don't not follow symlinks) | ||||
|         follow = follow_symlinks or not (os.path.islink(src) and os.path.islink(dst)) | ||||
|         if follow: | ||||
|             # use the real function if it exists | ||||
|             def lookup(name): | ||||
|                 return getattr(os, name, _nop) | ||||
| 
 | ||||
|         else: | ||||
|             # use the real function only if it exists | ||||
|             # *and* it supports follow_symlinks | ||||
|             def lookup(name): | ||||
|                 fn = getattr(os, name, _nop) | ||||
|                 if sys.version_info >= (3, 3): | ||||
|                     if fn in os.supports_follow_symlinks:  # novermin | ||||
|                         return fn | ||||
|                 return _nop | ||||
| 
 | ||||
|         st = lookup("stat")(src, follow_symlinks=follow) | ||||
|         mode = stat.S_IMODE(st.st_mode) | ||||
|         lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns), follow_symlinks=follow) | ||||
| 
 | ||||
|         # We must copy extended attributes before the file is (potentially) | ||||
|         # chmod()'ed read-only, otherwise setxattr() will error with -EACCES. | ||||
|         shutil._copyxattr(src, dst, follow_symlinks=follow) | ||||
| 
 | ||||
|         try: | ||||
|             lookup("chmod")(dst, mode, follow_symlinks=follow) | ||||
|         except NotImplementedError: | ||||
|             # if we got a NotImplementedError, it's because | ||||
|             #   * follow_symlinks=False, | ||||
|             #   * lchown() is unavailable, and | ||||
|             #   * either | ||||
|             #       * fchownat() is unavailable or | ||||
|             #       * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW. | ||||
|             #         (it returned ENOSUP.) | ||||
|             # therefore we're out of options--we simply cannot chown the | ||||
|             # symlink.  give up, suppress the error. | ||||
|             # (which is what shutil always did in this circumstance.) | ||||
|             pass | ||||
|         if hasattr(st, "st_flags"): | ||||
|             try: | ||||
|                 lookup("chflags")(dst, st.st_flags, follow_symlinks=follow) | ||||
|             except OSError as why: | ||||
|                 for err in "EOPNOTSUPP", "ENOTSUP": | ||||
|                     if hasattr(errno, err) and why.errno == getattr(errno, err): | ||||
|                         break | ||||
|                 else: | ||||
|                     raise | ||||
| 
 | ||||
|     shutil.copystat = copystat | ||||
| 
 | ||||
| 
 | ||||
| def getuid(): | ||||
|     if is_windows: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Ben Wibking
					Ben Wibking