From e561c3f1c5a9f80f139f22eb5d6eb3fd2c7c8d25 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Oct 2012 08:28:16 +0530 Subject: [PATCH] Only use hardlinks for set_path() not elsewhere. --- src/calibre/library/database2.py | 39 +++++++++++++++++++++++--------- src/calibre/utils/filenames.py | 7 ++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 9fd42d0dce..05f5cdec74 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -31,7 +31,7 @@ from calibre.ptempfile import (PersistentTemporaryFile, from calibre.customize.ui import run_plugins_on_import from calibre import isbytestring from calibre.utils.filenames import (ascii_filename, samefile, - WindowsAtomicFolderMove) + WindowsAtomicFolderMove, hardlink_file) from calibre.utils.date import (utcnow, now as nowf, utcfromtimestamp, parse_only_date, UNDEFINED_DATE) from calibre.utils.config import prefs, tweaks, from_json, to_json @@ -653,10 +653,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if current_path and os.path.exists(spath): # Migrate existing files self.copy_cover_to(id, os.path.join(tpath, 'cover.jpg'), - index_is_id=True, windows_atomic_move=wam) + index_is_id=True, windows_atomic_move=wam, + use_hardlink=True) for format in formats: copy_function = functools.partial(self.copy_format_to, id, - format, index_is_id=True, windows_atomic_move=wam) + format, index_is_id=True, windows_atomic_move=wam, + use_hardlink=True) try: self.add_format(id, format, None, index_is_id=True, path=tpath, notify=False, copy_function=copy_function) @@ -1347,7 +1349,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): return fmt_path def copy_format_to(self, index, fmt, dest, index_is_id=False, - windows_atomic_move=None): + windows_atomic_move=None, use_hardlink=False): ''' Copy the format ``fmt`` to the file like object ``dest``. If the specified format does not exist, raises :class:`NoSuchFormat` error. @@ -1355,6 +1357,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): the path is different from the current path (taking case sensitivity into account). + If use_hardlink is True, a hard link will be created instead of the + file being copied. Use with care, because a hard link means that + modifying any one file will cause both files to be modified. + windows_atomic_move is an internally used parameter. You should not use it in any code outside this module. ''' @@ -1375,23 +1381,28 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if hasattr(dest, 'flush'): dest.flush() elif dest and not samefile(dest, path): - try: - os.link(path, dest) - return - except: - pass + if use_hardlink: + try: + hardlink_file(path, dest) + return + except: + pass with lopen(path, 'rb') as f, lopen(dest, 'wb') as d: shutil.copyfileobj(f, d) def copy_cover_to(self, index, dest, index_is_id=False, - windows_atomic_move=None): + windows_atomic_move=None, use_hardlink=False): ''' - Copy the format cover to the file like object ``dest``. Returns False + Copy the cover to the file like object ``dest``. Returns False if no cover exists or dest is the same file as the current cover. dest can also be a path in which case the cover is copied to it iff the path is different from the current path (taking case sensitivity into account). + If use_hardlink is True, a hard link will be created instead of the + file being copied. Use with care, because a hard link means that + modifying any one file will cause both files to be modified. + windows_atomic_move is an internally used parameter. You should not use it in any code outside this module. ''' @@ -1418,6 +1429,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): dest.flush() return True elif dest and not samefile(dest, path): + if use_hardlink: + try: + hardlink_file(path, dest) + return + except: + pass with lopen(dest, 'wb') as d: shutil.copyfileobj(f, d) return True diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index ffd2ab0c14..b49c039ffe 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -326,3 +326,10 @@ class WindowsAtomicFolderMove(object): win32file.DeleteFile(path) self.close_handles() +def hardlink_file(src, dest): + if iswindows: + import win32file + win32file.CreateHardLink(dest, src) + return + os.link(src, dest) +