Only use hardlinks for set_path() not elsewhere.

This commit is contained in:
Kovid Goyal 2012-10-23 08:28:16 +05:30
parent 7a89cfe702
commit e561c3f1c5
2 changed files with 35 additions and 11 deletions

View File

@ -31,7 +31,7 @@ from calibre.ptempfile import (PersistentTemporaryFile,
from calibre.customize.ui import run_plugins_on_import from calibre.customize.ui import run_plugins_on_import
from calibre import isbytestring from calibre import isbytestring
from calibre.utils.filenames import (ascii_filename, samefile, from calibre.utils.filenames import (ascii_filename, samefile,
WindowsAtomicFolderMove) WindowsAtomicFolderMove, hardlink_file)
from calibre.utils.date import (utcnow, now as nowf, utcfromtimestamp, from calibre.utils.date import (utcnow, now as nowf, utcfromtimestamp,
parse_only_date, UNDEFINED_DATE) parse_only_date, UNDEFINED_DATE)
from calibre.utils.config import prefs, tweaks, from_json, to_json 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 if current_path and os.path.exists(spath): # Migrate existing files
self.copy_cover_to(id, os.path.join(tpath, 'cover.jpg'), 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: for format in formats:
copy_function = functools.partial(self.copy_format_to, id, 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: try:
self.add_format(id, format, None, index_is_id=True, self.add_format(id, format, None, index_is_id=True,
path=tpath, notify=False, copy_function=copy_function) path=tpath, notify=False, copy_function=copy_function)
@ -1347,7 +1349,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return fmt_path return fmt_path
def copy_format_to(self, index, fmt, dest, index_is_id=False, 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 Copy the format ``fmt`` to the file like object ``dest``. If the
specified format does not exist, raises :class:`NoSuchFormat` error. 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 the path is different from the current path (taking case sensitivity
into account). 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 windows_atomic_move is an internally used parameter. You should not use
it in any code outside this module. it in any code outside this module.
''' '''
@ -1375,23 +1381,28 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if hasattr(dest, 'flush'): if hasattr(dest, 'flush'):
dest.flush() dest.flush()
elif dest and not samefile(dest, path): elif dest and not samefile(dest, path):
try: if use_hardlink:
os.link(path, dest) try:
return hardlink_file(path, dest)
except: return
pass except:
pass
with lopen(path, 'rb') as f, lopen(dest, 'wb') as d: with lopen(path, 'rb') as f, lopen(dest, 'wb') as d:
shutil.copyfileobj(f, d) shutil.copyfileobj(f, d)
def copy_cover_to(self, index, dest, index_is_id=False, 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. 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 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 copied to it iff the path is different from the current path (taking
case sensitivity into account). 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 windows_atomic_move is an internally used parameter. You should not use
it in any code outside this module. it in any code outside this module.
''' '''
@ -1418,6 +1429,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
dest.flush() dest.flush()
return True return True
elif dest and not samefile(dest, path): elif dest and not samefile(dest, path):
if use_hardlink:
try:
hardlink_file(path, dest)
return
except:
pass
with lopen(dest, 'wb') as d: with lopen(dest, 'wb') as d:
shutil.copyfileobj(f, d) shutil.copyfileobj(f, d)
return True return True

View File

@ -326,3 +326,10 @@ class WindowsAtomicFolderMove(object):
win32file.DeleteFile(path) win32file.DeleteFile(path)
self.close_handles() self.close_handles()
def hardlink_file(src, dest):
if iswindows:
import win32file
win32file.CreateHardLink(dest, src)
return
os.link(src, dest)