From ba67d7386edf20bcc0f878a518de0894cb574e9f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 30 Jun 2020 11:56:03 +0300 Subject: [PATCH] bpo-41142: Add support of non-ASCII paths for CAB files. (GH-21195) * The path to the CAB file can be non-ASCII. * Paths of added files can be non-ASCII. --- Lib/test/test_msilib.py | 10 +++ .../2020-06-28-12-40-41.bpo-41142.jpZzzh.rst | 2 + PC/_msi.c | 67 +++++++++++++++---- 3 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2020-06-28-12-40-41.bpo-41142.jpZzzh.rst diff --git a/Lib/test/test_msilib.py b/Lib/test/test_msilib.py index 4a233c3784e..743bea7c14d 100644 --- a/Lib/test/test_msilib.py +++ b/Lib/test/test_msilib.py @@ -112,6 +112,16 @@ class MsiDatabaseTestCase(unittest.TestCase): with self.assertRaises(msilib.MSIError): si.GetProperty(-1) + def test_FCICreate(self): + filepath = TESTFN + '.txt' + cabpath = TESTFN + '.cab' + self.addCleanup(unlink, filepath) + with open(filepath, 'wb'): + pass + self.addCleanup(unlink, cabpath) + msilib.FCICreate(cabpath, [(filepath, 'test.txt')]) + self.assertTrue(os.path.isfile(cabpath)) + class Test_make_id(unittest.TestCase): #http://msdn.microsoft.com/en-us/library/aa369212(v=vs.85).aspx diff --git a/Misc/NEWS.d/next/Windows/2020-06-28-12-40-41.bpo-41142.jpZzzh.rst b/Misc/NEWS.d/next/Windows/2020-06-28-12-40-41.bpo-41142.jpZzzh.rst new file mode 100644 index 00000000000..91406da7a25 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2020-06-28-12-40-41.bpo-41142.jpZzzh.rst @@ -0,0 +1,2 @@ +:mod:`msilib` now supports creating CAB files with non-ASCII file path and +adding files with non-ASCII file path to them. diff --git a/PC/_msi.c b/PC/_msi.c index 58c1cfd997b..60a0c3aebb1 100644 --- a/PC/_msi.c +++ b/PC/_msi.c @@ -41,21 +41,50 @@ uuidcreate(PyObject* obj, PyObject*args) } +/* Helper for converting file names from UTF-8 to wchat_t*. */ +static wchar_t * +utf8_to_wchar(const char *s, int *err) +{ + PyObject *obj = PyUnicode_FromString(s); + if (obj == NULL) { + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + *err = ENOMEM; + } + else { + *err = EINVAL; + } + PyErr_Clear(); + return NULL; + } + wchar_t *ws = PyUnicode_AsWideCharString(obj, NULL); + if (ws == NULL) { + *err = ENOMEM; + PyErr_Clear(); + } + Py_DECREF(obj); + return ws; +} + /* FCI callback functions */ static FNFCIALLOC(cb_alloc) { - return malloc(cb); + return PyMem_RawMalloc(cb); } static FNFCIFREE(cb_free) { - free(memory); + PyMem_RawFree(memory); } static FNFCIOPEN(cb_open) { - int result = _open(pszFile, oflag | O_NOINHERIT, pmode); + wchar_t *ws = utf8_to_wchar(pszFile, err); + if (ws == NULL) { + return -1; + } + int result = _wopen(ws, oflag | O_NOINHERIT, pmode); + PyMem_Free(ws); if (result == -1) *err = errno; return result; @@ -95,7 +124,12 @@ static FNFCISEEK(cb_seek) static FNFCIDELETE(cb_delete) { - int result = remove(pszFile); + wchar_t *ws = utf8_to_wchar(pszFile, err); + if (ws == NULL) { + return -1; + } + int result = _wremove(ws); + PyMem_Free(ws); if (result != 0) *err = errno; return result; @@ -159,15 +193,22 @@ static FNFCIGETOPENINFO(cb_getopeninfo) FILETIME filetime; HANDLE handle; - /* Need Win32 handle to get time stamps */ - handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (handle == INVALID_HANDLE_VALUE) + wchar_t *ws = utf8_to_wchar(pszName, err); + if (ws == NULL) { return -1; + } - if (GetFileInformationByHandle(handle, &bhfi) == FALSE) - { + /* Need Win32 handle to get time stamps */ + handle = CreateFileW(ws, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) { + PyMem_Free(ws); + return -1; + } + + if (GetFileInformationByHandle(handle, &bhfi) == FALSE) { CloseHandle(handle); + PyMem_Free(ws); return -1; } @@ -179,7 +220,9 @@ static FNFCIGETOPENINFO(cb_getopeninfo) CloseHandle(handle); - return _open(pszName, _O_RDONLY | _O_BINARY | O_NOINHERIT); + int result = _wopen(ws, _O_RDONLY | _O_BINARY | O_NOINHERIT); + PyMem_Free(ws); + return result; } static PyObject* fcicreate(PyObject* obj, PyObject* args) @@ -212,7 +255,7 @@ static PyObject* fcicreate(PyObject* obj, PyObject* args) ccab.setID = 0; ccab.szDisk[0] = '\0'; - for (i = 0, p = cabname; *p; p = CharNext(p)) + for (i = 0, p = cabname; *p; p++) if (*p == '\\' || *p == '/') i = p - cabname + 1;