make thumbnail caching safer against concurrent writes
by writing to a tmpfile first, and then renaming it to the desired target - multiple nsxiv instances writing thumbnail at the same time are guaranteed not to stomp over one another. rename() is guaranteed to be atomic by POSIX. however, it can fail with EXDEV if both the files don't reside in the same filesystem. and so we cannot make the tmpfile something like "/tmp/nsxiv-XXXXXX". instead, create the tmpfile inside the cache_dir to reduce chances of EXDEV occuring.
This commit is contained in:
parent
3659361e76
commit
2093f36661
17
thumbs.c
17
thumbs.c
|
@ -35,6 +35,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static char *cache_dir;
|
static char *cache_dir;
|
||||||
|
static char *cache_tmpfile, *cache_tmpfile_base;
|
||||||
|
static const char TMP_NAME[] = "/nsxiv-XXXXXX";
|
||||||
|
|
||||||
static char *tns_cache_filepath(const char *filepath)
|
static char *tns_cache_filepath(const char *filepath)
|
||||||
{
|
{
|
||||||
|
@ -76,6 +78,7 @@ static Imlib_Image tns_cache_load(const char *filepath, bool *outdated)
|
||||||
static void tns_cache_write(Imlib_Image im, const char *filepath, bool force)
|
static void tns_cache_write(Imlib_Image im, const char *filepath, bool force)
|
||||||
{
|
{
|
||||||
char *cfile, *dirend;
|
char *cfile, *dirend;
|
||||||
|
int tmpfd;
|
||||||
struct stat cstats, fstats;
|
struct stat cstats, fstats;
|
||||||
struct utimbuf times;
|
struct utimbuf times;
|
||||||
Imlib_Load_Error err;
|
Imlib_Load_Error err;
|
||||||
|
@ -103,12 +106,17 @@ static void tns_cache_write(Imlib_Image im, const char *filepath, bool force)
|
||||||
imlib_image_set_format("jpg");
|
imlib_image_set_format("jpg");
|
||||||
imlib_image_attach_data_value("quality", NULL, 90, NULL);
|
imlib_image_attach_data_value("quality", NULL, 90, NULL);
|
||||||
}
|
}
|
||||||
imlib_save_image_with_error_return(cfile, &err);
|
memcpy(cache_tmpfile_base, TMP_NAME, sizeof(TMP_NAME));
|
||||||
if (err)
|
if ((tmpfd = mkstemp(cache_tmpfile)) < 0)
|
||||||
goto end;
|
goto end;
|
||||||
|
close(tmpfd);
|
||||||
|
/* UPGRADE: Imlib2 v1.11.0: use imlib_save_image_fd() */
|
||||||
|
imlib_save_image_with_error_return(cache_tmpfile, &err);
|
||||||
times.actime = fstats.st_atime;
|
times.actime = fstats.st_atime;
|
||||||
times.modtime = fstats.st_mtime;
|
times.modtime = fstats.st_mtime;
|
||||||
utime(cfile, ×);
|
utime(cache_tmpfile, ×);
|
||||||
|
if (err || rename(cache_tmpfile, cfile) < 0)
|
||||||
|
unlink(cache_tmpfile);
|
||||||
}
|
}
|
||||||
end:
|
end:
|
||||||
free(cfile);
|
free(cfile);
|
||||||
|
@ -169,6 +177,9 @@ void tns_init(tns_t *tns, fileinfo_t *tns_files, const int *cnt, int *sel, win_t
|
||||||
len = strlen(homedir) + strlen(dsuffix) + strlen(s) + 1;
|
len = strlen(homedir) + strlen(dsuffix) + strlen(s) + 1;
|
||||||
cache_dir = emalloc(len);
|
cache_dir = emalloc(len);
|
||||||
snprintf(cache_dir, len, "%s%s%s", homedir, dsuffix, s);
|
snprintf(cache_dir, len, "%s%s%s", homedir, dsuffix, s);
|
||||||
|
cache_tmpfile = emalloc(len + sizeof(TMP_NAME));
|
||||||
|
memcpy(cache_tmpfile, cache_dir, len - 1);
|
||||||
|
cache_tmpfile_base = cache_tmpfile + len - 1;
|
||||||
} else {
|
} else {
|
||||||
error(0, 0, "Cache directory not found");
|
error(0, 0, "Cache directory not found");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue