db_commit swap threads, add ability to cancel, backup/restore

extends the db_commit plugin with the ability to delete the database
backup, restore, dis/enable auto-commit

Change-Id: Id61546e463d151399d2ac8459480e6adc5d6fac6
This commit is contained in:
William Wilgus 2023-10-06 10:57:05 -04:00 committed by William Wilgus
parent eb13628bb8
commit a0c29d8857
2 changed files with 572 additions and 124 deletions

View file

@ -22,16 +22,19 @@
/*Plugin Includes*/ /*Plugin Includes*/
#include "plugin.h" #include "plugin.h"
#include "errno.h"
/* Redefinitons of ANSI C functions. */ /* Redefinitons of ANSI C functions. */
#include "lib/wrappers.h" #include "lib/wrappers.h"
#include "lib/helper.h" #include "lib/helper.h"
static void thread_create(void); static void thread_create(void);
static void thread(void); /* the thread running it all */ static void thread(void); /* the thread running commit*/
static void allocate_tempbuf(void); static void allocate_tempbuf(void);
static void free_tempbuf(void); static void free_tempbuf(void);
static bool do_timed_yield(void); static bool do_timed_yield(void);
static void _log(const char *fmt, ...); static void _log(const char *fmt, ...);
static bool logdump(bool append);
/*Aliases*/ /*Aliases*/
#if 0 #if 0
#ifdef ROCKBOX_HAS_LOGF #ifdef ROCKBOX_HAS_LOGF
@ -41,7 +44,6 @@ static void _log(const char *fmt, ...);
#endif #endif
#endif #endif
#define logf _log #define logf _log
#define sleep rb->sleep #define sleep rb->sleep
#define qsort rb->qsort #define qsort rb->qsort
@ -49,6 +51,7 @@ static void _log(const char *fmt, ...);
#define write(x,y,z) rb->write(x,y,z) #define write(x,y,z) rb->write(x,y,z)
#define ftruncate rb->ftruncate #define ftruncate rb->ftruncate
#define remove rb->remove #define remove rb->remove
#define rename rb->rename
#define vsnprintf rb->vsnprintf #define vsnprintf rb->vsnprintf
#define mkdir rb->mkdir #define mkdir rb->mkdir
@ -60,20 +63,26 @@ static void _log(const char *fmt, ...);
#define current_tick (*rb->current_tick) #define current_tick (*rb->current_tick)
#define crc_32(x,y,z) rb->crc_32(x,y,z) #define crc_32(x,y,z) rb->crc_32(x,y,z)
#define plugin_get_buffer rb->plugin_get_buffer
#if !defined(SIMULATOR) && !defined(APPLICATION)
#define errno (*rb->__errno())
#endif
#define ENOENT 2 /* No such file or directory */
#define EEXIST 17 /* File exists */
#define MAX_LOG_SIZE 16384 #define MAX_LOG_SIZE 16384
#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF) #define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
#define EV_ACTION MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x02)
#define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01) #define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01)
#define BACKUP_DIRECTORY PLUGIN_APPS_DATA_DIR "/db_commit"
#define RC_SUCCESS 0
#define RC_USER_CANCEL 2
enum fcpy_op_flags
{
FCPY_MOVE = 0x00, /* Is a move operation (default) */
FCPY_COPY = 0x01, /* Is a copy operation */
FCPY_OVERWRITE = 0x02, /* Overwrite destination */
FCPY_EXDEV = 0x04, /* Actually copy/move across volumes */
};
/* communication to the worker thread */ /* communication to the worker thread */
static struct static struct
{ {
@ -85,6 +94,23 @@ static struct
long last_useraction_tick; long last_useraction_tick;
} gThread; } gThread;
/* status of db and commit */
static struct
{
long last_check;
bool do_commit;
bool auto_commit;
bool commit_ready;
bool db_exists;
bool have_backup;
bool bu_exists;
} gStatus;
static unsigned char logbuffer[MAX_LOG_SIZE + 1];
static int log_font_h = -1;
static int logindex;
static bool logwrap;
static bool logenabled = true;
/*Support Fns*/ /*Support Fns*/
/* open but with a builtin printf for assembling the path */ /* open but with a builtin printf for assembling the path */
@ -103,8 +129,6 @@ int open_pathfmt(char *buf, size_t size, int oflag, const char *pathfmt, ...)
static void sleep_yield(void) static void sleep_yield(void)
{ {
rb->queue_remove_from_head(&gThread.queue, EV_ACTION);
rb->queue_post(&gThread.queue, EV_ACTION, 0);
sleep(1); sleep(1);
#undef yield #undef yield
rb->yield(); rb->yield();
@ -159,18 +183,10 @@ bool user_check_tag(int index_type, char* build_idx_buf)
/* paste the whole tagcache.c file */ /* paste the whole tagcache.c file */
#include "../tagcache.c" #include "../tagcache.c"
static bool logdump(bool append);
static unsigned char logbuffer[MAX_LOG_SIZE + 1];
static int log_font_h = -1;
static int logindex;
static bool logwrap;
static bool logenabled = true;
static void check_logindex(void) static void check_logindex(void)
{ {
if(logindex >= MAX_LOG_SIZE) if(logindex >= MAX_LOG_SIZE)
{ {
/* wrap */
logdump(true); logdump(true);
//logwrap = true; //logwrap = true;
logindex = 0; logindex = 0;
@ -196,7 +212,6 @@ static void _log(const char *fmt, ...)
return; return;
} }
#ifdef USB_ENABLE_SERIAL #ifdef USB_ENABLE_SERIAL
int old_logindex = logindex; int old_logindex = logindex;
#endif #endif
@ -291,13 +306,11 @@ static bool logdisplay(void)
/* if negative, will be set 0 to zero later */ /* if negative, will be set 0 to zero later */
} }
rb->lcd_clear_display(); rb->lcd_clear_display();
if(gThread.user_index < 0 || gThread.user_index >= MAX_LOG_SIZE) if(gThread.user_index < 0 || gThread.user_index >= MAX_LOG_SIZE)
gThread.user_index = 0; gThread.user_index = 0;
if(logwrap) if(logwrap)
i = logindex; i = logindex;
else else
@ -373,7 +386,7 @@ static bool logdump(bool append)
/* nothing is logged just yet */ /* nothing is logged just yet */
return false; return false;
} }
if (!append) if (append)
{ {
flags = O_CREAT|O_WRONLY|O_APPEND; flags = O_CREAT|O_WRONLY|O_APPEND;
} }
@ -431,7 +444,6 @@ static bool do_timed_yield(void)
static long wakeup_tick = 0; static long wakeup_tick = 0;
if (TIME_AFTER(current_tick, wakeup_tick)) if (TIME_AFTER(current_tick, wakeup_tick))
{ {
yield(); yield();
wakeup_tick = current_tick + (HZ/5); wakeup_tick = current_tick + (HZ/5);
@ -440,6 +452,402 @@ static bool do_timed_yield(void)
return false; return false;
} }
/* copy/move a file */
static int fcpy(const char *src, const char *target,
unsigned int flags, bool (*poll_cancel)(const char *))
{
int rc = -1;
while (!(flags & (FCPY_COPY | FCPY_EXDEV))) {
if ((flags & FCPY_OVERWRITE) || !file_exists(target)) {
/* Rename and possibly overwrite the file */
if (poll_cancel && poll_cancel(src)) {
rc = RC_USER_CANCEL;
} else {
rc = rename(src, target);
}
#ifdef HAVE_MULTIVOLUME
if (rc < 0 && errno == EXDEV) {
/* Failed because cross volume rename doesn't work; force
a move instead */
flags |= FCPY_EXDEV;
break;
}
#endif /* HAVE_MULTIVOLUME */
}
return rc;
}
/* See if we can get the plugin buffer for the file copy buffer */
size_t buffersize;
char *buffer = (char *) plugin_get_buffer(&buffersize);
if (buffer == NULL || buffersize < 512) {
/* Not large enough, try for a disk sector worth of stack
instead */
buffersize = 512;
buffer = (char *)alloca(buffersize);
}
if (buffer == NULL) {
return -1;
}
buffersize &= ~0x1ff; /* Round buffer size to multiple of sector
size */
int src_fd = open(src, O_RDONLY);
if (src_fd >= 0) {
int oflag = O_WRONLY|O_CREAT;
if (!(flags & FCPY_OVERWRITE)) {
oflag |= O_EXCL;
}
int target_fd = open(target, oflag, 0666);
if (target_fd >= 0) {
off_t total_size = 0;
off_t next_cancel_test = 0; /* No excessive button polling */
rc = RC_SUCCESS;
while (rc == RC_SUCCESS) {
if (total_size >= next_cancel_test) {
next_cancel_test = total_size + 0x10000;
if (poll_cancel && poll_cancel(src)) {
rc = RC_USER_CANCEL;
break;
}
}
ssize_t bytesread = read(src_fd, buffer, buffersize);
if (bytesread <= 0) {
if (bytesread < 0) {
rc = -1;
}
/* else eof on buffer boundary; nothing to write */
break;
}
ssize_t byteswritten = write(target_fd, buffer, bytesread);
if (byteswritten < bytesread) {
/* Some I/O error */
rc = -1;
break;
}
total_size += byteswritten;
if (bytesread < (ssize_t)buffersize) {
/* EOF with trailing bytes */
break;
}
}
if (rc == RC_SUCCESS) {
/* If overwriting, set the correct length if original was
longer */
rc = ftruncate(target_fd, total_size);
}
close(target_fd);
if (rc != RC_SUCCESS) {
/* Copy failed. Cleanup. */
remove(target);
}
}
close(src_fd);
}
if (rc == RC_SUCCESS && !(flags & FCPY_COPY)) {
/* Remove the source file */
rc = remove(src);
}
return rc;
}
static bool backup_restore_tagcache(bool backup)
{
struct master_header tcmh;
char path[MAX_PATH];
char bu_path[MAX_PATH];
int fd;
int rc;
if (backup)
{
if (!rb->dir_exists(BACKUP_DIRECTORY))
mkdir(BACKUP_DIRECTORY);
snprintf(path, sizeof(path), "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
snprintf(bu_path, sizeof(bu_path), "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
}
else
{
if (!rb->dir_exists(BACKUP_DIRECTORY))
return false;
snprintf(path, sizeof(path), "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
snprintf(bu_path, sizeof(bu_path), "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
}
fd = open(path, O_RDONLY, 0666);
if (fd >= 0)
{
rc = read(fd, &tcmh, sizeof(struct master_header));
close(fd);
if (rc != sizeof(struct master_header))
{
logf("master file read failed");
return false;
}
int entries = tcmh.tch.entry_count;
logf("master file %d entries", entries);
if (backup)
logf("backup db to %s", BACKUP_DIRECTORY);
else
logf("restore db to %s", tc_stat.db_path);
if (entries > 0)
{
logf("%s", path);
fcpy(path, bu_path, FCPY_COPY|FCPY_OVERWRITE, NULL);
for (int i = 0; i < TAG_COUNT; i++)
{
if (TAGCACHE_IS_NUMERIC(i))
continue;
snprintf(path, sizeof(path),
"%s/"TAGCACHE_FILE_INDEX, tc_stat.db_path, i);
snprintf(bu_path, sizeof(bu_path),
"%s/"TAGCACHE_FILE_INDEX, BACKUP_DIRECTORY, i);
/* Note: above we swapped paths in the snprintf call here we swap variables */
if (backup)
{
logf("%s", path);
if (fcpy(path, bu_path, FCPY_COPY|FCPY_OVERWRITE, NULL) < 0)
goto failed;
gStatus.have_backup = true;
}
else
{
logf("%s", bu_path);
if (fcpy(bu_path, path, FCPY_COPY|FCPY_OVERWRITE, NULL) < 0)
goto failed;
}
}
}
return true;
}
failed:
if (backup)
{
logf("failed backup");
}
return false;
}
/* asks the user if they wish to quit */
static bool confirm_quit(void)
{
const struct text_message prompt =
{ (const char*[]) {"Are you sure?", "This will abort commit."}, 2};
enum yesno_res response = rb->gui_syncyesno_run(&prompt, NULL, NULL);
return (response == YESNO_YES);
}
/* asks the user if they wish to backup/restore */
static bool prompt_backup_restore(bool backup)
{
const struct text_message bu_prompt = { (const char*[]) {"Backup database?"}, 1};
const struct text_message re_prompt =
{ (const char*[]) {"Error committing,", "Restore database?"}, 2};
enum yesno_res response =
rb->gui_syncyesno_run(backup ? &bu_prompt:&re_prompt, NULL, NULL);
if(response == YESNO_YES)
return backup_restore_tagcache(backup);
return true;
}
static const char* list_get_name_cb(int selected_item, void* data,
char* buf, size_t buf_len)
{
(void) data;
(void) buf;
(void) buf_len;
/* buf supplied isn't used so lets use it for a filename buffer */
if (TIME_AFTER(current_tick, gStatus.last_check))
{
snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_NOCOMMIT, tc_stat.db_path);
gStatus.auto_commit = !file_exists(buf);
snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_TEMP, tc_stat.db_path);
gStatus.commit_ready = file_exists(buf);
snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
gStatus.db_exists = file_exists(buf);
snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
gStatus.bu_exists = file_exists(buf);
gStatus.last_check = current_tick + HZ;
buf[0] = '\0';
}
switch(selected_item)
{
case 0: /* exit */
return ID2P(LANG_MENU_QUIT);
case 1: /*sep*/
return ID2P(VOICE_BLANK);
case 2: /*backup*/
if (!gStatus.db_exists)
return ID2P(VOICE_BLANK);
return "Backup";
case 3: /*restore*/
if (!gStatus.bu_exists)
return ID2P(VOICE_BLANK);
return "Restore";
case 4: /*sep*/
return ID2P(VOICE_BLANK);
case 5: /*auto commit*/
if (gStatus.auto_commit)
return "Disable auto commit";
else
return "Enable auto commit";
case 6: /*destroy*/
if (gStatus.db_exists)
return "Delete database";
/*fall through*/
case 7: /*sep*/
return ID2P(VOICE_BLANK);
case 8: /*commit*/
if (gStatus.commit_ready)
return "Commit";
else
return "Nothing to commit";
default:
return "?";
}
}
static int list_voice_cb(int list_index, void* data)
{
#define MAX_MENU_NAME 32
if (!rb->global_settings->talk_menu)
return -1;
else
{
char buf[MAX_MENU_NAME];
const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
long id = P2ID((const unsigned char *)name);
if(id>=0)
rb->talk_id(id, true);
else
rb->talk_spell(name, true);
}
return 0;
}
static int commit_menu(void)
{
struct gui_synclist lists;
bool exit = false;
int button,i;
int selection, ret = 0;
rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&lists, NULL);
rb->gui_synclist_set_nb_items(&lists, 9);
rb->gui_synclist_select_item(&lists, 0);
while (!exit)
{
rb->gui_synclist_draw(&lists);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
if (rb->gui_synclist_do_button(&lists, &button))
continue;
selection = rb->gui_synclist_get_sel_pos(&lists);
if (button == ACTION_STD_CANCEL)
return 0;
else if (button == ACTION_STD_OK)
{
switch(selection)
{
case 0: /* exit */
exit = true;
break;
case 1: /*sep*/
continue;
case 2: /*backup*/
if (!gStatus.db_exists)
break;
if (!backup_restore_tagcache(true))
rb->splash(HZ, "Backup failed!");
else
{
rb->splash(HZ, "Backup success!");
gStatus.bu_exists = true;
}
break;
case 3: /*restore*/
if (!gStatus.bu_exists)
break;
if (!backup_restore_tagcache(false))
rb->splash(HZ, "Restore failed!");
else
rb->splash(HZ, "Restore success!");
break;
case 4: /*sep*/
continue;
case 5: /*auto commit*/
{
/* build_idx_buf supplied by tagcache.c isn't being used
* so lets use it for a filename buffer */
snprintf(build_idx_buf, build_idx_bufsz,
"%s/" TAGCACHE_FILE_NOCOMMIT, tc_stat.db_path);
if(gStatus.auto_commit)
close(open(build_idx_buf, O_WRONLY | O_CREAT | O_TRUNC, 0666));
else
remove(build_idx_buf);
gStatus.auto_commit = !file_exists(build_idx_buf);
break;
}
case 6: /*destroy*/
{
if (!gStatus.db_exists)
break;
const struct text_message prompt =
{ (const char*[]) {"Are you sure?", "This will destroy database."}, 2};
if (rb->gui_syncyesno_run(&prompt, NULL, NULL) == YESNO_YES)
remove_files();
break;
}
case 7: /*sep*/
continue;
case 8: /*commit*/
if (gStatus.commit_ready)
{
gStatus.do_commit = true;
exit = true;
}
break;
case MENU_ATTACHED_USB:
return PLUGIN_USB_CONNECTED;
default:
return 0;
}
}
} /*while*/
return ret;
}
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
/******* plugin_start ******* */ /******* plugin_start ******* */
/*-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------*/
@ -451,6 +859,8 @@ enum plugin_status plugin_start(const void* parameter)
/* Turn off backlight timeout */ /* Turn off backlight timeout */
backlight_ignore_timeout(); backlight_ignore_timeout();
memset(&gThread, 0, sizeof(gThread));
memset(&gStatus, 0, sizeof(gStatus));
memset(&tc_stat, 0, sizeof(struct tagcache_stat)); memset(&tc_stat, 0, sizeof(struct tagcache_stat));
memset(&current_tcmh, 0, sizeof(struct master_header)); memset(&current_tcmh, 0, sizeof(struct master_header));
filenametag_fd = -1; filenametag_fd = -1;
@ -459,30 +869,102 @@ enum plugin_status plugin_start(const void* parameter)
if (!rb->dir_exists(tc_stat.db_path)) /* on fail use default DB path */ if (!rb->dir_exists(tc_stat.db_path)) /* on fail use default DB path */
strlcpy(tc_stat.db_path, ROCKBOX_DIR, sizeof(tc_stat.db_path)); strlcpy(tc_stat.db_path, ROCKBOX_DIR, sizeof(tc_stat.db_path));
tc_stat.initialized = true; tc_stat.initialized = true;
tc_stat.commit_step = -1;
memset(&gThread, 0, sizeof(gThread));
logf("started"); logf("started");
logdump(false);
int result = commit_menu();
if (!gStatus.do_commit)
{
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
return PLUGIN_OK;
}
logdump(false);
allocate_tempbuf();
if (gStatus.db_exists && !gStatus.have_backup && !prompt_backup_restore(true))
rb->splash(HZ, "Backup failed!");
thread_create(); thread_create();
gThread.user_index = 0; gThread.user_index = 0;
logdisplay(); /* get something on the screen while user waits */ logdisplay(); /* get something on the screen while user waits */
allocate_tempbuf(); while (!gThread.exiting)
{
logdisplay();
commit(); int action = rb->get_action(CONTEXT_STD, HZ/20);
switch( action )
{
case ACTION_NONE:
break;
case ACTION_STD_NEXT:
case ACTION_STD_NEXTREPEAT:
gThread.last_useraction_tick = current_tick;
gThread.user_index++;
break;
case ACTION_STD_PREV:
case ACTION_STD_PREVREPEAT:
gThread.last_useraction_tick = current_tick;
gThread.user_index--;
break;
case ACTION_STD_OK:
gThread.last_useraction_tick = current_tick;
gThread.user_index = 0;
break;
case SYS_POWEROFF:
case ACTION_STD_CANCEL:
if (tc_stat.commit_step >= 0 && !tc_stat.ready)
{
if (!confirm_quit())
break;
tc_stat.commit_delayed = true; /* Cancel the commit */
}
rb->queue_remove_from_head(&gThread.queue, EV_EXIT);
rb->queue_post(&gThread.queue, EV_EXIT, 0);
break;
#ifdef HAVE_TOUCHSCREEN
case ACTION_TOUCHSCREEN:
{
gThread.last_useraction_tick = current_tick;
short x, y;
static int prev_y;
action = rb->action_get_touchscreen_press(&x, &y);
if(action & BUTTON_REL)
prev_y = 0;
else
{
if(prev_y != 0)
gThread.user_index += (prev_y - y) / log_font_h;
prev_y = y;
}
}
#endif
default:
break;
}
yield();
}
rb->thread_wait(gThread.id);
rb->queue_delete(&gThread.queue);
free_tempbuf();
if (tc_stat.commit_delayed || !tc_stat.ready)
{
remove_files();
if (gStatus.bu_exists && !prompt_backup_restore(false))
rb->splash(HZ, "Restore failed!");
}
if (tc_stat.ready) if (tc_stat.ready)
rb->tagcache_commit_finalize(); rb->tagcache_commit_finalize();
free_tempbuf();
logdump(true);
gThread.user_index++;
logdisplay();
rb->thread_wait(gThread.id);
rb->queue_delete(&gThread.queue);
/* Turn on backlight timeout (revert to settings) */ /* Turn on backlight timeout (revert to settings) */
backlight_use_settings(); backlight_use_settings();
return PLUGIN_OK; return PLUGIN_OK;
@ -506,63 +988,20 @@ static void thread(void)
/*fall through*/ /*fall through*/
case EV_STARTUP: case EV_STARTUP:
logf("Thread Started"); logf("Thread Started");
cpu_boost(true);
if (commit())
tc_stat.ready = true;
cpu_boost(false);
logdump(true);
gThread.user_index++;
logdisplay();
break; break;
case EV_EXIT: case EV_EXIT:
return;
default:
break;
}
logdisplay();
int action = rb->get_action(CONTEXT_STD, HZ/10);
switch( action )
{
case ACTION_NONE:
break;
case ACTION_STD_NEXT:
case ACTION_STD_NEXTREPEAT:
gThread.last_useraction_tick = current_tick;
gThread.user_index++;
break;
case ACTION_STD_PREV:
case ACTION_STD_PREVREPEAT:
gThread.last_useraction_tick = current_tick;
gThread.user_index--;
break;
case ACTION_STD_OK:
gThread.last_useraction_tick = current_tick;
gThread.user_index = 0;
break;
case SYS_POWEROFF:
case ACTION_STD_CANCEL:
gThread.exiting = true; gThread.exiting = true;
return; return;
#ifdef HAVE_TOUCHSCREEN
case ACTION_TOUCHSCREEN:
{
gThread.last_useraction_tick = current_tick;
short x, y;
static int prev_y;
action = rb->action_get_touchscreen_press(&x, &y);
if(action & BUTTON_REL)
prev_y = 0;
else
{
if(prev_y != 0)
gThread.user_index += (prev_y - y) / log_font_h;
prev_y = y;
}
}
#endif
default: default:
break; break;
} }
yield(); yield();
} }
} }

