From b180fea7d6284fb2148ea853c8528d751c6c5bcc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 2 Oct 2020 08:08:51 +0530 Subject: [PATCH] Speedup windows_get_fileid Also make it more robust by avoiding registry/time lookups. Fixes #1898110 [Cannot load Calibre 64 bit](https://bugs.launchpad.net/calibre/+bug/1898110) --- src/calibre/utils/filenames.py | 22 +++++++++++++++++++--- src/calibre/utils/windows/winutil.c | 5 +++++ src/calibre/utils/windows/winutilpp.cpp | 17 +++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index 6ba5dbbedf..e7dbbeb317 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -9,6 +9,7 @@ import os import shutil import time from math import ceil +from contextlib import suppress from calibre import force_unicode, isbytestring, prints, sanitize_file_name from calibre.constants import ( @@ -218,9 +219,10 @@ def case_preserving_open_file(path, mode='wb', mkdir_mode=0o777): return ans, fpath -def windows_get_fileid(path): - ''' The fileid uniquely identifies actual file contents (it is the same for - all hardlinks to a file). Similar to inode number on linux. ''' +def old_windows_get_fileid(path): + # we dont use this anymore as the win32 implementation reads the windows + # registry to convert file times which is slow and breaks on systems with + # registry issues. import win32file from pywintypes import error if isbytestring(path): @@ -237,6 +239,20 @@ def windows_get_fileid(path): return data[4], data[8], data[9] +def windows_get_fileid(path): + ''' The fileid uniquely identifies actual file contents (it is the same for + all hardlinks to a file). Similar to inode number on linux. ''' + try: + get_file_id = plugins['winutil'][0].get_file_id + except AttributeError: + # running from source without updating binary + return old_windows_get_fileid(path) + if isbytestring(path): + path = path.decode(filesystem_encoding) + with suppress(OSError): + return get_file_id(path) + + def samefile_windows(src, dst): samestring = (os.path.normcase(os.path.abspath(src)) == os.path.normcase(os.path.abspath(dst))) diff --git a/src/calibre/utils/windows/winutil.c b/src/calibre/utils/windows/winutil.c index 6d429b4099..0b433b7831 100644 --- a/src/calibre/utils/windows/winutil.c +++ b/src/calibre/utils/windows/winutil.c @@ -380,6 +380,7 @@ extern PyObject *winutil_friendly_name(PyObject *self, PyObject *args); extern PyObject *winutil_notify_associations_changed(PyObject *self, PyObject *args); extern PyObject *winutil_move_to_trash(PyObject *self, PyObject *args); extern PyObject *winutil_manage_shortcut(PyObject *self, PyObject *args); +extern PyObject *winutil_get_file_id(PyObject *self, PyObject *args); static PyMethodDef winutil_methods[] = { {"special_folder_path", winutil_folder_path, METH_VARARGS, @@ -471,6 +472,10 @@ be a unicode string. Returns unicode strings." "manage_shortcut()\n\nManage a shortcut" }, + {"get_file_id", (PyCFunction)winutil_get_file_id, METH_VARARGS, + "get_file_id(path)\n\nGet the windows file id (volume_num, file_index_high, file_index_low)" + }, + {NULL, NULL, 0, NULL} }; diff --git a/src/calibre/utils/windows/winutilpp.cpp b/src/calibre/utils/windows/winutilpp.cpp index fe3020c6b3..0777506331 100644 --- a/src/calibre/utils/windows/winutilpp.cpp +++ b/src/calibre/utils/windows/winutilpp.cpp @@ -114,6 +114,23 @@ py_to_wchar(PyObject *obj, wchar_raii *output) { extern "C" { +PyObject* +winutil_get_file_id(PyObject *self, PyObject *args) { + wchar_raii path; + if (!PyArg_ParseTuple(args, "O&", py_to_wchar, &path)) return NULL; + if (path.ptr()) { + HANDLE h = CreateFileW(path.ptr(), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (h == INVALID_HANDLE_VALUE) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0)); + BY_HANDLE_FILE_INFORMATION info = {0}; + BOOL ok = GetFileInformationByHandle(h, &info); + CloseHandle(h); + if (!ok) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0)); + unsigned long volnum = info.dwVolumeSerialNumber, index_high = info.nFileIndexHigh, index_low = info.nFileIndexLow; + return Py_BuildValue("kkk", volnum, index_high, index_low); + } + Py_RETURN_NONE; +} + PyObject * winutil_add_to_recent_docs(PyObject *self, PyObject *args) { wchar_raii path, app_id;