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)
This commit is contained in:
Kovid Goyal 2020-10-02 08:08:51 +05:30
parent b94819be9e
commit b180fea7d6
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 41 additions and 3 deletions

View File

@ -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)))

View File

@ -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}
};

View File

@ -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;