efcf734590
ChangeLog: https://www.jdupes.com/2023/11/10/news-fix-for-v1-27-3-libjodycode-4-work/ Apply a fix for the jdupes hash database feature to avoid potential database loss, resulting from disk-full situations and other write failures. PR: 275128 Reported by: tom@hur.st (maintainer) MFH: 2023Q4 (bugfix)
115 lines
3.8 KiB
C
115 lines
3.8 KiB
C
--- hashdb.c.orig 2023-08-27 01:12:34 UTC
|
|
+++ hashdb.c
|
|
@@ -31,6 +31,7 @@ static hashdb_t *hashdb[HT_SIZE];
|
|
static int hashdb_init = 0;
|
|
static int hashdb_algo = 0;
|
|
static int hashdb_dirty = 0;
|
|
+static int new_hashdb = 0;
|
|
|
|
/* Pivot direction for rebalance */
|
|
enum pivot { PIVOT_LEFT, PIVOT_RIGHT };
|
|
@@ -68,20 +69,35 @@ int save_hash_database(const char * const restrict dbn
|
|
{
|
|
FILE *db = NULL;
|
|
uint64_t cnt = 0;
|
|
+ char *dbtemp;
|
|
|
|
if (dbname == NULL) goto error_hashdb_null;
|
|
LOUD(fprintf(stderr, "save_hash_database('%s')\n", dbname);)
|
|
/* Don't save the hash database if it wasn't changed */
|
|
if (hashdb_dirty == 0 && destroy == 0) return 0;
|
|
if (hashdb_dirty == 1) {
|
|
+
|
|
errno = 0;
|
|
- db = fopen(dbname, "w+b");
|
|
+ dbtemp = malloc(strlen(dbname) + 5);
|
|
+ if (dbtemp == NULL) goto error_hashdb_alloc;
|
|
+ strcpy(dbtemp, dbname);
|
|
+ strcat(dbtemp, ".tmp");
|
|
+ /* Try to remove any existing temporary database, ignoring errors */
|
|
+ remove(dbtemp);
|
|
+ db = fopen(dbtemp, "rb");
|
|
if (db == NULL) goto error_hashdb_open;
|
|
}
|
|
|
|
- if (write_hashdb_entry(db, NULL, &cnt, destroy) != 0) goto error_hashdb_write;
|
|
if (hashdb_dirty == 1) {
|
|
+ if (write_hashdb_entry(db, NULL, &cnt, destroy) != 0) goto error_hashdb_write;
|
|
fclose(db);
|
|
+ if (new_hashdb == 0) {
|
|
+ errno = 0;
|
|
+ if (remove(dbname) != 0) {
|
|
+ if (errno != ENOENT) goto error_hashdb_remove;
|
|
+ }
|
|
+ }
|
|
+ if (rename(dbtemp, dbname) != 0) goto error_hashdb_rename;
|
|
LOUD(if (hashdb_dirty == 1) fprintf(stderr, "Wrote %" PRIu64 " items to hash databse '%s'\n", cnt, dbname);)
|
|
hashdb_dirty = 0;
|
|
}
|
|
@@ -92,12 +108,22 @@ error_hashdb_null:
|
|
fprintf(stderr, "error: internal failure: NULL pointer for hashdb\n");
|
|
return -1;
|
|
error_hashdb_open:
|
|
- fprintf(stderr, "error: cannot open hashdb '%s' for writing: %s\n", dbname, strerror(errno));
|
|
+ fprintf(stderr, "error: cannot open temp hashdb '%s' for writing: %s\n", dbtemp, strerror(errno));
|
|
return -2;
|
|
error_hashdb_write:
|
|
- fprintf(stderr, "error: writing failed to hashdb '%s': %s\n", dbname, strerror(errno));
|
|
+ fprintf(stderr, "error: write failed to temp hashdb '%s': %s\n", dbtemp, strerror(errno));
|
|
fclose(db);
|
|
return -3;
|
|
+error_hashdb_alloc:
|
|
+ fprintf(stderr, "error: cannot allocate memory for temporary hashdb name\n");
|
|
+ return -4;
|
|
+error_hashdb_remove:
|
|
+ fprintf(stderr, "error: cannot delete old hashdb '%s': %s\n", dbname, strerror(errno));
|
|
+ remove(dbtemp);
|
|
+ return -5;
|
|
+error_hashdb_rename:
|
|
+ fprintf(stderr, "error: cannot rename temporary hashdb '%s' to '%s'; leaving it alone: %s\n", dbtemp, dbname, strerror(errno));
|
|
+ return -6;
|
|
}
|
|
|
|
|
|
@@ -435,31 +461,40 @@ int64_t load_hash_database(char *dbname)
|
|
entry->hashcount = hashcount;
|
|
}
|
|
|
|
+ fclose(db);
|
|
return linenum - 1;
|
|
|
|
warn_hashdb_open:
|
|
fprintf(stderr, "Creating a new hash database '%s'\n", dbname);
|
|
+ fclose(db);
|
|
+ new_hashdb = 1;
|
|
return 0;
|
|
error_hashdb_read:
|
|
fprintf(stderr, "error reading hash database '%s': %s\n", dbname, strerror(errno));
|
|
+ fclose(db);
|
|
return -1;
|
|
error_hashdb_header:
|
|
fprintf(stderr, "error in header of hash database '%s'\n", dbname);
|
|
+ fclose(db);
|
|
return -2;
|
|
error_hashdb_version:
|
|
fprintf(stderr, "error: bad db version %u in hash database '%s'\n", db_ver, dbname);
|
|
+ fclose(db);
|
|
return -3;
|
|
error_hashdb_line:
|
|
fprintf(stderr, "\nerror: bad line %" PRId64 " in hash database '%s':\n\n%s\n\n", linenum, dbname, line);
|
|
+ fclose(db);
|
|
return -4;
|
|
error_hashdb_add:
|
|
fprintf(stderr, "error: internal failure allocating a hashdb entry\n");
|
|
+ fclose(db);
|
|
return -5;
|
|
error_hashdb_null:
|
|
fprintf(stderr, "error: internal failure: NULL pointer for hashdb\n");
|
|
return -6;
|
|
warn_hashdb_algo:
|
|
fprintf(stderr, "warning: hashdb uses a different hash algorithm than selected; not loading\n");
|
|
+ fclose(db);
|
|
return -7;
|
|
}
|
|
|