From 9ae49e3f3bdf585473f03522a1b7dd7c9e4baa6a Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 30 May 2023 12:33:17 -0700 Subject: [PATCH] gh-88745: Add _winapi.CopyFile2 and update shutil.copy2 to use it (GH-105055) (cherry picked from commit cda1bd3c9d3b2cecdeeba0c498cd2df83fbdb535) Co-authored-by: Steve Dower --- Doc/data/python3.12.abi | 1935 +++++++++-------- .../pycore_global_objects_fini_generated.h | 3 + Include/internal/pycore_global_strings.h | 3 + .../internal/pycore_runtime_init_generated.h | 3 + .../internal/pycore_unicodeobject_generated.h | 9 + Lib/shutil.py | 25 + ...3-05-29-11-38-53.gh-issue-88745.cldf9G.rst | 3 + Modules/_winapi.c | 93 + Modules/clinic/_winapi.c.h | 72 +- 9 files changed, 1186 insertions(+), 960 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2023-05-29-11-38-53.gh-issue-88745.cldf9G.rst diff --git a/Doc/data/python3.12.abi b/Doc/data/python3.12.abi index 9802893e69cca7..94b09516b511e1 100644 --- a/Doc/data/python3.12.abi +++ b/Doc/data/python3.12.abi @@ -867,6 +867,7 @@ + @@ -1701,7 +1702,7 @@ - + @@ -1719,7 +1720,7 @@ - + @@ -1729,10 +1730,10 @@ - + - + @@ -1820,11 +1821,11 @@ - + - + @@ -1836,14 +1837,14 @@ - + - + @@ -1862,7 +1863,7 @@ - + @@ -1872,7 +1873,7 @@ - + @@ -1914,13 +1915,13 @@ - + - + @@ -1928,7 +1929,7 @@ - + @@ -2008,7 +2009,7 @@ - + @@ -2039,7 +2040,7 @@ - + @@ -2067,7 +2068,7 @@ - + @@ -3364,7 +3365,7 @@ - + @@ -3418,7 +3419,7 @@ - + @@ -3524,7 +3525,7 @@ - + @@ -3533,7 +3534,7 @@ - + @@ -3589,7 +3590,7 @@ - + @@ -3611,12 +3612,12 @@ - + - + @@ -3633,7 +3634,7 @@ - + @@ -3775,7 +3776,7 @@ - + @@ -3846,7 +3847,7 @@ - + @@ -3905,7 +3906,7 @@ - + @@ -3928,7 +3929,7 @@ - + @@ -4657,7 +4658,7 @@ - + @@ -4691,7 +4692,7 @@ - + @@ -5032,7 +5033,7 @@ - + @@ -5129,7 +5130,7 @@ - + @@ -5317,7 +5318,7 @@ - + @@ -5355,7 +5356,7 @@ - + @@ -5571,7 +5572,7 @@ - + @@ -5636,7 +5637,7 @@ - + @@ -5658,7 +5659,7 @@ - + @@ -5714,7 +5715,7 @@ - + @@ -5973,7 +5974,7 @@ - + @@ -6090,7 +6091,7 @@ - + @@ -6227,7 +6228,7 @@ - + @@ -6527,7 +6528,7 @@ - + @@ -6536,7 +6537,7 @@ - + @@ -6760,136 +6761,141 @@ - - + + + - - + + - - + + - - + + - - + + - - - + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - + + + - + @@ -6978,7 +6984,7 @@ - + @@ -7171,7 +7177,7 @@ - + @@ -7302,7 +7308,7 @@ - + @@ -7397,7 +7403,7 @@ - + @@ -7426,7 +7432,7 @@ - + @@ -7567,7 +7573,7 @@ - + @@ -7623,7 +7629,7 @@ - + @@ -7637,7 +7643,7 @@ - + @@ -7694,7 +7700,7 @@ - + @@ -7849,7 +7855,7 @@ - + @@ -7877,7 +7883,7 @@ - + @@ -7904,7 +7910,7 @@ - + @@ -8086,7 +8092,7 @@ - + @@ -8098,7 +8104,7 @@ - + @@ -8396,7 +8402,7 @@ - + @@ -8531,7 +8537,7 @@ - + @@ -8576,7 +8582,7 @@ - + @@ -8591,7 +8597,7 @@ - + @@ -8604,7 +8610,7 @@ - + @@ -8632,7 +8638,7 @@ - + @@ -8660,7 +8666,7 @@ - + @@ -8731,7 +8737,7 @@ - + @@ -8750,7 +8756,7 @@ - + @@ -8971,7 +8977,7 @@ - + @@ -8987,7 +8993,7 @@ - + @@ -9009,7 +9015,7 @@ - + @@ -9678,7 +9684,7 @@ - + @@ -9692,7 +9698,7 @@ - + @@ -9710,7 +9716,7 @@ - + @@ -10830,7 +10836,7 @@ - + @@ -10877,10 +10883,10 @@ - + - + @@ -11106,7 +11112,7 @@ - + @@ -11907,7 +11913,7 @@ - + @@ -13337,12 +13343,12 @@ - + - + @@ -13355,19 +13361,19 @@ - + - + - + - + - + @@ -13419,7 +13425,7 @@ - + @@ -13433,18 +13439,18 @@ - + - + - - + + - - + + @@ -13473,7 +13479,7 @@ - + @@ -13488,7 +13494,7 @@ - + @@ -13509,13 +13515,13 @@ - + - + - + @@ -13551,7 +13557,7 @@ - + @@ -13623,7 +13629,7 @@ - + @@ -13652,7 +13658,7 @@ - + @@ -14252,7 +14258,7 @@ - + @@ -14312,7 +14318,7 @@ - + @@ -14366,7 +14372,7 @@ - + @@ -14525,7 +14531,7 @@ - + @@ -14609,7 +14615,7 @@ - + @@ -14666,1027 +14672,1036 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -15809,20 +15824,20 @@ - + - + - + - + - + - + @@ -15982,7 +15997,7 @@ - + @@ -15993,7 +16008,7 @@ - + @@ -16526,7 +16541,7 @@ - + @@ -16635,7 +16650,7 @@ - + @@ -17369,8 +17384,8 @@ - + @@ -17601,7 +17616,7 @@ - + @@ -18891,7 +18906,7 @@ - + @@ -18929,7 +18944,7 @@ - + @@ -20794,11 +20809,13 @@ + + @@ -20935,8 +20952,8 @@ - - + + @@ -21003,7 +21020,7 @@ - + @@ -21058,7 +21075,7 @@ - + @@ -21083,7 +21100,7 @@ - + @@ -21121,7 +21138,7 @@ - + @@ -21227,7 +21244,7 @@ - + @@ -21238,17 +21255,17 @@ - + - + - + @@ -21316,7 +21333,7 @@ - + @@ -21336,7 +21353,7 @@ - + @@ -21349,7 +21366,7 @@ - + @@ -21456,7 +21473,7 @@ - + @@ -21505,7 +21522,7 @@ - + @@ -21522,12 +21539,12 @@ - + - + @@ -21897,7 +21914,7 @@ - + @@ -22063,7 +22080,7 @@ - + @@ -22154,7 +22171,7 @@ - + @@ -22553,7 +22570,7 @@ - + @@ -22720,7 +22737,7 @@ - + @@ -22809,7 +22826,7 @@ - + @@ -22820,7 +22837,7 @@ - + @@ -22978,7 +22995,7 @@ - + @@ -23070,53 +23087,53 @@ - - - + + + - - + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - + + - - - + + + - - - + + + - + @@ -23126,7 +23143,7 @@ - + @@ -23212,7 +23229,7 @@ - + @@ -23226,7 +23243,7 @@ - + @@ -23331,7 +23348,7 @@ - + @@ -23403,7 +23420,7 @@ - + @@ -23508,21 +23525,21 @@ - + - + - + - + @@ -23531,7 +23548,7 @@ - + @@ -23605,7 +23622,7 @@ - + @@ -23821,7 +23838,7 @@ - + @@ -24162,11 +24179,11 @@ - + - + @@ -24241,7 +24258,7 @@ - + @@ -24260,7 +24277,7 @@ - + @@ -24367,7 +24384,7 @@ - + @@ -24396,7 +24413,7 @@ - + @@ -24412,7 +24429,7 @@ - + @@ -24484,7 +24501,7 @@ - + @@ -24493,7 +24510,7 @@ - + @@ -24529,7 +24546,7 @@ - + @@ -24541,7 +24558,7 @@ - + @@ -24626,7 +24643,7 @@ - + @@ -25346,7 +25363,7 @@ - + @@ -25553,14 +25570,14 @@ - + - + @@ -25579,7 +25596,7 @@ - + @@ -25816,7 +25833,7 @@ - + @@ -26073,7 +26090,7 @@ - + @@ -26104,7 +26121,7 @@ - + @@ -26116,10 +26133,10 @@ - + - + @@ -26333,7 +26350,7 @@ - + @@ -26359,7 +26376,7 @@ - + diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 5a1993eac23a8a..d5819fcd1c5038 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -916,6 +916,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exc_value)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(excepthook)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exception)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(existing_file_name)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exp)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(extend)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(extra_tokens)); @@ -1068,6 +1069,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(namespaces)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(narg)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ndigits)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(new_file_name)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(new_limit)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(newline)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(newlines)); @@ -1122,6 +1124,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(priority)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(progress)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(progress_handler)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(progress_routine)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(proto)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(protocol)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ps1)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 61967877ab4ac8..0c84999cbf8127 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -404,6 +404,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(exc_value) STRUCT_FOR_ID(excepthook) STRUCT_FOR_ID(exception) + STRUCT_FOR_ID(existing_file_name) STRUCT_FOR_ID(exp) STRUCT_FOR_ID(extend) STRUCT_FOR_ID(extra_tokens) @@ -556,6 +557,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(namespaces) STRUCT_FOR_ID(narg) STRUCT_FOR_ID(ndigits) + STRUCT_FOR_ID(new_file_name) STRUCT_FOR_ID(new_limit) STRUCT_FOR_ID(newline) STRUCT_FOR_ID(newlines) @@ -610,6 +612,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(priority) STRUCT_FOR_ID(progress) STRUCT_FOR_ID(progress_handler) + STRUCT_FOR_ID(progress_routine) STRUCT_FOR_ID(proto) STRUCT_FOR_ID(protocol) STRUCT_FOR_ID(ps1) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 59ec49af358f2e..07f237b2905864 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -910,6 +910,7 @@ extern "C" { INIT_ID(exc_value), \ INIT_ID(excepthook), \ INIT_ID(exception), \ + INIT_ID(existing_file_name), \ INIT_ID(exp), \ INIT_ID(extend), \ INIT_ID(extra_tokens), \ @@ -1062,6 +1063,7 @@ extern "C" { INIT_ID(namespaces), \ INIT_ID(narg), \ INIT_ID(ndigits), \ + INIT_ID(new_file_name), \ INIT_ID(new_limit), \ INIT_ID(newline), \ INIT_ID(newlines), \ @@ -1116,6 +1118,7 @@ extern "C" { INIT_ID(priority), \ INIT_ID(progress), \ INIT_ID(progress_handler), \ + INIT_ID(progress_routine), \ INIT_ID(proto), \ INIT_ID(protocol), \ INIT_ID(ps1), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 8f8a067e4c1808..9b470094b7afe2 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1053,6 +1053,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(exception); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(existing_file_name); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(exp); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -1509,6 +1512,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(ndigits); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(new_file_name); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(new_limit); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -1671,6 +1677,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(progress_handler); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(progress_routine); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(proto); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Lib/shutil.py b/Lib/shutil.py index 7d1a3d00011f37..3f2864af517e7d 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -42,6 +42,8 @@ if sys.platform == 'win32': import _winapi +else: + _winapi = None COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024 # This should never be removed, see rationale in: @@ -435,6 +437,29 @@ def copy2(src, dst, *, follow_symlinks=True): """ if os.path.isdir(dst): dst = os.path.join(dst, os.path.basename(src)) + + if hasattr(_winapi, "CopyFile2"): + src_ = os.fsdecode(src) + dst_ = os.fsdecode(dst) + flags = _winapi.COPY_FILE_ALLOW_DECRYPTED_DESTINATION # for compat + if not follow_symlinks: + flags |= _winapi.COPY_FILE_COPY_SYMLINK + try: + _winapi.CopyFile2(src_, dst_, flags) + return dst + except OSError as exc: + if (exc.winerror == _winapi.ERROR_PRIVILEGE_NOT_HELD + and not follow_symlinks): + # Likely encountered a symlink we aren't allowed to create. + # Fall back on the old code + pass + elif exc.winerror == _winapi.ERROR_ACCESS_DENIED: + # Possibly encountered a hidden or readonly file we can't + # overwrite. Fall back on old code + pass + else: + raise + copyfile(src, dst, follow_symlinks=follow_symlinks) copystat(src, dst, follow_symlinks=follow_symlinks) return dst diff --git a/Misc/NEWS.d/next/Windows/2023-05-29-11-38-53.gh-issue-88745.cldf9G.rst b/Misc/NEWS.d/next/Windows/2023-05-29-11-38-53.gh-issue-88745.cldf9G.rst new file mode 100644 index 00000000000000..258eb89d50d9f5 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-05-29-11-38-53.gh-issue-88745.cldf9G.rst @@ -0,0 +1,3 @@ +Improve performance of :func:`shutil.copy2` by using the operating system's +``CopyFile2`` function. This may result in subtle changes to metadata copied +along with some files, bringing them in line with normal OS behavior. diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 1e02dbc1a4bfd1..bbc9facd227c9e 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -1947,6 +1947,7 @@ _winapi_GetFileType_impl(PyObject *module, HANDLE handle) return result; } + /*[clinic input] _winapi._mimetypes_read_windows_registry @@ -2075,6 +2076,67 @@ _winapi_NeedCurrentDirectoryForExePath_impl(PyObject *module, return result; } + +/*[clinic input] +_winapi.CopyFile2 + + existing_file_name: LPCWSTR + new_file_name: LPCWSTR + flags: DWORD + progress_routine: object = None + +Copies a file from one name to a new name. + +This is implemented using the CopyFile2 API, which preserves all stat +and metadata information apart from security attributes. + +progress_routine is reserved for future use, but is currently not +implemented. Its value is ignored. +[clinic start generated code]*/ + +static PyObject * +_winapi_CopyFile2_impl(PyObject *module, LPCWSTR existing_file_name, + LPCWSTR new_file_name, DWORD flags, + PyObject *progress_routine) +/*[clinic end generated code: output=43d960d9df73d984 input=fb976b8d1492d130]*/ +{ + HRESULT hr; + COPYFILE2_EXTENDED_PARAMETERS params = { sizeof(COPYFILE2_EXTENDED_PARAMETERS) }; + + if (PySys_Audit("_winapi.CopyFile2", "uuI", + existing_file_name, new_file_name, flags) < 0) { + return NULL; + } + + params.dwCopyFlags = flags; + /* For future implementation. We ignore the value for now so that + users only have to test for 'CopyFile2' existing and not whether + the additional parameter exists. + if (progress_routine != Py_None) { + params.pProgressRoutine = _winapi_CopyFile2ProgressRoutine; + params.pvCallbackContext = Py_NewRef(progress_routine); + } + */ + Py_BEGIN_ALLOW_THREADS; + hr = CopyFile2(existing_file_name, new_file_name, ¶ms); + Py_END_ALLOW_THREADS; + /* For future implementation. + if (progress_routine != Py_None) { + Py_DECREF(progress_routine); + } + */ + if (FAILED(hr)) { + if ((hr & 0xFFFF0000) == 0x80070000) { + PyErr_SetFromWindowsErr(hr & 0xFFFF); + } else { + PyErr_SetFromWindowsErr(hr); + } + return NULL; + } + Py_RETURN_NONE; +} + + static PyMethodDef winapi_functions[] = { _WINAPI_CLOSEHANDLE_METHODDEF _WINAPI_CONNECTNAMEDPIPE_METHODDEF @@ -2110,6 +2172,7 @@ static PyMethodDef winapi_functions[] = { _WINAPI_GETFILETYPE_METHODDEF _WINAPI__MIMETYPES_READ_WINDOWS_REGISTRY_METHODDEF _WINAPI_NEEDCURRENTDIRECTORYFOREXEPATH_METHODDEF + _WINAPI_COPYFILE2_METHODDEF {NULL, NULL} }; @@ -2146,6 +2209,7 @@ static int winapi_exec(PyObject *m) WINAPI_CONSTANT(F_DWORD, CREATE_NEW_PROCESS_GROUP); WINAPI_CONSTANT(F_DWORD, DUPLICATE_SAME_ACCESS); WINAPI_CONSTANT(F_DWORD, DUPLICATE_CLOSE_SOURCE); + WINAPI_CONSTANT(F_DWORD, ERROR_ACCESS_DENIED); WINAPI_CONSTANT(F_DWORD, ERROR_ALREADY_EXISTS); WINAPI_CONSTANT(F_DWORD, ERROR_BROKEN_PIPE); WINAPI_CONSTANT(F_DWORD, ERROR_IO_PENDING); @@ -2159,6 +2223,7 @@ static int winapi_exec(PyObject *m) WINAPI_CONSTANT(F_DWORD, ERROR_OPERATION_ABORTED); WINAPI_CONSTANT(F_DWORD, ERROR_PIPE_BUSY); WINAPI_CONSTANT(F_DWORD, ERROR_PIPE_CONNECTED); + WINAPI_CONSTANT(F_DWORD, ERROR_PRIVILEGE_NOT_HELD); WINAPI_CONSTANT(F_DWORD, ERROR_SEM_TIMEOUT); WINAPI_CONSTANT(F_DWORD, FILE_FLAG_FIRST_PIPE_INSTANCE); WINAPI_CONSTANT(F_DWORD, FILE_FLAG_OVERLAPPED); @@ -2252,6 +2317,34 @@ static int winapi_exec(PyObject *m) WINAPI_CONSTANT(F_DWORD, LCMAP_TRADITIONAL_CHINESE); WINAPI_CONSTANT(F_DWORD, LCMAP_UPPERCASE); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_ALLOW_DECRYPTED_DESTINATION); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_COPY_SYMLINK); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_FAIL_IF_EXISTS); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_NO_BUFFERING); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_NO_OFFLOAD); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_OPEN_SOURCE_FOR_WRITE); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_RESTARTABLE); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_REQUEST_SECURITY_PRIVILEGES); + WINAPI_CONSTANT(F_DWORD, COPY_FILE_RESUME_FROM_PAUSE); +#ifndef COPY_FILE_REQUEST_COMPRESSED_TRAFFIC + // Only defined in newer WinSDKs + #define COPY_FILE_REQUEST_COMPRESSED_TRAFFIC 0x10000000 +#endif + WINAPI_CONSTANT(F_DWORD, COPY_FILE_REQUEST_COMPRESSED_TRAFFIC); + + WINAPI_CONSTANT(F_DWORD, COPYFILE2_CALLBACK_CHUNK_STARTED); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_CALLBACK_CHUNK_FINISHED); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_CALLBACK_STREAM_STARTED); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_CALLBACK_STREAM_FINISHED); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_CALLBACK_POLL_CONTINUE); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_CALLBACK_ERROR); + + WINAPI_CONSTANT(F_DWORD, COPYFILE2_PROGRESS_CONTINUE); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_PROGRESS_CANCEL); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_PROGRESS_STOP); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_PROGRESS_QUIET); + WINAPI_CONSTANT(F_DWORD, COPYFILE2_PROGRESS_PAUSE); + WINAPI_CONSTANT("i", NULL); return 0; diff --git a/Modules/clinic/_winapi.c.h b/Modules/clinic/_winapi.c.h index 7bc63e612be348..3767b19d76db05 100644 --- a/Modules/clinic/_winapi.c.h +++ b/Modules/clinic/_winapi.c.h @@ -1411,4 +1411,74 @@ _winapi_NeedCurrentDirectoryForExePath(PyObject *module, PyObject *arg) return return_value; } -/*[clinic end generated code: output=96ea65ece7912d0a input=a9049054013a1b77]*/ + +PyDoc_STRVAR(_winapi_CopyFile2__doc__, +"CopyFile2($module, /, existing_file_name, new_file_name, flags,\n" +" progress_routine=None)\n" +"--\n" +"\n" +"Copies a file from one name to a new name.\n" +"\n" +"This is implemented using the CopyFile2 API, which preserves all stat\n" +"and metadata information apart from security attributes.\n" +"\n" +"progress_routine is reserved for future use, but is currently not\n" +"implemented. Its value is ignored."); + +#define _WINAPI_COPYFILE2_METHODDEF \ + {"CopyFile2", _PyCFunction_CAST(_winapi_CopyFile2), METH_FASTCALL|METH_KEYWORDS, _winapi_CopyFile2__doc__}, + +static PyObject * +_winapi_CopyFile2_impl(PyObject *module, LPCWSTR existing_file_name, + LPCWSTR new_file_name, DWORD flags, + PyObject *progress_routine); + +static PyObject * +_winapi_CopyFile2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(existing_file_name), &_Py_ID(new_file_name), &_Py_ID(flags), &_Py_ID(progress_routine), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"existing_file_name", "new_file_name", "flags", "progress_routine", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .format = "O&O&k|O:CopyFile2", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + LPCWSTR existing_file_name = NULL; + LPCWSTR new_file_name = NULL; + DWORD flags; + PyObject *progress_routine = Py_None; + + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, + _PyUnicode_WideCharString_Converter, &existing_file_name, _PyUnicode_WideCharString_Converter, &new_file_name, &flags, &progress_routine)) { + goto exit; + } + return_value = _winapi_CopyFile2_impl(module, existing_file_name, new_file_name, flags, progress_routine); + +exit: + /* Cleanup for existing_file_name */ + PyMem_Free((void *)existing_file_name); + /* Cleanup for new_file_name */ + PyMem_Free((void *)new_file_name); + + return return_value; +} +/*[clinic end generated code: output=be1343b3759e0c96 input=a9049054013a1b77]*/