about summary refs log tree commit diff stats
path: root/ranger
diff options
context:
space:
mode:
authortoonn <toonn@toonn.io>2020-07-08 17:20:37 +0200
committertoonn <toonn@toonn.io>2020-07-08 17:27:34 +0200
commitf9e0a77f6711ba111b15fcbfa58d7d84d0804232 (patch)
treef637d0bd6d7e72b4a61771b889afaeea450461b4 /ranger
parentd6853cbfb8e05a63afe626e8dbc2ed191d838b9b (diff)
downloadranger-f9e0a77f6711ba111b15fcbfa58d7d84d0804232.tar.gz
Add path to cache hash
Without the `st_dev` the `st_ino` is no longer enough to uniquely
identify a file. A disadvantage of including the mtime in the hash is
that changed previews don't overwrite the old cache path so the cached
grows faster.

We use the path to the file to uniquely identify a device. We
concatenate it with the `st_ino` which ensures that replacing a file is
not enough to cause a collision. This is hashed to create the cache
path. Every time we check whether a preview is cached we verify that the
file being preview is as old or older than the cached preview.

The only differences from the original scheme are the algorithm (SHA-512
now), accepting previews of with the same mtime as the original file and
including the inode identifier in the hash.
Diffstat (limited to 'ranger')
-rw-r--r--ranger/core/actions.py24
1 files changed, 13 insertions, 11 deletions
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 8ba17657..f2a52451 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -17,7 +17,6 @@ import tempfile
 from inspect import cleandoc
 from stat import S_IEXEC
 from hashlib import sha512
-from struct import pack
 from logging import getLogger
 
 import ranger
@@ -1048,13 +1047,12 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         return True
 
     @staticmethod
-    def sha512_encode(path):
-        stat_ = stat(path)
-        # How I arrived at the pack format string:
-        #   < -> little-endian
-        #   L -> st_ino: unsigned int (ditto)
-        #   d -> st_mtime: double in python
-        sha = sha512(pack('<Ld', stat_.st_ino, stat_.st_mtime))
+    def sha512_encode(path, inode=None):
+        if not inode:
+            inode = stat(path).st_ino
+        sha = sha512(
+            os.path.join(path, str(inode)).encode('utf-8','backslashescape')
+        )
         return '{0}.jpg'.format(sha.hexdigest())
 
     def get_preview(self, fobj, width, height):  # pylint: disable=too-many-return-statements
@@ -1123,9 +1121,13 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
 
         if not os.path.exists(ranger.args.cachedir):
             os.makedirs(ranger.args.cachedir)
-        cacheimg = os.path.join(ranger.args.cachedir, self.sha512_encode(path))
-        if self.settings.preview_images and \
-                os.path.isfile(cacheimg):
+        fobj.load_if_outdated()
+        cacheimg = os.path.join(
+            ranger.args.cachedir,
+            self.sha512_encode(path, inode=fobj.stat.st_ino)
+        )
+        if (self.settings.preview_images and os.path.isfile(cacheimg)
+                and fobj.stat.st_mtime <= os.path.getmtime(cacheimg)):
             data['foundpreview'] = True
             data['imagepreview'] = True
             pager.set_image(cacheimg)