View file

@ -92,6 +92,9 @@
#include "lang.h" #include "lang.h"
#include "eeprom_settings.h" #include "eeprom_settings.h"
#endif #endif
#define USR_CANCEL false
#else/*!defined(PLUGIN)*/
#define USR_CANCEL (tc_stat.commit_delayed == true)
#endif /*!defined(PLUGIN)*/ #endif /*!defined(PLUGIN)*/
/* /*
* Define this to support non-native endian tagcache files. * Define this to support non-native endian tagcache files.
@ -2370,7 +2373,7 @@ static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
unsigned *crcbuf = (unsigned *)&tempbuf[tempbuf_size-4]; unsigned *crcbuf = (unsigned *)&tempbuf[tempbuf_size-4];
unsigned crc32 = 0xffffffff; unsigned crc32 = 0xffffffff;
char chr_lower; char chr_lower;
for (i = 0; str[i] != '\0' && i < len; i++) for (i = 0; str[i] != '\0' && i < len -1; i++)
{ {
chr_lower = tolower(str[i]); chr_lower = tolower(str[i]);
crc32 = crc_32(&chr_lower, 1, crc32); crc32 = crc_32(&chr_lower, 1, crc32);
@ -2404,9 +2407,12 @@ static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
/* Insert it to the buffer. */ /* Insert it to the buffer. */
tempbuf_left -= len; tempbuf_left -= len;
if (tempbuf_left - 4 < 0 || tempbufidx >= commit_entry_count-1) if (tempbuf_left - 4 < 0 || tempbufidx >= commit_entry_count)
{
logf("temp buf error rem: %ld idx: %d / %d",
tempbuf_left, tempbufidx, commit_entry_count-1);
return false; return false;
}
if (id >= lookup_buffer_depth) if (id >= lookup_buffer_depth)
{ {
logf("lookup buf overf. #2: %d", id); logf("lookup buf overf. #2: %d", id);
@ -2585,7 +2591,7 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
return false; return false;
} }
while (entries_processed < h->entry_count) while (entries_processed < h->entry_count && !USR_CANCEL)
{ {
int count = MIN(h->entry_count - entries_processed, max_entries); int count = MIN(h->entry_count - entries_processed, max_entries);
@ -2651,7 +2657,7 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
lseek(masterfd, sizeof(struct master_header), SEEK_SET); lseek(masterfd, sizeof(struct master_header), SEEK_SET);
/* Check if we can resurrect some deleted runtime statistics data. */ /* Check if we can resurrect some deleted runtime statistics data. */
for (i = 0; i < tcmh.tch.entry_count; i++) for (i = 0; i < tcmh.tch.entry_count && !USR_CANCEL; i++)
{ {
/* Read the index entry. */ /* Read the index entry. */
if (read_index_entries(masterfd, &idx, 1) != sizeof(struct index_entry)) if (read_index_entries(masterfd, &idx, 1) != sizeof(struct index_entry))
@ -2742,7 +2748,7 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
lseek(masterfd, masterfd_pos, SEEK_SET); lseek(masterfd, masterfd_pos, SEEK_SET);
/* Commit the data to the index. */ /* Commit the data to the index. */
for (i = 0; i < count; i++) for (i = 0; i < count && !USR_CANCEL; i++)
{ {
int loc = lseek(masterfd, 0, SEEK_CUR); int loc = lseek(masterfd, 0, SEEK_CUR);
@ -2895,7 +2901,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
if (TAGCACHE_IS_SORTED(index_type)) if (TAGCACHE_IS_SORTED(index_type))
{ {
logf("loading tags..."); logf("loading tags...");
for (i = 0; i < tch.entry_count; i++) for (i = 0; i < tch.entry_count && !USR_CANCEL; i++)
{ {
struct tagfile_entry entry; struct tagfile_entry entry;
int loc = lseek(fd, 0, SEEK_CUR); int loc = lseek(fd, 0, SEEK_CUR);
@ -3040,7 +3046,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET); lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
/* h is the header of the temporary file containing new tags. */ /* h is the header of the temporary file containing new tags. */
logf("inserting new tags..."); logf("inserting new tags...");
for (i = 0; i < h->entry_count; i++) for (i = 0; i < h->entry_count && !USR_CANCEL; i++)
{ {
struct temp_file_entry entry; struct temp_file_entry entry;
@ -3112,7 +3118,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
*/ */
logf("updating indices..."); logf("updating indices...");
lseek(masterfd, sizeof(struct master_header), SEEK_SET); lseek(masterfd, sizeof(struct master_header), SEEK_SET);
for (i = 0; i < tcmh.tch.entry_count; i += idxbuf_pos) for (i = 0; i < tcmh.tch.entry_count && !USR_CANCEL; i += idxbuf_pos)
{ {
int j; int j;
int loc = lseek(masterfd, 0, SEEK_CUR); int loc = lseek(masterfd, 0, SEEK_CUR);
@ -3172,7 +3178,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
lseek(masterfd, masterfd_pos, SEEK_SET); lseek(masterfd, masterfd_pos, SEEK_SET);
lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET); lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_END);
for (i = 0; i < h->entry_count; i += idxbuf_pos) for (i = 0; i < h->entry_count && !USR_CANCEL; i += idxbuf_pos)
{ {
int j; int j;
@ -3412,7 +3418,7 @@ static bool commit(void)
tch.datasize = 0; tch.datasize = 0;
tc_stat.commit_delayed = false; tc_stat.commit_delayed = false;
for (i = 0; i < TAG_COUNT; i++) for (i = 0; i < TAG_COUNT && !USR_CANCEL; i++)
{ {
int ret; int ret;
@ -3446,41 +3452,44 @@ static bool commit(void)
tc_stat.commit_step = 0; tc_stat.commit_step = 0;
/* Update the master index headers. */ if (!USR_CANCEL)
if ( (masterfd = open_master_fd(&tcmh, true)) < 0) {
goto commit_error; /* Update the master index headers. */
if ( (masterfd = open_master_fd(&tcmh, true)) < 0)
goto commit_error;
remove_db_file(TAGCACHE_FILE_TEMP); remove_db_file(TAGCACHE_FILE_TEMP);
tcmh.tch.entry_count += tch.entry_count; tcmh.tch.entry_count += tch.entry_count;
tcmh.tch.datasize = sizeof(struct master_header) tcmh.tch.datasize = sizeof(struct master_header)
+ sizeof(struct index_entry) * tcmh.tch.entry_count + sizeof(struct index_entry) * tcmh.tch.entry_count
+ tch.datasize; + tch.datasize;
tcmh.dirty = false; tcmh.dirty = false;
tcmh.commitid++; tcmh.commitid++;
lseek(masterfd, 0, SEEK_SET); lseek(masterfd, 0, SEEK_SET);
write_master_header(masterfd, &tcmh); write_master_header(masterfd, &tcmh);
close(masterfd); close(masterfd);
logf("tagcache committed"); logf("tagcache committed");
tagcache_commit_finalize(); tagcache_commit_finalize();
#if defined(HAVE_TC_RAMCACHE) #if defined(HAVE_TC_RAMCACHE)
if (ramcache_buffer_stolen) if (ramcache_buffer_stolen)
{ {
tempbuf = NULL; tempbuf = NULL;
tempbuf_size = 0; tempbuf_size = 0;
ramcache_buffer_stolen = false; ramcache_buffer_stolen = false;
tcrc_buffer_unlock(); tcrc_buffer_unlock();
} }
/* Reload tagcache. */ /* Reload tagcache. */
if (tc_stat.ramcache_allocated > 0) if (tc_stat.ramcache_allocated > 0)
tagcache_start_scan(); tagcache_start_scan();
#endif /* HAVE_TC_RAMCACHE */ #endif /* HAVE_TC_RAMCACHE */
rc = true; rc = true;
} /*!USR_CANCEL*/
commit_error: commit_error:
#ifdef HAVE_TC_RAMCACHE #ifdef HAVE_TC_RAMCACHE