1Added some DB-access routines to help rsync keep extra filesystem info 2about the files it is dealing with. This adds both the --db=CONFIG_FILE 3option and the "db config" daemon parameter. 4 5Future improvements may include: 6 7 - Updating of MD4 checksums when transferring any file, even w/o -c. 8 To make that work we'd need to make the sender force checksum_seed to 9 0 when using a DB and having the receiving side check to see if it 10 got a 0 checksum_seed. 11 12 - Caching of path info that allows for the finding of files to use for 13 moving/linking/copying/alternate-basis-use. 14 15 - Extend DB support beyond MySQL and SQLite (PostgreSQL?). 16 17To use this patch, run these commands for a successful build: 18 19 patch -p1 <patches/db.diff 20 ./prepare-source 21 ./configure 22 make 23 24based-on: e94bad1c156fc3910f24e2b3b71a81b0b0bdeb70 25diff --git a/.gitignore b/.gitignore 26--- a/.gitignore 27+++ b/.gitignore 28@@ -30,6 +30,7 @@ aclocal.m4 29 /getgroups 30 /gmon.out 31 /rsync 32+/rsyncdb 33 /stunnel-rsyncd.conf 34 /shconfig 35 /git-version.h 36diff --git a/Makefile.in b/Makefile.in 37--- a/Makefile.in 38+++ b/Makefile.in 39@@ -4,6 +4,7 @@ prefix=@prefix@ 40 datarootdir=@datarootdir@ 41 exec_prefix=@exec_prefix@ 42 bindir=@bindir@ 43+sbindir=@sbindir@ 44 libdir=@libdir@/rsync 45 mandir=@mandir@ 46 47@@ -33,7 +34,7 @@ SIMD_x86_64=simd-checksum-x86_64.o 48 ASM_x86_64=lib/md5-asm-x86_64.o 49 50 GENFILES=configure.sh aclocal.m4 config.h.in rsync.1 rsync.1.html \ 51- rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html 52+ rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html rsyncdb.1 rsyncdb.1.html 53 HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h itypes.h inums.h \ 54 lib/pool_alloc.h lib/mdigest.h lib/md-defines.h version.h 55 LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \ 56@@ -43,7 +44,7 @@ zlib_OBJS=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \ 57 OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \ 58 util.o util2.o main.o checksum.o match.o syscall.o log.o backup.o delete.o 59 OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \ 60- usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o 61+ usage.o fileio.o batch.o clientname.o chmod.o db.o acls.o xattrs.o 62 OBJS3=progress.o pipe.o @ASM@ 63 DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o 64 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \ 65@@ -75,10 +76,12 @@ install: all 66 -$(MKDIR_P) $(DESTDIR)$(bindir) 67 $(INSTALLCMD) $(INSTALL_STRIP) -m 755 rsync$(EXEEXT) $(DESTDIR)$(bindir) 68 $(INSTALLCMD) -m 755 $(srcdir)/rsync-ssl $(DESTDIR)$(bindir) 69+ rsync -ilt rsyncdb$(EXEEXT) $(DESTDIR)$(bindir)/ 70 -$(MKDIR_P) $(DESTDIR)$(mandir)/man1 71 -$(MKDIR_P) $(DESTDIR)$(mandir)/man5 72 if test -f rsync.1; then $(INSTALLMAN) -m 644 rsync.1 $(DESTDIR)$(mandir)/man1; fi 73 if test -f rsync-ssl.1; then $(INSTALLMAN) -m 644 rsync-ssl.1 $(DESTDIR)$(mandir)/man1; fi 74+ if test -f rsyncdb.1; then $(INSTALLMAN) -m 644 rsyncdb.1 $(DESTDIR)$(mandir)/man1; fi 75 if test -f rsyncd.conf.5; then $(INSTALLMAN) -m 644 rsyncd.conf.5 $(DESTDIR)$(mandir)/man5; fi 76 77 install-ssl-daemon: stunnel-rsyncd.conf 78@@ -96,10 +99,13 @@ install-strip: 79 rsync$(EXEEXT): $(OBJS) 80 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) 81 82+rsyncdb$(EXEEXT): rsync$(EXEEXT) 83+ ln -s rsync$(EXEEXT) rsyncdb$(EXEEXT) 84+ 85 $(OBJS): $(HEADERS) 86 $(CHECK_OBJS): $(HEADERS) 87 tls.o xattrs.o: lib/sysxattrs.h 88-usage.o: latest-year.h help-rsync.h help-rsyncd.h git-version.h default-cvsignore.h 89+usage.o: latest-year.h help-rsync.h help-rsyncd.h help-rsyncdb.h git-version.h default-cvsignore.h 90 loadparm.o: default-dont-compress.h daemon-parm.h 91 92 flist.o: rounding.h 93@@ -110,6 +116,9 @@ default-cvsignore.h default-dont-compress.h: rsync.1.md define-from-md.awk 94 help-rsync.h help-rsyncd.h: rsync.1.md help-from-md.awk 95 $(AWK) -f $(srcdir)/help-from-md.awk -v hfile=$@ $(srcdir)/rsync.1.md 96 97+help-rsyncdb.h: rsyncdb.1.md help-from-md.awk 98+ awk -f $(srcdir)/help-from-md.awk -v hfile=$@ $(srcdir)/rsyncdb.1.md 99+ 100 daemon-parm.h: daemon-parm.txt daemon-parm.awk 101 $(AWK) -f $(srcdir)/daemon-parm.awk $(srcdir)/daemon-parm.txt 102 103@@ -240,7 +249,7 @@ proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h 104 $(AWK) -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h 105 106 .PHONY: man 107-man: rsync.1 rsync-ssl.1 rsyncd.conf.5 108+man: rsync.1 rsync-ssl.1 rsyncd.conf.5 rsyncdb.1 109 110 rsync.1: rsync.1.md md2man version.h Makefile 111 @$(srcdir)/maybe-make-man $(srcdir) rsync.1.md 112@@ -251,9 +260,12 @@ rsync-ssl.1: rsync-ssl.1.md md2man version.h Makefile 113 rsyncd.conf.5: rsyncd.conf.5.md md2man version.h Makefile 114 @$(srcdir)/maybe-make-man $(srcdir) rsyncd.conf.5.md 115 116+rsyncdb.1: rsyncdb.1.md md2man NEWS.md Makefile 117+ @$(srcdir)/maybe-make-man $(srcdir) rsyncdb.1.md 118+ 119 .PHONY: clean 120 clean: cleantests 121- rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) \ 122+ rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) rsyncdb$(EXEEXT) \ 123 rounding rounding.h *.old rsync*.1 rsync*.5 rsync*.html \ 124 daemon-parm.h help-*.h default-*.h proto.h proto.h-tstamp 125 126diff --git a/checksum.c b/checksum.c 127--- a/checksum.c 128+++ b/checksum.c 129@@ -40,6 +40,7 @@ extern int whole_file; 130 extern int checksum_seed; 131 extern int protocol_version; 132 extern int proper_seed_order; 133+extern int use_db; 134 extern const char *checksum_choice; 135 136 struct name_num_obj valid_checksums = { 137@@ -386,6 +387,8 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) 138 MD5_Update(&m5, (uchar *)map_ptr(buf, i, remainder), remainder); 139 140 MD5_Final((uchar *)sum, &m5); 141+ if (use_db) 142+ db_set_checksum(5, st_p, sum); 143 break; 144 } 145 case CSUM_MD4: 146@@ -425,6 +428,8 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) 147 mdfour_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder); 148 149 mdfour_result(&m, (uchar *)sum); 150+ if (use_db) 151+ db_set_checksum(4, st_p, sum); 152 break; 153 } 154 default: 155diff --git a/cleanup.c b/cleanup.c 156--- a/cleanup.c 157+++ b/cleanup.c 158@@ -28,6 +28,7 @@ extern int am_daemon; 159 extern int am_receiver; 160 extern int am_sender; 161 extern int io_error; 162+extern int use_db; 163 extern int keep_partial; 164 extern int got_xfer_error; 165 extern int protocol_version; 166@@ -143,6 +144,12 @@ NORETURN void _exit_cleanup(int code, const char *file, int line) 167 #include "case_N.h" 168 switch_step++; 169 170+ if (use_db) 171+ db_disconnect(False); 172+ 173+ /* FALLTHROUGH */ 174+#include "case_N.h" 175+ 176 if (cleanup_child_pid != -1) { 177 int status; 178 int pid = wait_process(cleanup_child_pid, &status, WNOHANG); 179diff --git a/clientserver.c b/clientserver.c 180--- a/clientserver.c 181+++ b/clientserver.c 182@@ -44,12 +44,15 @@ extern int numeric_ids; 183 extern int filesfrom_fd; 184 extern int remote_protocol; 185 extern int protocol_version; 186+extern int always_checksum; 187+extern int db_lax; 188 extern int io_timeout; 189 extern int no_detach; 190 extern int write_batch; 191 extern int default_af_hint; 192 extern int logfile_format_has_i; 193 extern int logfile_format_has_o_or_i; 194+extern char *db_config; 195 extern char *bind_address; 196 extern char *config_file; 197 extern char *logfile_format; 198@@ -809,6 +812,11 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char 199 200 log_init(1); 201 202+ if (*lp_db_config(i)) { 203+ db_read_config(FLOG, lp_db_config(i)); 204+ db_lax = lp_db_lax(i); 205+ } 206+ 207 #ifdef HAVE_PUTENV 208 if ((*lp_early_exec(module_id) || *lp_prexfer_exec(module_id) 209 || *lp_postxfer_exec(module_id) || *lp_name_converter(module_id)) 210@@ -1021,6 +1029,8 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char 211 212 am_server = 1; /* Don't let someone try to be tricky. */ 213 quiet = 0; 214+ db_config = NULL; 215+ 216 if (lp_ignore_errors(module_id)) 217 ignore_errors = 1; 218 if (write_batch < 0) 219diff --git a/configure.ac b/configure.ac 220--- a/configure.ac 221+++ b/configure.ac 222@@ -494,6 +494,7 @@ AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \ 223 unistd.h utime.h grp.h compat.h sys/param.h ctype.h sys/wait.h \ 224 sys/ioctl.h sys/filio.h string.h stdlib.h sys/socket.h sys/mode.h \ 225 sys/un.h sys/attr.h mcheck.h arpa/inet.h arpa/nameser.h locale.h \ 226+ mysql/mysql.h sqlite3.h \ 227 netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h \ 228 sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h dl.h \ 229 popt.h popt/popt.h linux/falloc.h netinet/in_systm.h netinet/ip.h \ 230@@ -1369,6 +1370,48 @@ if test x"$enable_acl_support" = x"no" -o x"$enable_xattr_support" = x"no" -o x" 231 fi 232 fi 233 234+AC_MSG_CHECKING([whether to include mysql DB support]) 235+AC_ARG_ENABLE(mysql, 236+ AC_HELP_STRING([--disable-mysql], 237+ [disable mysql DB support])) 238+ 239+if test x"$enable_mysql" = x"no"; then 240+ AC_MSG_RESULT(no) 241+else 242+ AC_MSG_RESULT([yes]) 243+ AC_CHECK_PROG(MYSQL_CONFIG, mysql_config, 1, 0) 244+ if test x$MYSQL_CONFIG = x1; then 245+ AC_MSG_CHECKING(for mysql version >= 4) 246+ mysql_version=`mysql_config --version` 247+ mysql_major_version=`echo $mysql_version | sed 's/\..*//'` 248+ if test $mysql_major_version -lt 4; then 249+ AC_MSG_RESULT(no.. skipping MySQL) 250+ else 251+ AC_MSG_RESULT(yes) 252+ 253+ MYSQL_CFLAGS=`mysql_config --cflags` 254+ MYSQL_LIBS=`mysql_config --libs` 255+ 256+ CPPFLAGS="$CPPFLAGS $MYSQL_CFLAGS" 257+ LIBS="$MYSQL_LIBS $LIBS" 258+ 259+ AC_CHECK_LIB(mysqlclient, mysql_init) 260+ fi 261+ fi 262+fi 263+ 264+AC_MSG_CHECKING([whether to include sqlite DB support]) 265+AC_ARG_ENABLE(sqlite, 266+ AC_HELP_STRING([--disable-sqlite], 267+ [disable sqlite DB support])) 268+ 269+if test x"$enable_sqlite" = x"no"; then 270+ AC_MSG_RESULT(no) 271+else 272+ AC_CHECK_LIB(sqlite3, sqlite3_open) 273+ AC_CHECK_FUNCS(sqlite3_open_v2 sqlite3_prepare_v2) 274+fi 275+ 276 case "$CC" in 277 ' checker'*|checker*) 278 AC_DEFINE(FORCE_FD_ZERO_MEMSET, 1, [Used to make "checker" understand that FD_ZERO() clears memory.]) 279diff --git a/daemon-parm.txt b/daemon-parm.txt 280--- a/daemon-parm.txt 281+++ b/daemon-parm.txt 282@@ -18,6 +18,7 @@ Locals: ================================================================= 283 STRING auth_users NULL 284 STRING charset NULL 285 STRING comment NULL 286+STRING db_config NULL 287 STRING dont_compress DEFAULT_DONT_COMPRESS 288 STRING early_exec NULL 289 STRING exclude NULL 290@@ -51,6 +52,7 @@ INTEGER timeout 0 291 292 ENUM syslog_facility LOG_DAEMON 293 294+BOOL db_lax False 295 BOOL fake_super False 296 BOOL forward_lookup True 297 BOOL ignore_errors False 298diff --git a/db.c b/db.c 299new file mode 100644 300--- /dev/null 301+++ b/db.c 302@@ -0,0 +1,1943 @@ 303+/* 304+ * Routines to access extended file info via DB. 305+ * 306+ * Copyright (C) 2008-2013 Wayne Davison 307+ * 308+ * This program is free software; you can redistribute it and/or modify 309+ * it under the terms of the GNU General Public License as published by 310+ * the Free Software Foundation; either version 3 of the License, or 311+ * (at your option) any later version. 312+ * 313+ * This program is distributed in the hope that it will be useful, 314+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 315+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 316+ * GNU General Public License for more details. 317+ * 318+ * You should have received a copy of the GNU General Public License along 319+ * with this program; if not, visit the http://fsf.org website. 320+ */ 321+ 322+#include "rsync.h" 323+#include "ifuncs.h" 324+#include "itypes.h" 325+#include "inums.h" 326+#ifdef USE_OPENSSL 327+#include "openssl/md4.h" 328+#include "openssl/md5.h" 329+#endif 330+ 331+extern int recurse; 332+extern int same_db; 333+extern int am_receiver; 334+extern int am_generator; 335+extern int checksum_type; 336+extern int db_clean, db_check, db_do_md4, db_do_md5, db_update, db_lax, db_init, db_mounts; 337+extern int db_output_name, db_output_sum, db_output_info, db_output_unchanged, db_output_dirs, db_output_msgs; 338+extern int saw_db_output_opt, saw_db_sum_opt; 339+extern char *db_config; 340+ 341+#define MOUNT_HELPER_SCRIPT "/usr/sbin/rsyncdb-mountinfo" 342+ 343+#if defined HAVE_MYSQL_MYSQL_H && defined HAVE_LIBMYSQLCLIENT 344+#define USE_MYSQL 345+#include <mysql/mysql.h> 346+#include <mysql/errmsg.h> 347+#endif 348+ 349+#if defined HAVE_SQLITE3_H && defined HAVE_LIBSQLITE3 350+#define USE_SQLITE 351+#include <sqlite3.h> 352+#ifndef HAVE_SQLITE3_OPEN_V2 353+#define sqlite3_open_v2(dbname, dbhptr, flags, vfs) \ 354+ sqlite3_open(dbname, dbhptr) 355+#endif 356+#ifndef HAVE_SQLITE3_PREPARE_V2 357+#define sqlite3_prepare_v2 sqlite3_prepare 358+#endif 359+#define MAX_LOCK_FAILURES 10 360+#define LOCK_FAIL_MSLEEP 100 361+#endif 362+ 363+#ifndef USE_OPENSSL 364+#define MD5_CTX md_context 365+#define MD5_Init md5_begin 366+#define MD5_Update md5_update 367+#define MD5_Final(digest, cptr) md5_result(cptr, digest) 368+#endif 369+ 370+#define DB_TYPE_NONE 0 371+#define DB_TYPE_MYSQL 1 372+#define DB_TYPE_SQLITE 2 373+ 374+int use_db = DB_TYPE_NONE; 375+int select_many_sums = 0; 376+ 377+#define PREP_NORM 0 378+#define PREP_MOUNT 1 379+ 380+static const char *dbhost = NULL, *dbuser = NULL, *dbpass = NULL, *dbname = NULL; 381+static unsigned int dbport = 0; 382+static int transaction_state = -1; 383+ 384+static union { 385+#ifdef USE_MYSQL 386+ MYSQL *mysql; 387+#endif 388+#ifdef USE_SQLITE 389+ sqlite3 *sqlite; 390+#endif 391+ void *all; 392+} dbh; 393+ 394+#define SEL_DEV 0 395+#define SEL_SUM 1 396+#define REP_SUM 2 397+#define UPD_CTIME 3 398+#define INS_MOUNT 4 399+#define UPD_MOUNT 5 /* SQLite only */ 400+#define SEL_MOUNT 6 401+#define UN_MOUNT 7 402+#define DEL_SUMS 8 403+#define INS_PRESENT 9 404+#define MAX_PREP_CNT 10 405+ 406+#define MAX_BIND_CNT 7 407+#define MAX_RESULT_BINDS 32 408+ 409+static union { 410+#ifdef USE_MYSQL 411+ MYSQL_STMT *mysql; 412+#endif 413+#ifdef USE_SQLITE 414+ sqlite3_stmt *sqlite; 415+#endif 416+ void *all; 417+} statements[MAX_PREP_CNT]; 418+ 419+static int md_num; 420+static enum logcode log_code; 421+ 422+#ifdef USE_MYSQL 423+static unsigned int bind_disk_id, bind_mdnum; 424+static int64 bind_devno, bind_ino, bind_size, bind_mtime, bind_ctime; 425+static char bind_sum[MAX_DIGEST_LEN]; 426+static unsigned long result_length[MAX_RESULT_BINDS]; 427+static bool result_is_null[MAX_RESULT_BINDS], result_error[MAX_RESULT_BINDS]; 428+#elif defined USE_SQLITE 429+static int64 bind_mtime; 430+#endif 431+static char bind_thishost[128+1]; 432+static unsigned long bind_thishost_len; 433+static char *mount_helper_script = NULL; 434+ 435+static char *error_log; 436+#if defined USE_SQLITE && defined SQLITE_CONFIG_LOG 437+static char bind_mount_uniq[128+1]; 438+static unsigned long bind_mount_uniq_len; 439+static FILE *error_log_fp; 440+#endif 441+ 442+#define PTR_SIZE (sizeof (struct file_struct *)) 443+ 444+#if defined USE_MYSQL || defined USE_SQLITE 445+static void update_mounts(void); 446+#endif 447+ 448+struct name_list { 449+ struct name_list *next; 450+ char name[1]; 451+} *dirs_list; 452+ 453+int db_read_config(enum logcode code, const char *config_file) 454+{ 455+ char buf[2048], *cp; 456+ FILE *fp; 457+ int lineno = 0; 458+ 459+ log_code = code; 460+ 461+ bind_thishost_len = strlcpy(bind_thishost, "localhost", sizeof bind_thishost); 462+ 463+ if (!(fp = fopen(config_file, "r"))) { 464+ rsyserr(log_code, errno, "unable to open %s", config_file); 465+ return 0; 466+ } 467+ if (DEBUG_GTE(DB, 1)) 468+ rprintf(FCLIENT, "[%s] Reading DB config from %s\n", who_am_i(), config_file); 469+ while (fgets(buf, sizeof buf, fp)) { 470+ lineno++; 471+ if ((cp = strchr(buf, '#')) == NULL 472+ && (cp = strchr(buf, '\r')) == NULL 473+ && (cp = strchr(buf, '\n')) == NULL) 474+ cp = buf + strlen(buf); 475+ while (cp != buf && isSpace(cp-1)) cp--; 476+ *cp = '\0'; 477+ 478+ if (!*buf) 479+ continue; 480+ 481+ if (!(cp = strchr(buf, ':'))) 482+ goto invalid_line; 483+ *cp++ = '\0'; 484+ 485+ while (isSpace(cp)) cp++; 486+ if (strcasecmp(buf, "dbhost") == 0) 487+ dbhost = strdup(cp); 488+ else if (strcasecmp(buf, "dbuser") == 0) 489+ dbuser = strdup(cp); 490+ else if (strcasecmp(buf, "dbpass") == 0) 491+ dbpass = strdup(cp); 492+ else if (strcasecmp(buf, "dbname") == 0) 493+ dbname = strdup(cp); 494+ else if (strcasecmp(buf, "dbport") == 0) 495+ dbport = atoi(cp); 496+ else if (strcasecmp(buf, "transaction") == 0) 497+ transaction_state = atoi(cp) ? 0 : -1; 498+ else if (strcasecmp(buf, "mountHelper") == 0) 499+ mount_helper_script = strdup(cp); 500+ else if (strcasecmp(buf, "errlog") == 0) 501+ error_log = strdup(cp); 502+ else if (strcasecmp(buf, "thishost") == 0) 503+ bind_thishost_len = strlcpy(bind_thishost, cp, sizeof bind_thishost); 504+ else if (strcasecmp(buf, "dbtype") == 0) { 505+#ifdef USE_MYSQL 506+ if (strcasecmp(cp, "mysql") == 0) { 507+ use_db = DB_TYPE_MYSQL; 508+ continue; 509+ } 510+#endif 511+#ifdef USE_SQLITE 512+ if (strcasecmp(cp, "sqlite") == 0) { 513+ use_db = DB_TYPE_SQLITE; 514+ continue; 515+ } 516+#endif 517+ rprintf(log_code, 518+ "Unsupported dbtype on line #%d in %s.\n", 519+ lineno, config_file); 520+ use_db = DB_TYPE_NONE; 521+ return 0; 522+ } else { 523+ invalid_line: 524+ rprintf(log_code, "Invalid line #%d in %s\n", 525+ lineno, config_file); 526+ use_db = DB_TYPE_NONE; 527+ return 0; 528+ } 529+ } 530+ fclose(fp); 531+ 532+ if (bind_thishost_len >= (int)sizeof bind_thishost) 533+ bind_thishost_len = sizeof bind_thishost - 1; 534+ 535+ if (!use_db || !dbname) { 536+ rprintf(log_code, "Please specify at least dbtype and dbname in %s.\n", config_file); 537+ use_db = DB_TYPE_NONE; 538+ return 0; 539+ } 540+ 541+ md_num = checksum_type == 5 ? 5 : 4; 542+ 543+ if (error_log) { 544+ if (use_db != DB_TYPE_SQLITE) 545+ rprintf(log_code, "Ignoring errlog setting for non-SQLite DB.\n"); 546+#ifndef SQLITE_CONFIG_LOG 547+ else 548+ rprintf(log_code, "Your sqlite doesn't support SQLITE_CONFIG_LOG.\n"); 549+#endif 550+ } 551+ 552+ if (!mount_helper_script) 553+ mount_helper_script = MOUNT_HELPER_SCRIPT; 554+ 555+ return 1; 556+} 557+ 558+#if defined USE_SQLITE && defined SQLITE_CONFIG_LOG 559+static void errorLogCallback(UNUSED(void *pArg), int iErrCode, const char *zMsg) 560+{ 561+ fprintf(error_log_fp, "[%d] %s (%d)\n", (int)getpid(), zMsg, iErrCode); 562+} 563+#endif 564+ 565+static int run_sql(const char *fmt, ...) 566+{ 567+ va_list ap; 568+ char *query; 569+ int ok = 0, qlen; 570+ 571+ va_start(ap, fmt); 572+ qlen = vasprintf(&query, fmt, ap); 573+ va_end(ap); 574+ if (qlen < 0) 575+ out_of_memory("run_sql"); 576+ if (DEBUG_GTE(DB, 3)) 577+ rprintf(FCLIENT, "[%s] SQL being run: %s\n", who_am_i(), query); 578+ 579+ switch (use_db) { 580+#ifdef USE_MYSQL 581+ case DB_TYPE_MYSQL: 582+ if (mysql_query(dbh.mysql, query) < 0) { 583+ rprintf(FERROR, "Failed to run sql: %s\n", mysql_error(dbh.mysql)); 584+ rprintf(FERROR, "%s\n", query); 585+ } else 586+ ok = 1; 587+ break; 588+#endif 589+#ifdef USE_SQLITE 590+ case DB_TYPE_SQLITE: { 591+ int rc, lock_failures = 0; 592+ while (1) { 593+ if ((rc = sqlite3_exec(dbh.sqlite, query, NULL, NULL, NULL)) == 0) 594+ break; 595+ if (rc != SQLITE_BUSY && rc != SQLITE_LOCKED) 596+ break; 597+ if (++lock_failures > MAX_LOCK_FAILURES) 598+ break; 599+ msleep(LOCK_FAIL_MSLEEP); 600+ } 601+ if (rc) { 602+ rprintf(FERROR, "[%s] Failed to run sql: %s\n", who_am_i(), sqlite3_errmsg(dbh.sqlite)); 603+ rprintf(FERROR, "%s\n", query); 604+ } else 605+ ok = 1; 606+ break; 607+ } 608+#endif 609+ } 610+ 611+ free(query); 612+ 613+ return ok; 614+} 615+ 616+#ifdef USE_MYSQL 617+static int prepare_mysql(int ndx, MYSQL_BIND *binds, int bind_cnt, const char *fmt, ...) 618+{ 619+ va_list ap; 620+ char *query; 621+ int qlen, param_cnt; 622+ MYSQL_STMT *stmt = mysql_stmt_init(dbh.mysql); 623+ 624+ if (stmt == NULL) 625+ out_of_memory("prepare_mysql"); 626+ 627+ va_start(ap, fmt); 628+ qlen = vasprintf(&query, fmt, ap); 629+ va_end(ap); 630+ if (qlen < 0) 631+ out_of_memory("prepare_mysql"); 632+ if (DEBUG_GTE(DB, 3)) 633+ rprintf(FCLIENT, "[%s] SQL being prepared: %s\n", who_am_i(), query); 634+ 635+ if (mysql_stmt_prepare(stmt, query, qlen) != 0) { 636+ rprintf(log_code, "[%s] Prepare failed: %s\n", who_am_i(), mysql_stmt_error(stmt)); 637+ rprintf(log_code, "%s\n", query); 638+ free(query); 639+ return 0; 640+ } 641+ 642+ if ((param_cnt = mysql_stmt_param_count(stmt)) != bind_cnt) { 643+ rprintf(log_code, "[%s] Parameters in statement = %d, bind vars = %d\n", 644+ who_am_i(), param_cnt, bind_cnt); 645+ rprintf(log_code, "%s\n", query); 646+ free(query); 647+ return 0; 648+ } 649+ if (bind_cnt) 650+ mysql_stmt_bind_param(stmt, binds); 651+ 652+ statements[ndx].mysql = stmt; 653+ free(query); 654+ 655+ return 1; 656+} 657+#endif 658+ 659+#ifdef USE_MYSQL 660+static int prepare_mysql_queries(int type) 661+{ 662+ MYSQL_BIND binds[MAX_BIND_CNT]; 663+ char *sql; 664+ 665+ switch (type) { 666+ case PREP_NORM: 667+ sql="SELECT disk_id" 668+ " FROM disk" 669+ " WHERE host = ? AND devno = ?"; 670+ memset(binds, 0, sizeof binds); 671+ binds[0].buffer_type = MYSQL_TYPE_STRING; 672+ binds[0].buffer = &bind_thishost; 673+ binds[0].buffer_length = bind_thishost_len; 674+ binds[1].buffer_type = MYSQL_TYPE_LONGLONG; 675+ binds[1].buffer = &bind_devno; 676+ if (!prepare_mysql(SEL_DEV, binds, 2, sql)) 677+ return 0; 678+ 679+ memset(binds, 0, sizeof binds); 680+ binds[0].buffer_type = MYSQL_TYPE_LONG; 681+ binds[0].buffer = &bind_disk_id; 682+ binds[1].buffer_type = MYSQL_TYPE_LONGLONG; 683+ binds[1].buffer = &bind_ino; 684+ if (select_many_sums) { 685+ sql="SELECT checksum, sum_type, size, mtime, ctime" 686+ " FROM inode_map" 687+ " WHERE disk_id = ? AND ino = ?"; 688+ if (!prepare_mysql(SEL_SUM, binds, 2, sql)) 689+ return 0; 690+ } else { 691+ sql="SELECT checksum" 692+ " FROM inode_map" 693+ " WHERE disk_id = ? AND ino = ? AND sum_type = %d" 694+ " AND size = ? AND mtime = ? %s"; /* optional: AND ctime = ? */ 695+ binds[2].buffer_type = MYSQL_TYPE_LONGLONG; 696+ binds[2].buffer = &bind_size; 697+ binds[3].buffer_type = MYSQL_TYPE_LONGLONG; 698+ binds[3].buffer = &bind_mtime; 699+ if (!db_lax) { 700+ binds[4].buffer_type = MYSQL_TYPE_LONGLONG; 701+ binds[4].buffer = &bind_ctime; 702+ } 703+ if (!prepare_mysql(SEL_SUM, binds, 4 + !db_lax, sql, md_num, db_lax ? "" : "AND ctime = ?")) 704+ return 0; 705+ } 706+ 707+ sql="INSERT INTO inode_map" 708+ " SET disk_id = ?, ino = ?, sum_type = ?," 709+ " size = ?, mtime = ?, ctime = ?, checksum = ?" 710+ " ON DUPLICATE KEY" 711+ " UPDATE size = VALUES(size), mtime = VALUES(mtime)," 712+ " ctime = VALUES(ctime), checksum = VALUES(checksum)"; 713+ memset(binds, 0, sizeof binds); 714+ binds[0].buffer_type = MYSQL_TYPE_LONG; 715+ binds[0].buffer = &bind_disk_id; 716+ binds[1].buffer_type = MYSQL_TYPE_LONGLONG; 717+ binds[1].buffer = &bind_ino; 718+ binds[2].buffer_type = MYSQL_TYPE_LONG; 719+ binds[2].buffer = &bind_mdnum; 720+ binds[3].buffer_type = MYSQL_TYPE_LONGLONG; 721+ binds[3].buffer = &bind_size; 722+ binds[4].buffer_type = MYSQL_TYPE_LONGLONG; 723+ binds[4].buffer = &bind_mtime; 724+ binds[5].buffer_type = MYSQL_TYPE_LONGLONG; 725+ binds[5].buffer = &bind_ctime; 726+ binds[6].buffer_type = MYSQL_TYPE_BLOB; 727+ binds[6].buffer = &bind_sum; 728+ binds[6].buffer_length = MD5_DIGEST_LEN; /* Same as MD4_DIGEST_LEN */ 729+ if (!prepare_mysql(REP_SUM, binds, 7, sql)) 730+ return 0; 731+ 732+ sql="UPDATE inode_map" 733+ " SET ctime = ?" 734+ " WHERE disk_id = ? AND ino = ? AND sum_type = ? AND size = ? AND mtime = ?"; 735+ memset(binds, 0, sizeof binds); 736+ binds[0].buffer_type = MYSQL_TYPE_LONGLONG; 737+ binds[0].buffer = &bind_ctime; 738+ binds[1].buffer_type = MYSQL_TYPE_LONG; 739+ binds[1].buffer = &bind_disk_id; 740+ binds[2].buffer_type = MYSQL_TYPE_LONGLONG; 741+ binds[2].buffer = &bind_ino; 742+ binds[3].buffer_type = MYSQL_TYPE_LONG; 743+ binds[3].buffer = &bind_mdnum; 744+ binds[4].buffer_type = MYSQL_TYPE_LONGLONG; 745+ binds[4].buffer = &bind_size; 746+ binds[5].buffer_type = MYSQL_TYPE_LONGLONG; 747+ binds[5].buffer = &bind_mtime; 748+ if (!prepare_mysql(UPD_CTIME, binds, 6, sql)) 749+ return 0; 750+ break; 751+ 752+ case PREP_MOUNT: 753+ sql="INSERT INTO disk" 754+ " SET host = ?, last_seen = ?, mount_uniq = ?, devno = ?" 755+ " ON DUPLICATE KEY" 756+ " UPDATE last_seen = VALUES(last_seen), devno = VALUES(devno)"; 757+ memset(binds, 0, sizeof binds); 758+ binds[0].buffer_type = MYSQL_TYPE_STRING; 759+ binds[0].buffer = &bind_thishost; 760+ binds[0].buffer_length = bind_thishost_len; 761+ binds[1].buffer_type = MYSQL_TYPE_LONGLONG; 762+ binds[1].buffer = &bind_mtime; /* we abuse mtime to hold the last_seen value */ 763+ binds[2].buffer_type = MYSQL_TYPE_STRING; 764+ binds[2].buffer = &bind_mount_uniq; 765+ binds[2].buffer_length = sizeof bind_mount_uniq; 766+ binds[2].length = &bind_mount_uniq_len; 767+ binds[3].buffer_type = MYSQL_TYPE_LONGLONG; 768+ binds[3].buffer = &bind_devno; 769+ if (!prepare_mysql(INS_MOUNT, binds, 4, sql)) 770+ return 0; 771+ 772+ sql="SELECT mount_uniq" 773+ " FROM disk" 774+ " WHERE host = ? AND last_seen < ? AND devno != 0"; 775+ /* Reusing first 2 binds from INS_MOUNT */ 776+ if (!prepare_mysql(SEL_MOUNT, binds, 2, sql)) 777+ return 0; 778+ 779+ sql="UPDATE disk" 780+ " SET devno = 0" 781+ " WHERE host = ? AND last_seen < ? AND devno != 0"; 782+ /* Reusing binds from SEL_MOUNT */ 783+ if (!prepare_mysql(UN_MOUNT, binds, 2, sql)) 784+ return 0; 785+ break; 786+ } 787+ 788+ return 1; 789+} 790+#endif 791+ 792+#ifdef USE_MYSQL 793+static int db_connect_mysql(void) 794+{ 795+ const char *open_dbname = db_init ? "mysql" : dbname; 796+ 797+ if (!(dbh.mysql = mysql_init(NULL))) 798+ out_of_memory("db_read_config"); 799+ 800+ if (DEBUG_GTE(DB, 1)) { 801+ rprintf(FCLIENT, "[%s] connecting: host=%s user=%s db=%s port=%d\n", 802+ who_am_i(), dbhost, dbuser, open_dbname, dbport); 803+ } 804+ if (!mysql_real_connect(dbh.mysql, dbhost, dbuser, dbpass, open_dbname, dbport, NULL, 0)) { 805+ rprintf(log_code, "[%s] Unable to connect to DB: %s\n", who_am_i(), mysql_error(dbh.mysql)); 806+ return 0; 807+ } 808+ 809+ if (db_init) { 810+ if (db_output_msgs) 811+ rprintf(FCLIENT, "Creating DB %s (if it does not exist)\n", dbname); 812+ if (!run_sql("CREATE DATABASE IF NOT EXISTS `%s`", dbname) 813+ || !run_sql("USE `%s`", dbname)) 814+ exit_cleanup(RERR_IPC); 815+ 816+ if (db_output_msgs) 817+ rprintf(FCLIENT, "Dropping old tables (if they exist))\n"); 818+ if (!run_sql("DROP TABLE IF EXISTS disk") 819+ || !run_sql("DROP TABLE IF EXISTS inode_map")) 820+ exit_cleanup(RERR_IPC); 821+ 822+ if (db_output_msgs) 823+ rprintf(FCLIENT, "Creating empty tables ...\n"); 824+ if (!run_sql( 825+ "CREATE TABLE disk (\n" 826+ " disk_id integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,\n" 827+ " host varchar(128) NOT NULL default 'localhost',\n" 828+ " mount_uniq varchar(128) default NULL,\n" 829+ " devno bigint unsigned NOT NULL,\n" /* This is 0 when not mounted */ 830+ " last_seen bigint NOT NULL,\n" 831+ " UNIQUE KEY mount_lookup (host, mount_uniq),\n" 832+ " KEY dev_lookup (devno, host)\n" 833+ ")")) 834+ exit_cleanup(RERR_IPC); 835+ 836+ if (!run_sql( 837+ "CREATE TABLE inode_map (\n" 838+ " disk_id integer unsigned NOT NULL,\n" 839+ " ino bigint unsigned NOT NULL,\n" 840+ " sum_type tinyint NOT NULL default '0',\n" 841+ " size bigint unsigned NOT NULL,\n" 842+ " mtime bigint NOT NULL,\n" 843+ " ctime bigint NOT NULL,\n" 844+ " checksum binary(16) NOT NULL,\n" 845+ " PRIMARY KEY (disk_id,ino,sum_type)\n" 846+ ")")) 847+ exit_cleanup(RERR_IPC); 848+ 849+ if (!db_mounts) 850+ exit_cleanup(0); 851+ } 852+ 853+ if (db_mounts) { 854+ if (!prepare_mysql_queries(PREP_MOUNT)) 855+ exit_cleanup(RERR_IPC); 856+ update_mounts(); 857+ exit_cleanup(0); 858+ } 859+ 860+ if (!prepare_mysql_queries(PREP_NORM)) 861+ return 0; 862+ 863+ return 1; 864+} 865+#endif 866+ 867+#ifdef USE_SQLITE 868+static int prepare_sqlite(int ndx, const char *fmt, ...) 869+{ 870+ va_list ap; 871+ char *query; 872+ int rc, qlen, lock_failures = 0; 873+ 874+ va_start(ap, fmt); 875+ qlen = vasprintf(&query, fmt, ap); 876+ va_end(ap); 877+ if (qlen < 0) 878+ out_of_memory("prepare_sqlite"); 879+ if (DEBUG_GTE(DB, 3)) 880+ rprintf(FCLIENT, "[%s] SQL being prepared: %s\n", who_am_i(), query); 881+ 882+ while ((rc = sqlite3_prepare_v2(dbh.sqlite, query, -1, &statements[ndx].sqlite, NULL)) != 0) { 883+ if (DEBUG_GTE(DB, 4)) { 884+ rprintf(FCLIENT, "[%s] sqlite3_prepare_v2(,%s,,) returned %d\n", 885+ who_am_i(), query, rc); 886+ } 887+ if (rc != SQLITE_BUSY && rc != SQLITE_LOCKED) 888+ break; 889+ if (++lock_failures > MAX_LOCK_FAILURES) 890+ break; 891+ msleep(LOCK_FAIL_MSLEEP); 892+ } 893+ if (rc) { 894+ rprintf(log_code, "[%s] Failed to prepare SQL: %s (%d)\n", who_am_i(), sqlite3_errmsg(dbh.sqlite), rc); 895+ rprintf(log_code, "%s\n", query); 896+ free(query); 897+ return 0; 898+ } 899+ free(query); 900+ 901+ return 1; 902+} 903+#endif 904+ 905+#ifdef USE_SQLITE 906+static int prepare_sqlite_queries(int type) 907+{ 908+ char *sql; 909+ 910+ switch (type) { 911+ case PREP_NORM: 912+ sql="SELECT disk_id" 913+ " FROM disk" 914+ " WHERE host = ? AND devno = ?"; 915+ if (!prepare_sqlite(SEL_DEV, sql)) 916+ return 0; 917+ 918+ if (select_many_sums) { 919+ sql="SELECT checksum, sum_type, size, mtime, ctime" 920+ " FROM inode_map" 921+ " WHERE disk_id = ? AND ino = ?"; 922+ if (!prepare_sqlite(SEL_SUM, sql)) 923+ return 0; 924+ } else { 925+ sql="SELECT checksum" 926+ " FROM inode_map" 927+ " WHERE disk_id = ? AND ino = ? AND sum_type = %d" 928+ " AND size = ? AND mtime = ? %s"; 929+ if (!prepare_sqlite(SEL_SUM, sql, md_num, db_lax ? "" : "AND ctime = ?")) 930+ return 0; 931+ } 932+ 933+ sql="INSERT OR REPLACE INTO inode_map" 934+ " (disk_id, ino, sum_type, size, mtime, ctime, checksum)" 935+ " VALUES (?, ?, ?, ?, ?, ?, ?)"; 936+ if (!prepare_sqlite(REP_SUM, sql)) 937+ return 0; 938+ 939+ sql="UPDATE inode_map" 940+ " SET ctime = ?" 941+ " WHERE disk_id = ? AND ino = ? AND sum_type = ? AND size = ? AND mtime = ?"; 942+ if (!prepare_sqlite(UPD_CTIME, sql)) 943+ return 0; 944+ break; 945+ 946+ case PREP_MOUNT: 947+ sql="INSERT OR IGNORE INTO disk" 948+ " (host, last_seen, mount_uniq, devno)" 949+ " VALUES (?, ?, ?, ?)"; 950+ if (!prepare_sqlite(INS_MOUNT, sql)) 951+ return 0; 952+ 953+ sql="UPDATE disk" 954+ " SET last_seen = ?, devno = ?" 955+ " WHERE host = ? AND mount_uniq = ?"; 956+ if (!prepare_sqlite(UPD_MOUNT, sql)) 957+ return 0; 958+ 959+ sql="SELECT mount_uniq" 960+ " FROM disk" 961+ " WHERE host = ? AND last_seen < ? AND devno != 0"; 962+ if (!prepare_sqlite(SEL_MOUNT, sql)) 963+ return 0; 964+ 965+ sql="UPDATE disk" 966+ " SET devno = 0" 967+ " WHERE host = ? AND last_seen < ? AND devno != 0"; 968+ if (!prepare_sqlite(UN_MOUNT, sql)) 969+ return 0; 970+ break; 971+ } 972+ 973+ return 1; 974+} 975+#endif 976+ 977+#ifdef USE_SQLITE 978+static int db_connect_sqlite(void) 979+{ 980+ int lock_failures = 0; 981+ int rc; 982+ 983+#ifdef SQLITE_CONFIG_LOG 984+ if (error_log) { 985+ if (DEBUG_GTE(DB, 1)) 986+ rprintf(FCLIENT, "[%s] Setting sqlite errlog to %s\n", who_am_i(), error_log); 987+ if (!(error_log_fp = fopen(error_log, "a"))) { 988+ rsyserr(log_code, errno, "unable to append to logfile %s", error_log); 989+ error_log = NULL; 990+ } else if (sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, NULL) != 0) 991+ rprintf(log_code, "Failed to set errorLogCallback: %s\n", sqlite3_errmsg(dbh.sqlite)); 992+ } 993+#endif 994+ 995+ while (1) { 996+ int open_flags = SQLITE_OPEN_READWRITE; 997+ if (db_init) 998+ open_flags |= SQLITE_OPEN_CREATE; 999+ if (DEBUG_GTE(DB, 1)) 1000+ rprintf(FCLIENT, "[%s] opening %s (%d)\n", who_am_i(), dbname, open_flags); 1001+ if ((rc = sqlite3_open_v2(dbname, &dbh.sqlite, open_flags, NULL)) == 0) { 1002+ break; 1003+ } 1004+ if (DEBUG_GTE(DB, 4)) { 1005+ rprintf(FCLIENT, "[%s] sqlite3_open_v2(%s,,%d,NULL) returned %d\n", 1006+ who_am_i(), dbname, open_flags, rc); 1007+ } 1008+ if (rc != SQLITE_BUSY && rc != SQLITE_LOCKED) 1009+ break; 1010+ if (++lock_failures > MAX_LOCK_FAILURES) 1011+ break; 1012+ msleep(LOCK_FAIL_MSLEEP); 1013+ } 1014+ 1015+ if (rc) { 1016+ rprintf(log_code, "Unable to connect to DB: %s (%d)\n", sqlite3_errmsg(dbh.sqlite), rc); 1017+ return 0; 1018+ } 1019+ 1020+ if (db_init) { 1021+ char *sql; 1022+ if (db_output_msgs) 1023+ rprintf(FCLIENT, "Dropping old tables (if they exist) ...\n"); 1024+ if (!run_sql("DROP TABLE IF EXISTS disk") 1025+ || !run_sql("DROP TABLE IF EXISTS inode_map")) 1026+ exit_cleanup(RERR_IPC); 1027+ 1028+ if (db_output_msgs) 1029+ rprintf(FCLIENT, "Creating empty tables ...\n"); 1030+ sql="CREATE TABLE disk (\n" 1031+ " disk_id integer NOT NULL PRIMARY KEY AUTOINCREMENT,\n" 1032+ " host varchar(128) NOT NULL default 'localhost',\n" 1033+ " mount_uniq varchar(128) default NULL,\n" 1034+ " devno bigint NOT NULL,\n" /* This is 0 when not mounted */ 1035+ " last_seen bigint NOT NULL,\n" 1036+ " UNIQUE (host, mount_uniq)\n" 1037+ ")"; 1038+ if (!run_sql(sql)) 1039+ exit_cleanup(RERR_IPC); 1040+ 1041+ sql="CREATE TABLE inode_map (\n" 1042+ " disk_id integer NOT NULL,\n" 1043+ " ino bigint NOT NULL,\n" 1044+ " size bigint NOT NULL,\n" 1045+ " mtime bigint NOT NULL,\n" 1046+ " ctime bigint NOT NULL,\n" 1047+ " sum_type tinyint NOT NULL default '0',\n" 1048+ " checksum binary(16) NOT NULL,\n" 1049+ " PRIMARY KEY (disk_id,ino,sum_type)\n" 1050+ ")"; 1051+ if (!run_sql(sql)) 1052+ exit_cleanup(RERR_IPC); 1053+ 1054+#if SQLITE_VERSION_NUMBER >= 3007000 1055+ /* Using WAL locking makes concurrency much better (requires sqlite 3.7.0). */ 1056+ sql="PRAGMA journal_mode = wal"; 1057+ run_sql(sql); /* We don't check this for success. */ 1058+#endif 1059+ 1060+ if (!db_mounts) 1061+ exit_cleanup(0); 1062+ } 1063+ 1064+ if (db_mounts) { 1065+ if (!prepare_sqlite_queries(PREP_MOUNT)) 1066+ exit_cleanup(RERR_IPC); 1067+ update_mounts(); 1068+ exit_cleanup(0); 1069+ } 1070+ 1071+ if (!prepare_sqlite_queries(PREP_NORM)) { 1072+ db_disconnect(False); 1073+ return 0; 1074+ } 1075+ 1076+ return 1; 1077+} 1078+#endif 1079+ 1080+int db_connect(int select_many) 1081+{ 1082+ select_many_sums = select_many; 1083+ 1084+ switch (use_db) { 1085+#ifdef USE_MYSQL 1086+ case DB_TYPE_MYSQL: 1087+ if (db_connect_mysql()) 1088+ return 1; 1089+ break; 1090+#endif 1091+#ifdef USE_SQLITE 1092+ case DB_TYPE_SQLITE: 1093+ if (db_connect_sqlite()) 1094+ return 1; 1095+ break; 1096+#endif 1097+ } 1098+ 1099+ db_disconnect(False); 1100+ 1101+ return 0; 1102+} 1103+ 1104+void db_disconnect(BOOL commit) 1105+{ 1106+ int ndx; 1107+ 1108+ if (!dbh.all) 1109+ return; 1110+ 1111+ if (transaction_state > 0) { 1112+ if (DEBUG_GTE(DB, 1)) { 1113+ rprintf(FCLIENT, "[%s] %s our DB transaction\n", 1114+ who_am_i(), commit ? "Committing" : "Rolling back"); 1115+ } 1116+ transaction_state = 0; 1117+ if (commit) 1118+ run_sql("COMMIT"); 1119+ else 1120+ run_sql("ROLLBACK"); 1121+ } 1122+ 1123+ if (DEBUG_GTE(DB, 1)) 1124+ rprintf(FCLIENT, "[%s] Disconnecting from the DB\n", who_am_i()); 1125+ 1126+ for (ndx = 0; ndx < MAX_PREP_CNT; ndx++) { 1127+ if (statements[ndx].all) { 1128+ switch (use_db) { 1129+#ifdef USE_MYSQL 1130+ case DB_TYPE_MYSQL: 1131+ mysql_stmt_close(statements[ndx].mysql); 1132+ break; 1133+#endif 1134+#ifdef USE_SQLITE 1135+ case DB_TYPE_SQLITE: 1136+ sqlite3_finalize(statements[ndx].sqlite); 1137+ break; 1138+#endif 1139+ } 1140+ statements[ndx].all = NULL; 1141+ } 1142+ } 1143+ 1144+ switch (use_db) { 1145+#ifdef USE_MYSQL 1146+ case DB_TYPE_MYSQL: 1147+ mysql_close(dbh.mysql); 1148+ break; 1149+#endif 1150+#ifdef USE_SQLITE 1151+ case DB_TYPE_SQLITE: 1152+ sqlite3_close(dbh.sqlite); 1153+ break; 1154+#endif 1155+ } 1156+ 1157+ dbh.all = NULL; 1158+ use_db = DB_TYPE_NONE; 1159+} 1160+ 1161+#ifdef USE_MYSQL 1162+static MYSQL_STMT *exec_mysql(int ndx) 1163+{ 1164+ MYSQL_STMT *stmt = statements[ndx].mysql; 1165+ int rc; 1166+ 1167+ if ((rc = mysql_stmt_execute(stmt)) == CR_SERVER_LOST) { 1168+ db_disconnect(False); 1169+ use_db = DB_TYPE_MYSQL; 1170+ if (db_connect(select_many_sums)) { 1171+ stmt = statements[ndx].mysql; 1172+ rc = mysql_stmt_execute(stmt); 1173+ } 1174+ } 1175+ if (rc != 0) { 1176+ rprintf(log_code, "SQL execute failed: %s\n", mysql_stmt_error(stmt)); 1177+ return NULL; 1178+ } 1179+ 1180+ return stmt; 1181+} 1182+#endif 1183+ 1184+#ifdef USE_MYSQL 1185+/* This stores up to max_rows into the values pointed to by the bind data arrays. 1186+ * If max_rows is > 1, then all the buffer pointers MUST be set to an array long 1187+ * enough to hold the max count of rows. The buffer pointer will be incremented 1188+ * to read additional rows (but never past the end). If stmt_ptr is non-NULL, it 1189+ * will be set to the "stmt" pointer IFF we didn't run out of rows before hitting 1190+ * the max. In this case, the caller should call mysql_stmt_fetch() to read any 1191+ * remaining rows (the buffer pointers will point at the final array element) and 1192+ * then call mysql_stmt_free_result(). If *stmt_ptr is a NULL value, there were 1193+ * not enough rows to fill the max_rows arrays, and the stmt was already freed. */ 1194+static int fetch_mysql(MYSQL_BIND *binds, int bind_cnt, int ndx, int max_rows, MYSQL_STMT **stmt_ptr) 1195+{ 1196+ MYSQL_STMT *stmt; 1197+ int i, rc, rows = 0; 1198+ 1199+ if (bind_cnt > MAX_RESULT_BINDS) { 1200+ fprintf(stderr, "Internal error: MAX_RESULT_BINDS overflow\n"); 1201+ exit_cleanup(RERR_UNSUPPORTED); 1202+ } 1203+ 1204+ if ((stmt = exec_mysql(ndx)) == NULL) 1205+ return 0; 1206+ 1207+ for (i = 0; i < bind_cnt; i++) { 1208+ binds[i].is_null = &result_is_null[i]; 1209+ binds[i].length = &result_length[i]; 1210+ binds[i].error = &result_error[i]; 1211+ } 1212+ mysql_stmt_bind_result(stmt, binds); 1213+ 1214+ while (rows < max_rows) { 1215+ if ((rc = mysql_stmt_fetch(stmt)) != 0) { 1216+ if (rc != MYSQL_NO_DATA) 1217+ rprintf(log_code, "SELECT fetch failed: %s\n", mysql_stmt_error(stmt)); 1218+ break; 1219+ } 1220+ if (++rows >= max_rows) 1221+ break; 1222+ for (i = 0; i < bind_cnt; i++) { 1223+ switch (binds[i].buffer_type) { 1224+ case MYSQL_TYPE_BLOB: 1225+ case MYSQL_TYPE_STRING: 1226+ binds[i].buffer += binds[i].buffer_length; 1227+ break; 1228+ case MYSQL_TYPE_LONG: 1229+ binds[i].buffer += sizeof (int); 1230+ break; 1231+ case MYSQL_TYPE_LONGLONG: 1232+ binds[i].buffer += sizeof (int64); 1233+ break; 1234+ default: 1235+ fprintf(stderr, "Unknown MYSQL_TYPE_* in multi-row read: %d.\n", binds[i].buffer_type); 1236+ exit_cleanup(RERR_UNSUPPORTED); 1237+ } 1238+ } 1239+ } 1240+ 1241+ if (!stmt_ptr || rows < max_rows) { 1242+ mysql_stmt_free_result(stmt); 1243+ stmt = NULL; 1244+ } 1245+ if (stmt_ptr) 1246+ *stmt_ptr = stmt; 1247+ 1248+ return rows; 1249+} 1250+#endif 1251+ 1252+#if defined USE_MYSQL || defined USE_SQLITE 1253+static void update_mounts(void) 1254+{ 1255+ char buf[2048], *argv[2]; 1256+ int f_from, f_to, len; 1257+ STRUCT_STAT st; 1258+ int pid, status; 1259+ 1260+ if (DEBUG_GTE(DB, 2)) 1261+ printf("Running %s to grab mount info\n", mount_helper_script); 1262+ argv[0] = mount_helper_script; 1263+ argv[1] = NULL; 1264+ pid = piped_child(argv, &f_from, &f_to); 1265+ close(f_to); 1266+ 1267+ bind_mtime = time(NULL); /* abuse mtime slightly to hold our last_seen value */ 1268+ 1269+ /* Strict format has 2 items with one tab as separator: MOUNT_UNIQ\tPATH */ 1270+ while ((len = read_line(f_from, buf, sizeof buf, 0)) > 0) { 1271+ char *mount_uniq, *path; 1272+ 1273+ if (DEBUG_GTE(DB, 3)) 1274+ printf("Parsing mount info: %s\n", buf); 1275+ mount_uniq = strtok(buf, "\t"); 1276+ path = mount_uniq ? strtok(NULL, "\r\n") : NULL; 1277+ if (!path) { 1278+ fprintf(stderr, "Failed to parse line from %s output\n", mount_helper_script); 1279+ exit_cleanup(RERR_SYNTAX); 1280+ } 1281+ 1282+ if (lstat(path, &st) < 0) { 1283+ fprintf(stderr, "Failed to lstat(%s): %s\n", path, strerror(errno)); 1284+ exit_cleanup(RERR_IPC); 1285+ } 1286+ 1287+ bind_mount_uniq_len = strlcpy(bind_mount_uniq, mount_uniq, sizeof bind_mount_uniq); 1288+ if (bind_mount_uniq_len >= (int)sizeof bind_mount_uniq) 1289+ bind_mount_uniq_len = sizeof bind_mount_uniq - 1; 1290+ 1291+ if (db_output_msgs) { 1292+ printf("Marking mount \"%s\" (%s) as a recent mount\n", 1293+ bind_mount_uniq, big_num(st.st_dev)); 1294+ } 1295+ switch (use_db) { 1296+#ifdef USE_MYSQL 1297+ case DB_TYPE_MYSQL: 1298+ bind_devno = st.st_dev; 1299+ if (exec_mysql(INS_MOUNT) == NULL) { 1300+ fprintf(stderr, "Failed to update mount info for \"%s\" - %s\n", 1301+ bind_mount_uniq, mysql_error(dbh.mysql)); 1302+ exit_cleanup(RERR_IPC); 1303+ } 1304+ break; 1305+#endif 1306+#ifdef USE_SQLITE 1307+ case DB_TYPE_SQLITE: { 1308+ int rc, change_cnt; 1309+ sqlite3_stmt *stmt = statements[INS_MOUNT].sqlite; 1310+ sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC); 1311+ sqlite3_bind_int64(stmt, 2, bind_mtime); 1312+ sqlite3_bind_text(stmt, 3, bind_mount_uniq, bind_mount_uniq_len, SQLITE_STATIC); 1313+ sqlite3_bind_int64(stmt, 4, st.st_dev); 1314+ rc = sqlite3_step(stmt); 1315+ if (rc != SQLITE_DONE) { 1316+ fprintf(stderr, "Failed to insert mount info for \"%s\" - %s (%d)\n", 1317+ bind_mount_uniq, sqlite3_errmsg(dbh.sqlite), rc); 1318+ exit_cleanup(RERR_IPC); 1319+ } 1320+ change_cnt = sqlite3_changes(dbh.sqlite); 1321+ sqlite3_reset(stmt); 1322+ if (change_cnt == 0) { 1323+ stmt = statements[UPD_MOUNT].sqlite; 1324+ sqlite3_bind_int64(stmt, 1, bind_mtime); 1325+ sqlite3_bind_int64(stmt, 2, st.st_dev); 1326+ sqlite3_bind_text(stmt, 3, bind_thishost, bind_thishost_len, SQLITE_STATIC); 1327+ sqlite3_bind_text(stmt, 4, bind_mount_uniq, bind_mount_uniq_len, SQLITE_STATIC); 1328+ rc = sqlite3_step(stmt); 1329+ if (rc != SQLITE_DONE) { 1330+ fprintf(stderr, "Failed to update mount info for \"%s\" - %s (%d)\n", 1331+ bind_mount_uniq, sqlite3_errmsg(dbh.sqlite), rc); 1332+ exit_cleanup(RERR_IPC); 1333+ } 1334+ sqlite3_reset(stmt); 1335+ } 1336+ break; 1337+ } 1338+#endif 1339+ } 1340+ } 1341+ close(f_from); 1342+ 1343+ waitpid(pid, &status, 0); 1344+ 1345+ switch (use_db) { 1346+#ifdef USE_MYSQL 1347+ case DB_TYPE_MYSQL: { 1348+ if (db_output_msgs) { 1349+ MYSQL_BIND binds[1]; 1350+ MYSQL_STMT *stmt; 1351+ 1352+ binds[0].buffer_type = MYSQL_TYPE_BLOB; 1353+ binds[0].buffer = bind_mount_uniq; 1354+ binds[0].buffer_length = sizeof bind_mount_uniq; 1355+ if (fetch_mysql(binds, 1, SEL_MOUNT, 1, &stmt)) { 1356+ while (1) { 1357+ printf("Marking mount \"%s\" as unmounted.\n", bind_mount_uniq); 1358+ if (mysql_stmt_fetch(stmt) != 0) 1359+ break; 1360+ } 1361+ mysql_stmt_free_result(stmt); 1362+ } 1363+ } 1364+ 1365+ if (exec_mysql(UN_MOUNT) == NULL) { 1366+ fprintf(stderr, "Failed to update old mount info - %s\n", 1367+ mysql_error(dbh.mysql)); 1368+ exit_cleanup(RERR_IPC); 1369+ } 1370+ break; 1371+ } 1372+#endif 1373+#ifdef USE_SQLITE 1374+ case DB_TYPE_SQLITE: { 1375+ sqlite3_stmt *stmt; 1376+ int rc; 1377+ 1378+ if (db_output_msgs) { 1379+ stmt = statements[SEL_MOUNT].sqlite; 1380+ sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC); 1381+ sqlite3_bind_int64(stmt, 2, bind_mtime); 1382+ while (1) { 1383+ if (sqlite3_step(stmt) != SQLITE_ROW) 1384+ break; 1385+ printf("Marking mount \"%s\" as unmounted.\n", sqlite3_column_text(stmt, 0)); 1386+ } 1387+ sqlite3_reset(stmt); 1388+ } 1389+ 1390+ stmt = statements[UN_MOUNT].sqlite; 1391+ sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC); 1392+ sqlite3_bind_int64(stmt, 2, bind_mtime); 1393+ rc = sqlite3_step(stmt); 1394+ sqlite3_reset(stmt); 1395+ if (rc != SQLITE_DONE) { 1396+ fprintf(stderr, "Failed to update old mount info - %s (%d)\n", 1397+ sqlite3_errmsg(dbh.sqlite), rc); 1398+ exit_cleanup(RERR_IPC); 1399+ } 1400+ break; 1401+ } 1402+#endif 1403+ } 1404+} 1405+#endif 1406+ 1407+static unsigned int get_disk_id(int64 devno) 1408+{ 1409+ static unsigned int prior_disk_id = 0; 1410+ static int64 prior_devno = 0; 1411+ 1412+ if (prior_devno == devno && prior_disk_id) { 1413+ if (DEBUG_GTE(DB, 5)) 1414+ rprintf(FCLIENT, "get_disk_id(%s,%s) = %d (cached)\n", bind_thishost, big_num(devno), prior_disk_id); 1415+ return prior_disk_id; 1416+ } 1417+ prior_devno = devno; 1418+ 1419+ switch (use_db) { 1420+#ifdef USE_MYSQL 1421+ case DB_TYPE_MYSQL: { 1422+ MYSQL_BIND binds[1]; 1423+ 1424+ bind_devno = devno; /* The one changing SEL_DEV input value. */ 1425+ 1426+ /* Bind where to put the output. */ 1427+ binds[0].buffer_type = MYSQL_TYPE_LONG; 1428+ binds[0].buffer = &prior_disk_id; 1429+ if (!fetch_mysql(binds, 1, SEL_DEV, 1, NULL)) 1430+ prior_disk_id = 0; 1431+ break; 1432+ } 1433+#endif 1434+#ifdef USE_SQLITE 1435+ case DB_TYPE_SQLITE: { 1436+ sqlite3_stmt *stmt = statements[SEL_DEV].sqlite; 1437+ sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC); 1438+ sqlite3_bind_int64(stmt, 2, devno); 1439+ if (sqlite3_step(stmt) == SQLITE_ROW) 1440+ prior_disk_id = sqlite3_column_int(stmt, 0); 1441+ else 1442+ prior_disk_id = 0; 1443+ sqlite3_reset(stmt); 1444+ break; 1445+ } 1446+#endif 1447+ } 1448+ 1449+ if (DEBUG_GTE(DB, 2)) 1450+ rprintf(FCLIENT, "get_disk_id(%s,%s) = %d\n", bind_thishost, big_num(devno), prior_disk_id); 1451+ return prior_disk_id; 1452+} 1453+ 1454+int db_get_checksum(const STRUCT_STAT *st_p, char *sum) 1455+{ 1456+ unsigned int disk_id = get_disk_id(st_p->st_dev); 1457+ int ok = 0; 1458+ 1459+ if (disk_id == 0) 1460+ return 0; 1461+ 1462+ switch (use_db) { 1463+#ifdef USE_MYSQL 1464+ case DB_TYPE_MYSQL: { 1465+ MYSQL_BIND binds[1]; 1466+ 1467+ bind_disk_id = disk_id; 1468+ bind_ino = st_p->st_ino; 1469+ bind_size = st_p->st_size; 1470+ bind_mtime = st_p->st_mtime; 1471+ if (!db_lax) 1472+ bind_ctime = st_p->st_ctime; 1473+ 1474+ binds[0].buffer_type = MYSQL_TYPE_BLOB; 1475+ binds[0].buffer = sum; 1476+ binds[0].buffer_length = MD5_DIGEST_LEN; 1477+ ok = fetch_mysql(binds, 1, SEL_SUM, 1, NULL); 1478+ break; 1479+ } 1480+#endif 1481+#ifdef USE_SQLITE 1482+ case DB_TYPE_SQLITE: { 1483+ sqlite3_stmt *stmt = statements[SEL_SUM].sqlite; 1484+ sqlite3_bind_int(stmt, 1, disk_id); 1485+ sqlite3_bind_int64(stmt, 2, st_p->st_ino); 1486+ sqlite3_bind_int64(stmt, 3, st_p->st_size); 1487+ sqlite3_bind_int64(stmt, 4, st_p->st_mtime); 1488+ if (!db_lax) 1489+ sqlite3_bind_int64(stmt, 5, st_p->st_ctime); 1490+ if (sqlite3_step(stmt) == SQLITE_ROW) { 1491+ int len = sqlite3_column_bytes(stmt, 0); 1492+ if (len > MAX_DIGEST_LEN) 1493+ len = MAX_DIGEST_LEN; 1494+ memcpy(sum, sqlite3_column_blob(stmt, 0), len); 1495+ ok = 1; 1496+ } 1497+ sqlite3_reset(stmt); 1498+ break; 1499+ } 1500+#endif 1501+ } 1502+ 1503+ if (DEBUG_GTE(DB, 2)) { 1504+ if (ok) { 1505+ rprintf(FCLIENT, "[%s] Found DB checksum for %s,%s,%d: %s\n", 1506+ who_am_i(), big_num(st_p->st_dev), 1507+ big_num(st_p->st_ino), md_num, sum_as_hex(md_num, sum, 0)); 1508+ } else { 1509+ rprintf(FCLIENT, "[%s] No DB checksum for %s,%s,%d\n", 1510+ who_am_i(), big_num(st_p->st_dev), 1511+ big_num(st_p->st_ino), md_num); 1512+ } 1513+ } 1514+ 1515+ return ok; 1516+} 1517+ 1518+int db_get_both_checksums(const STRUCT_STAT *st_p, int *right_sum_cnt, int *wrong_sum_cnt, char **sum4, char **sum5) 1519+{ 1520+ static char dbsum[MD5_DIGEST_LEN*2]; 1521+ int rows, j, sum_type[2]; 1522+ int64 dbsize[2], dbmtime[2], dbctime[2]; 1523+ unsigned int disk_id = get_disk_id(st_p->st_dev); 1524+ 1525+ if (disk_id == 0) 1526+ return 0; 1527+ 1528+ switch (use_db) { 1529+#ifdef USE_MYSQL 1530+ case DB_TYPE_MYSQL: { 1531+ MYSQL_BIND binds[5]; 1532+ 1533+ bind_disk_id = disk_id; 1534+ bind_ino = st_p->st_ino; 1535+ 1536+ binds[0].buffer_type = MYSQL_TYPE_BLOB; 1537+ binds[0].buffer = dbsum; 1538+ binds[0].buffer_length = MD5_DIGEST_LEN; 1539+ binds[1].buffer_type = MYSQL_TYPE_LONG; 1540+ binds[1].buffer = (char*)sum_type; 1541+ binds[2].buffer_type = MYSQL_TYPE_LONGLONG; 1542+ binds[2].buffer = (char*)dbsize; 1543+ binds[3].buffer_type = MYSQL_TYPE_LONGLONG; 1544+ binds[3].buffer = (char*)dbmtime; 1545+ binds[4].buffer_type = MYSQL_TYPE_LONGLONG; 1546+ binds[4].buffer = (char*)dbctime; 1547+ rows = fetch_mysql(binds, 5, SEL_SUM, 2, NULL); 1548+ break; 1549+ } 1550+#endif 1551+#ifdef USE_SQLITE 1552+ case DB_TYPE_SQLITE: { 1553+ sqlite3_stmt *stmt = statements[SEL_SUM].sqlite; 1554+ sqlite3_bind_int(stmt, 1, disk_id); 1555+ sqlite3_bind_int64(stmt, 2, st_p->st_ino); 1556+ for (j = 0; j < 2; j++) { 1557+ int len; 1558+ if (sqlite3_step(stmt) != SQLITE_ROW) 1559+ break; 1560+ len = sqlite3_column_bytes(stmt, 0); 1561+ if (len > MD5_DIGEST_LEN) 1562+ len = MD5_DIGEST_LEN; 1563+ memcpy(dbsum + MD5_DIGEST_LEN*j, sqlite3_column_blob(stmt, 0), len); 1564+ sum_type[j] = sqlite3_column_int(stmt, 1); 1565+ dbsize[j] = sqlite3_column_int(stmt, 2); 1566+ dbmtime[j] = sqlite3_column_int64(stmt, 3); 1567+ dbctime[j] = sqlite3_column_int64(stmt, 4); 1568+ } 1569+ sqlite3_reset(stmt); 1570+ rows = j; 1571+ break; 1572+ } 1573+#endif 1574+ default: 1575+ return 0; 1576+ } 1577+ 1578+ if (sum4) 1579+ *sum4 = NULL; 1580+ if (sum5) 1581+ *sum5 = NULL; 1582+ *right_sum_cnt = *wrong_sum_cnt = 0; 1583+ for (j = 0; j < rows; j++) { 1584+ if (DEBUG_GTE(DB, 3)) { 1585+ rprintf(FCLIENT, "DB checksum for %s,%s,%d: %s\n", 1586+ big_num(st_p->st_dev), big_num(st_p->st_ino), sum_type[j], 1587+ sum_as_hex(sum_type[j], dbsum + MD5_DIGEST_LEN*j, 0)); 1588+ } 1589+ 1590+ if (sum_type[j] == 4) { 1591+ if (!sum4) 1592+ continue; 1593+ *sum4 = dbsum + MD5_DIGEST_LEN*j; 1594+ } else { 1595+ if (!sum5) 1596+ continue; 1597+ *sum5 = dbsum + MD5_DIGEST_LEN*j; 1598+ } 1599+ if (st_p->st_size == dbsize[j] && st_p->st_mtime == dbmtime[j] && (db_lax || st_p->st_ctime == dbctime[j])) 1600+ ++*right_sum_cnt; 1601+ else 1602+ ++*wrong_sum_cnt; 1603+ } 1604+ 1605+ return rows; 1606+} 1607+ 1608+int db_set_checksum(int mdnum, const STRUCT_STAT *st_p, const char *sum) 1609+{ 1610+ unsigned int disk_id; 1611+ const char *errmsg = NULL; 1612+ int rc = 0; 1613+ 1614+ if (am_receiver || (am_generator && same_db)) { 1615+ /* Forward the setting to a single process. The receiver always 1616+ * forwards to the generator, and the generator will forward to 1617+ * the sender ONLY if this is a local transfer. */ 1618+ char data[MSG_CHECKSUM_LEN]; 1619+ SIVAL64(data, 0, st_p->st_dev); 1620+ SIVAL64(data, 8, st_p->st_ino); 1621+ SIVAL64(data, 16, st_p->st_size); 1622+ SIVAL64(data, 24, st_p->st_mtime); 1623+ SIVAL64(data, 32, st_p->st_ctime); 1624+#if MSG_CHECKSUM_LONGS != 5 1625+#error Fix the setting of checksum long values 1626+#endif 1627+ SIVAL(data, MSG_CHECKSUM_LONGS*8, mdnum); 1628+ memcpy(data + MSG_CHECKSUM_LONGS*8 + 4, sum, MAX_DIGEST_LEN); 1629+ return send_msg(MSG_CHECKSUM, data, sizeof data, 0); 1630+ } 1631+ 1632+ if ((disk_id = get_disk_id(st_p->st_dev)) == 0) 1633+ return 0; 1634+ 1635+ switch (use_db) { 1636+#ifdef USE_MYSQL 1637+ case DB_TYPE_MYSQL: 1638+ if (transaction_state == 0) { 1639+ if (!run_sql("BEGIN")) 1640+ return 0; 1641+ transaction_state = 1; 1642+ } 1643+ 1644+ bind_disk_id = disk_id; 1645+ bind_ino = st_p->st_ino; 1646+ bind_mdnum = mdnum; 1647+ bind_size = st_p->st_size; 1648+ bind_mtime = st_p->st_mtime; 1649+ bind_ctime = st_p->st_ctime; 1650+ memcpy(bind_sum, sum, MD5_DIGEST_LEN); 1651+ if (exec_mysql(REP_SUM) == NULL) 1652+ errmsg = mysql_error(dbh.mysql); 1653+ break; 1654+#endif 1655+#ifdef USE_SQLITE 1656+ case DB_TYPE_SQLITE: { 1657+ sqlite3_stmt *stmt = statements[REP_SUM].sqlite; 1658+ int lock_failures = 0; 1659+ 1660+ if (transaction_state == 0) { 1661+ if (!run_sql("BEGIN")) 1662+ return 0; 1663+ transaction_state = 1; 1664+ } 1665+ 1666+ sqlite3_bind_int(stmt, 1, disk_id); 1667+ sqlite3_bind_int64(stmt, 2, st_p->st_ino); 1668+ sqlite3_bind_int(stmt, 3, mdnum); 1669+ sqlite3_bind_int64(stmt, 4, st_p->st_size); 1670+ sqlite3_bind_int64(stmt, 5, st_p->st_mtime); 1671+ sqlite3_bind_int64(stmt, 6, st_p->st_ctime); 1672+ sqlite3_bind_blob(stmt, 7, sum, MD5_DIGEST_LEN, SQLITE_TRANSIENT); 1673+ while (1) { 1674+ rc = sqlite3_step(stmt); 1675+ if (rc != SQLITE_BUSY && rc != SQLITE_LOCKED) 1676+ break; 1677+ if (++lock_failures > MAX_LOCK_FAILURES) 1678+ break; 1679+ sqlite3_reset(stmt); 1680+ msleep(LOCK_FAIL_MSLEEP); 1681+ } 1682+ if (rc != SQLITE_DONE) 1683+ errmsg = sqlite3_errmsg(dbh.sqlite); 1684+ sqlite3_reset(stmt); 1685+ break; 1686+ } 1687+#endif 1688+ } 1689+ 1690+ if (!errmsg) { 1691+ if (DEBUG_GTE(DB, 2)) { 1692+ rprintf(FCLIENT, "[%s] Set DB checksum for %s,%s,%d: %s\n", 1693+ who_am_i(), big_num(st_p->st_dev), big_num(st_p->st_ino), 1694+ md_num, sum_as_hex(md_num, sum, 0)); 1695+ } 1696+ } else { 1697+ rprintf(log_code, "[%s] Failed to set checksum for %s,%s,%d: %s (%d) -- closing DB\n", 1698+ who_am_i(), big_num(st_p->st_dev), big_num(st_p->st_ino), 1699+ md_num, errmsg, rc); 1700+ db_disconnect(False); 1701+ } 1702+ 1703+ return errmsg ? 0 : 1; 1704+} 1705+ 1706+/* For a delayed-update copy, we set the checksum on the file when it was 1707+ * inside the partial-dir. Since renaming the file changes its ctime, we need 1708+ * to update the ctime to its new value (we can skip this in db_lax mode). */ 1709+int db_update_ctime(UNUSED(int mdnum), const STRUCT_STAT *st_p) 1710+{ 1711+ unsigned int disk_id = get_disk_id(st_p->st_dev); 1712+ 1713+ if (disk_id == 0) 1714+ return 0; 1715+ 1716+ switch (use_db) { 1717+#ifdef USE_MYSQL 1718+ case DB_TYPE_MYSQL: 1719+ bind_ctime = st_p->st_ctime; 1720+ bind_disk_id = disk_id; 1721+ bind_ino = st_p->st_ino; 1722+ bind_mdnum = mdnum; 1723+ bind_size = st_p->st_size; 1724+ bind_mtime = st_p->st_mtime; 1725+ return exec_mysql(UPD_CTIME) != NULL; 1726+#endif 1727+#ifdef USE_SQLITE 1728+ case DB_TYPE_SQLITE: { 1729+ int rc; 1730+ 1731+ sqlite3_stmt *stmt = statements[UPD_CTIME].sqlite; 1732+ if (stmt == NULL) 1733+ return 0; 1734+ sqlite3_bind_int64(stmt, 1, st_p->st_ctime); 1735+ sqlite3_bind_int(stmt, 2, disk_id); 1736+ sqlite3_bind_int64(stmt, 3, st_p->st_ino); 1737+ sqlite3_bind_int(stmt, 4, mdnum); 1738+ sqlite3_bind_int64(stmt, 5, st_p->st_size); 1739+ sqlite3_bind_int64(stmt, 6, st_p->st_mtime); 1740+ rc = sqlite3_step(stmt); 1741+ sqlite3_reset(stmt); 1742+ return rc == SQLITE_DONE; 1743+ } 1744+#endif 1745+ } 1746+ 1747+ return 0; 1748+} 1749+ 1750+static int db_clean_init(void) 1751+{ 1752+ switch (use_db) { 1753+#ifdef USE_MYSQL 1754+ case DB_TYPE_MYSQL: { 1755+ MYSQL_BIND binds[MAX_BIND_CNT]; 1756+ char *sql; 1757+ 1758+ mysql_query(dbh.mysql, 1759+ "CREATE TEMPORARY TABLE inode_present (" 1760+ " disk_id integer unsigned NOT NULL," 1761+ " ino bigint unsigned NOT NULL," 1762+ " PRIMARY KEY (disk_id,ino)" 1763+ ") ENGINE=MEMORY" 1764+ ); 1765+ 1766+ sql="INSERT IGNORE INTO inode_present" 1767+ " SET disk_id = ?, ino = ?"; 1768+ memset(binds, 0, sizeof binds); 1769+ binds[0].buffer_type = MYSQL_TYPE_LONG; 1770+ binds[0].buffer = &bind_disk_id; 1771+ binds[1].buffer_type = MYSQL_TYPE_LONGLONG; 1772+ binds[1].buffer = &bind_ino; 1773+ if (!prepare_mysql(INS_PRESENT, binds, 2, sql)) 1774+ exit_cleanup(RERR_SYNTAX); 1775+ 1776+ sql="DELETE m.*" 1777+ " FROM inode_map AS m" 1778+ " LEFT JOIN inode_present AS p USING(disk_id, ino)" 1779+ " JOIN disk AS d ON(m.disk_id = d.disk_id)" 1780+ " WHERE host = ? AND devno != 0 AND p.disk_id IS NULL AND ctime < ?"; 1781+ memset(binds, 0, sizeof binds); 1782+ binds[0].buffer_type = MYSQL_TYPE_STRING; 1783+ binds[0].buffer = &bind_thishost; 1784+ binds[0].buffer_length = bind_thishost_len; 1785+ binds[1].buffer_type = MYSQL_TYPE_LONGLONG; 1786+ binds[1].buffer = &bind_ctime; 1787+ if (!prepare_mysql(DEL_SUMS, binds, 2, sql)) 1788+ exit_cleanup(RERR_SYNTAX); 1789+ 1790+ return 1; 1791+ } 1792+#endif 1793+#ifdef USE_SQLITE 1794+ case DB_TYPE_SQLITE: { 1795+ char *sql; 1796+ sql="ATTACH DATABASE '' AS aux1;"; /* Private temp DB, probably in-memory */ 1797+ if (!run_sql(sql)) 1798+ exit_cleanup(RERR_IPC); 1799+ 1800+ sql="CREATE TABLE aux1.inode_present (" 1801+ " disk_id integer NOT NULL," 1802+ " ino bigint NOT NULL," 1803+ " PRIMARY KEY (disk_id,ino)" 1804+ ")"; 1805+ if (!run_sql(sql)) 1806+ exit_cleanup(RERR_IPC); 1807+ 1808+ sql="INSERT OR IGNORE INTO aux1.inode_present" 1809+ " (disk_id, ino)" 1810+ " VALUES (?, ?)"; 1811+ if (!prepare_sqlite(INS_PRESENT, sql)) 1812+ exit_cleanup(RERR_IPC); 1813+ 1814+ sql="DELETE FROM inode_map" 1815+ " WHERE ROWID IN (" 1816+ " SELECT m.ROWID" 1817+ " FROM inode_map AS m" 1818+ " LEFT JOIN aux1.inode_present AS p USING(disk_id, ino)" 1819+ " JOIN disk AS d ON(m.disk_id = d.disk_id)" 1820+ " WHERE host = ? AND devno != 0 AND p.disk_id IS NULL AND ctime < ?" 1821+ " )"; 1822+ if (!prepare_sqlite(DEL_SUMS, sql)) 1823+ exit_cleanup(RERR_IPC); 1824+ 1825+ transaction_state = -1; /* bug work-around -- force transaction off when cleaning XXX */ 1826+ 1827+ return 1; 1828+ } 1829+#endif 1830+ } 1831+ 1832+ return 0; 1833+} 1834+ 1835+static int db_note_present(UNUSED(int disk_id), UNUSED(int64 ino)) 1836+{ 1837+ switch (use_db) { 1838+#ifdef USE_MYSQL 1839+ case DB_TYPE_MYSQL: 1840+ bind_disk_id = disk_id; 1841+ bind_ino = ino; 1842+ return exec_mysql(INS_PRESENT) != NULL; 1843+#endif 1844+#ifdef USE_SQLITE 1845+ case DB_TYPE_SQLITE: { 1846+ int rc; 1847+ sqlite3_stmt *stmt = statements[INS_PRESENT].sqlite; 1848+ sqlite3_bind_int(stmt, 1, disk_id); 1849+ sqlite3_bind_int64(stmt, 2, ino); 1850+ rc = sqlite3_step(stmt); 1851+ sqlite3_reset(stmt); 1852+ return rc == SQLITE_DONE; 1853+ } 1854+#endif 1855+ } 1856+ 1857+ return 0; 1858+} 1859+ 1860+/* This function requires the user to have populated all disk_id+inode pairs 1861+ * into the inode_present table. */ 1862+static int db_clean_inodes(UNUSED(time_t start_time)) 1863+{ 1864+ int del_cnt = 0; 1865+ 1866+ /* The extra ctime < start_time check ensures that brand-new checksums that 1867+ * were added after the start of our cleaning run are not removed. */ 1868+ switch (use_db) { 1869+#ifdef USE_MYSQL 1870+ case DB_TYPE_MYSQL: { 1871+ MYSQL_STMT *stmt; 1872+ bind_ctime = start_time; 1873+ stmt = exec_mysql(DEL_SUMS); 1874+ if (stmt != NULL) 1875+ del_cnt = mysql_affected_rows(dbh.mysql); 1876+ break; 1877+ } 1878+#endif 1879+#ifdef USE_SQLITE 1880+ case DB_TYPE_SQLITE: { 1881+ int rc; 1882+ sqlite3_stmt *stmt = statements[DEL_SUMS].sqlite; 1883+ sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC); 1884+ sqlite3_bind_int64(stmt, 2, start_time); 1885+ rc = sqlite3_step(stmt); 1886+ if (rc == SQLITE_DONE) 1887+ del_cnt = sqlite3_changes(dbh.sqlite); 1888+ sqlite3_reset(stmt); 1889+ break; 1890+ } 1891+#endif 1892+ } 1893+ 1894+ return del_cnt; 1895+} 1896+ 1897+static int abs_path(char *buf, int bufsiz, const char *curdir, const char *dir) 1898+{ 1899+ if (*dir == '/') 1900+ strlcpy(buf, dir, bufsiz); 1901+ else { 1902+ int len = snprintf(buf, bufsiz, "%s/%s", curdir, dir); 1903+ assert(len > 0); /* silence a compiler warning */ 1904+ } 1905+ 1906+ return clean_fname(buf, CFN_DROP_TRAILING_DOT_DIR | CFN_COLLAPSE_DOT_DOT_DIRS); 1907+} 1908+ 1909+static struct name_list *new_name(const char *basename, const char *filename) 1910+{ 1911+ struct name_list *n; 1912+ int blen = strlen(basename); 1913+ int slen = filename ? (int)strlen(filename) : -1; 1914+ int len = blen + 1 + slen; 1915+ 1916+ if (len >= MAXPATHLEN) { 1917+ if (filename) 1918+ rprintf(FERROR, "Filename too long: %s/%s\n", basename, filename); 1919+ else 1920+ rprintf(FERROR, "Filename too long: %s\n", basename); 1921+ return NULL; 1922+ } 1923+ 1924+ n = (struct name_list *)new_array(char, sizeof (struct name_list) + len); 1925+ 1926+ memcpy(n->name, basename, blen); 1927+ if (filename) { 1928+ n->name[blen] = '/'; 1929+ memcpy(n->name + 1 + blen, filename, slen); 1930+ } 1931+ n->name[len] = '\0'; 1932+ n->next = NULL; 1933+ 1934+ return n; 1935+} 1936+ 1937+static int name_compare(const void *n1, const void *n2) 1938+{ 1939+ struct name_list *p1 = *(struct name_list **)n1; 1940+ struct name_list *p2 = *(struct name_list **)n2; 1941+ return strcmp(p1->name, p2->name); 1942+} 1943+ 1944+static struct name_list *get_sorted_names(const char *dir) 1945+{ 1946+ struct name_list *add, **sortbuf, *names = NULL, *prior_name = NULL; 1947+ struct dirent *di; 1948+ int cnt = 0; 1949+ DIR *d; 1950+ 1951+ if (!(d = opendir("."))) { 1952+ rprintf(FERROR, "Unable to opendir %s: %s\n", dir, strerror(errno)); 1953+ return NULL; 1954+ } 1955+ while ((di = readdir(d)) != NULL) { 1956+ char *dname = d_name(di); 1957+ if (dname[0] == '.' && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) 1958+ continue; 1959+ if (!(add = new_name(dname, NULL))) 1960+ continue; 1961+ if (prior_name) 1962+ prior_name->next = add; 1963+ else 1964+ names = add; 1965+ prior_name = add; 1966+ cnt++; 1967+ } 1968+ closedir(d); 1969+ 1970+ if (cnt) { 1971+ int j; 1972+ 1973+ sortbuf = new_array(struct name_list *, cnt); 1974+ for (j = 0; j < cnt; j++) { 1975+ sortbuf[j] = names; 1976+ names = names->next; 1977+ } 1978+ 1979+ qsort(sortbuf, cnt, PTR_SIZE, name_compare); 1980+ 1981+ names = prior_name = NULL; 1982+ for (j = 0; j < cnt; j++) { 1983+ add = sortbuf[j]; 1984+ if (prior_name) 1985+ prior_name->next = add; 1986+ else 1987+ names = add; 1988+ prior_name = add; 1989+ } 1990+ 1991+ if (prior_name) 1992+ prior_name->next = NULL; 1993+ free(sortbuf); 1994+ } 1995+ 1996+ return names; 1997+} 1998+ 1999+static inline int sums_ne(const char *sum1, const char *sum2) 2000+{ 2001+ return memcmp(sum1, sum2, MD5_DIGEST_LEN) != 0; 2002+} 2003+ 2004+/* Returns 1 if there is a checksum change, else 0. */ 2005+static int mention_file(const char *dir, const char *name, int right_cnt, int wrong_cnt, 2006+ const char *dbsum4, const char *dbsum5, const char *sum4, const char *sum5) 2007+{ 2008+ char *info_str = wrong_cnt && !right_cnt ? "!i " : " "; 2009+ char *md4_str = !db_do_md4 ? NULL : !dbsum4 ? "+4 " : !sum4 ? "?4 " : sums_ne(sum4, dbsum4) ? "!4 " : " "; 2010+ char *md5_str = !db_do_md5 ? NULL : !dbsum5 ? "+5 " : !sum5 ? "?5 " : sums_ne(sum5, dbsum5) ? "!5 " : " "; 2011+ int chg = *info_str != ' ' || (md4_str && *md4_str != ' ') || (md5_str && *md5_str != ' '); 2012+ if (chg || db_output_unchanged) { 2013+ if (db_output_info) { 2014+ fputs(info_str, stdout); 2015+ if (md4_str) 2016+ fputs(md4_str, stdout); 2017+ if (md5_str) 2018+ fputs(md5_str, stdout); 2019+ } 2020+ if (db_output_sum) { 2021+ if (db_do_md4) 2022+ printf("%s ", sum_as_hex(4, sum4, 0)); 2023+ if (db_do_md5) 2024+ printf("%s ", sum_as_hex(5, sum5, 0)); 2025+ } 2026+ if (db_output_name) { 2027+ if (db_output_sum) 2028+ putchar(' '); /* We want 2 spaces, like md5sum. */ 2029+ if (*dir != '.' || dir[1]) { 2030+ fputs(dir, stdout); 2031+ putchar('/'); 2032+ } 2033+ puts(name); 2034+ } 2035+ } 2036+ 2037+ return chg; 2038+} 2039+ 2040+NORETURN void run_dbonly(const char **args) 2041+{ 2042+ char start_dir[MAXPATHLEN], dirbuf[MAXPATHLEN]; 2043+ int need_sum_cnt, start_dir_len; 2044+ struct name_list *prior_dir; 2045+ struct name_list *names; 2046+ time_t clean_start = 0; 2047+ int exit_code = 0; 2048+ 2049+ checksum_type = 5; 2050+ 2051+ need_sum_cnt = db_do_md4 + db_do_md5; 2052+ 2053+ if (!db_read_config(FERROR, db_config) || !db_connect(1)) 2054+ exit_cleanup(RERR_FILEIO); 2055+ 2056+ if (db_clean) { 2057+ clean_start = time(NULL); 2058+ db_clean_init(); 2059+ } 2060+ 2061+ if (getcwd(start_dir, sizeof start_dir - 1) == NULL) { 2062+ rsyserr(FERROR, errno, "getcwd()"); 2063+ exit_cleanup(RERR_FILESELECT); 2064+ } 2065+ start_dir_len = strlen(start_dir); 2066+ 2067+ if (args) { 2068+ prior_dir = NULL; 2069+ while (*args) { 2070+ struct name_list *add; 2071+ if (abs_path(dirbuf, sizeof dirbuf, start_dir, *args++) <= 0) 2072+ continue; 2073+ if (!(add = new_name(dirbuf, NULL))) 2074+ continue; 2075+ if (prior_dir) 2076+ prior_dir->next = add; 2077+ else 2078+ dirs_list = add; 2079+ prior_dir = add; 2080+ } 2081+ } else 2082+ dirs_list = new_name(start_dir, NULL); 2083+ 2084+ prior_dir = NULL; 2085+ while (dirs_list) { 2086+ struct name_list *subdirs, *prior_subdir, *prior_name; 2087+ const char *dir = dirs_list->name; 2088+ const char *reldir = dir; 2089+ 2090+ if (prior_dir) 2091+ free((void*)prior_dir); 2092+ prior_dir = dirs_list; 2093+ dirs_list = dirs_list->next; 2094+ 2095+ if (strncmp(reldir, start_dir, start_dir_len) == 0) { 2096+ if (reldir[start_dir_len] == '\0') 2097+ reldir = "."; 2098+ else if (reldir[start_dir_len] == '/') 2099+ reldir += start_dir_len + 1; 2100+ } 2101+ if (db_output_dirs) 2102+ printf("... %s/ ...\n", reldir); 2103+ 2104+ if (chdir(dir) < 0) { 2105+ rprintf(FERROR, "Unable to chdir to %s: %s\n", dir, strerror(errno)); 2106+ continue; 2107+ } 2108+ if (!(names = get_sorted_names(dir))) 2109+ continue; 2110+ 2111+ subdirs = prior_subdir = prior_name = NULL; 2112+ while (names) { 2113+ STRUCT_STAT st; 2114+ char *dbsum4, *sum4, sumbuf4[MD5_DIGEST_LEN]; 2115+ char *dbsum5, *sum5, sumbuf5[MD5_DIGEST_LEN]; 2116+ int right_sum_cnt, wrong_sum_cnt; 2117+ const char *name = names->name; 2118+ unsigned int disk_id; 2119+ 2120+ if (prior_name) 2121+ free((void*)prior_name); 2122+ prior_name = names; 2123+ names = names->next; 2124+ 2125+ dbsum4 = dbsum5 = sum4 = sum5 = NULL; 2126+ 2127+ if (lstat(name, &st) < 0) { 2128+ rprintf(FERROR, "Failed to lstat(%s): %s\n", name, strerror(errno)); 2129+ continue; 2130+ } 2131+ if (S_ISLNK(st.st_mode)) 2132+ continue; 2133+ if (S_ISDIR(st.st_mode)) { 2134+ /* add optional excluding of things like /^(CVS|\.svn|\.git|\.bzr)$/; */ 2135+ if (recurse) { 2136+ struct name_list *add = new_name(dir, name); 2137+ if (add) { 2138+ if (prior_subdir) 2139+ prior_subdir->next = add; 2140+ else 2141+ subdirs = add; 2142+ prior_subdir = add; 2143+ } 2144+ } 2145+ continue; 2146+ } 2147+ if (!S_ISREG(st.st_mode)) 2148+ continue; 2149+ 2150+ if (!(disk_id = get_disk_id(st.st_dev))) 2151+ continue; 2152+ if (db_clean) { 2153+ db_note_present(disk_id, st.st_ino); 2154+ if (!db_update && !db_check) 2155+ continue; 2156+ } 2157+ db_get_both_checksums(&st, &right_sum_cnt, &wrong_sum_cnt, 2158+ db_do_md4 ? &dbsum4 : NULL, db_do_md5 ? &dbsum5 : NULL); 2159+ 2160+ if (!db_check && right_sum_cnt == need_sum_cnt) { 2161+ mention_file(reldir, name, right_sum_cnt, wrong_sum_cnt, dbsum4, dbsum5, dbsum4, dbsum5); 2162+ continue; 2163+ } 2164+ 2165+ if (db_update || (db_check && right_sum_cnt) || db_output_sum) { 2166+ uchar *data; 2167+ int32 remainder; 2168+ md_context m4; 2169+ MD5_CTX m5; 2170+ struct map_struct *buf; 2171+ OFF_T off, len = st.st_size; 2172+ int fd = do_open(name, O_RDONLY, 0); 2173+ 2174+ if (fd < 0) { 2175+ rprintf(FERROR, "ERROR: unable to read %s: %s\n", name, strerror(errno)); 2176+ continue; 2177+ } 2178+ 2179+ if (db_do_md4) 2180+ mdfour_begin(&m4); 2181+ if (db_do_md5) 2182+ MD5_Init(&m5); 2183+ 2184+ buf = map_file(fd, len, MAX_MAP_SIZE, CSUM_CHUNK); 2185+ 2186+ for (off = 0; off + CSUM_CHUNK <= len; off += CSUM_CHUNK) { 2187+ data = (uchar*)map_ptr(buf, off, CSUM_CHUNK); 2188+ if (db_do_md4) 2189+ mdfour_update(&m4, data, CSUM_CHUNK); 2190+ if (db_do_md5) 2191+ MD5_Update(&m5, data, CSUM_CHUNK); 2192+ } 2193+ 2194+ remainder = (int32)(len - off); 2195+ data = (uchar*)map_ptr(buf, off, remainder); 2196+ if (db_do_md4) { 2197+ mdfour_update(&m4, data, remainder); 2198+ mdfour_result(&m4, (uchar*)(sum4 = sumbuf4)); 2199+ } 2200+ if (db_do_md5) { 2201+ MD5_Update(&m5, data, remainder); 2202+ MD5_Final((uchar*)(sum5 = sumbuf5), &m5); 2203+ } 2204+ 2205+ close(fd); 2206+ unmap_file(buf); 2207+ } 2208+ 2209+ int chg = mention_file(reldir, name, right_sum_cnt, wrong_sum_cnt, dbsum4, dbsum5, sum4, sum5); 2210+ if (!chg) { 2211+ /* Only db_check should get here... */ 2212+ } else if (!db_update) { 2213+ exit_code = 1; 2214+ } else { 2215+ int fail = 0; 2216+ if (db_do_md4 && !db_set_checksum(4, &st, sum4)) 2217+ fail = 1; 2218+ if (db_do_md5 && !db_set_checksum(5, &st, sum5)) 2219+ fail = 1; 2220+ if (fail) { 2221+ fprintf(stderr, "Failed to set checksum on %s/%s\n", reldir, name); 2222+ exit_cleanup(RERR_FILEIO); 2223+ } 2224+ } 2225+ } 2226+ if (prior_name) 2227+ free((void*)prior_name); 2228+ 2229+ if (recurse && subdirs) { 2230+ prior_subdir->next = dirs_list; 2231+ dirs_list = subdirs; 2232+ } 2233+ } 2234+ if (prior_dir) 2235+ free((void*)prior_dir); 2236+ 2237+ if (db_clean) { 2238+ int rows = db_clean_inodes(clean_start); 2239+ if (db_output_msgs) 2240+ printf("Cleaned out %d old inode%s.\n", rows, rows == 1 ? "" : "s"); 2241+ } 2242+ 2243+ db_disconnect(True); 2244+ exit(exit_code); 2245+} 2246diff --git a/flist.c b/flist.c 2247--- a/flist.c 2248+++ b/flist.c 2249@@ -54,6 +54,7 @@ extern int preserve_devices; 2250 extern int preserve_specials; 2251 extern int delete_during; 2252 extern int missing_args; 2253+extern int use_db; 2254 extern int eol_nulls; 2255 extern int atimes_ndx; 2256 extern int crtimes_ndx; 2257@@ -1367,11 +1368,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, 2258 extra_len += EXTRA_LEN; 2259 #endif 2260 2261- if (always_checksum && am_sender && S_ISREG(st.st_mode)) { 2262- file_checksum(thisname, &st, tmp_sum); 2263- if (sender_keeps_checksum) 2264- extra_len += SUM_EXTRA_CNT * EXTRA_LEN; 2265- } 2266+ if (sender_keeps_checksum && S_ISREG(st.st_mode)) 2267+ extra_len += SUM_EXTRA_CNT * EXTRA_LEN; 2268 2269 #if EXTRA_ROUNDING > 0 2270 if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN)) 2271@@ -1460,8 +1458,12 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, 2272 return NULL; 2273 } 2274 2275- if (sender_keeps_checksum && S_ISREG(st.st_mode)) 2276- memcpy(F_SUM(file), tmp_sum, flist_csum_len); 2277+ if (always_checksum && am_sender && S_ISREG(st.st_mode)) { 2278+ if (!use_db || !db_get_checksum(&st, tmp_sum)) 2279+ file_checksum(thisname, &st, tmp_sum); 2280+ if (sender_keeps_checksum) 2281+ memcpy(F_SUM(file), tmp_sum, flist_csum_len); 2282+ } 2283 2284 if (unsort_ndx) 2285 F_NDX(file) = stats.num_dirs; 2286@@ -2145,6 +2147,9 @@ void send_extra_file_list(int f, int at_least) 2287 finish: 2288 if (io_error != save_io_error && protocol_version == 30 && !ignore_errors) 2289 send_msg_int(MSG_IO_ERROR, io_error); 2290+ 2291+ if (use_db && flist_eof) 2292+ db_disconnect(True); 2293 } 2294 2295 struct file_list *send_file_list(int f, int argc, char *argv[]) 2296@@ -2168,6 +2173,13 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) 2297 | (eol_nulls || reading_remotely ? RL_EOL_NULLS : 0); 2298 int implied_dot_dir = 0; 2299 2300+ if (use_db) { 2301+ if (always_checksum) 2302+ db_connect(0); /* Will reset use_db on error. */ 2303+ else 2304+ use_db = 0; 2305+ } 2306+ 2307 rprintf(FLOG, "building file list\n"); 2308 if (show_filelist_progress) 2309 start_filelist_progress("building file list"); 2310@@ -2511,6 +2523,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) 2311 rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i()); 2312 } 2313 2314+ if (use_db && (!inc_recurse || flist_eof)) 2315+ db_disconnect(True); 2316+ 2317 return flist; 2318 } 2319 2320diff --git a/generator.c b/generator.c 2321--- a/generator.c 2322+++ b/generator.c 2323@@ -61,6 +61,7 @@ extern int ignore_non_existing; 2324 extern int want_xattr_optim; 2325 extern int modify_window; 2326 extern int inplace; 2327+extern int use_db; 2328 extern int append_mode; 2329 extern int make_backups; 2330 extern int csum_length; 2331@@ -610,7 +611,8 @@ int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st) 2332 of the file time to determine whether to sync */ 2333 if (always_checksum > 0 && S_ISREG(st->st_mode)) { 2334 char sum[MAX_DIGEST_LEN]; 2335- file_checksum(fn, st, sum); 2336+ if (!use_db || !db_get_checksum(st, sum)) 2337+ file_checksum(fn, st, sum); 2338 return memcmp(sum, F_SUM(file), flist_csum_len) == 0; 2339 } 2340 2341@@ -2268,6 +2270,13 @@ void generate_files(int f_out, const char *local_name) 2342 : "enabled"); 2343 } 2344 2345+ if (use_db) { 2346+ if (always_checksum || (append_mode != 1 && protocol_version >= 30)) 2347+ db_connect(0); /* Will reset use_db on error. */ 2348+ else 2349+ use_db = 0; 2350+ } 2351+ 2352 dflt_perms = (ACCESSPERMS & ~orig_umask); 2353 2354 do { 2355@@ -2393,6 +2402,9 @@ void generate_files(int f_out, const char *local_name) 2356 wait_for_receiver(); 2357 } 2358 2359+ if (use_db) 2360+ db_disconnect(True); 2361+ 2362 info_levels[INFO_FLIST] = save_info_flist; 2363 info_levels[INFO_PROGRESS] = save_info_progress; 2364 2365diff --git a/io.c b/io.c 2366--- a/io.c 2367+++ b/io.c 2368@@ -41,8 +41,10 @@ extern int am_server; 2369 extern int am_sender; 2370 extern int am_receiver; 2371 extern int am_generator; 2372+extern int local_server; 2373 extern int msgs2stderr; 2374 extern int inc_recurse; 2375+extern int same_db; 2376 extern int io_error; 2377 extern int batch_fd; 2378 extern int eol_nulls; 2379@@ -1492,6 +1494,32 @@ static void read_a_msg(void) 2380 if (am_sender) 2381 maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); 2382 break; 2383+ case MSG_CHECKSUM: 2384+ /* This receives some checksum info that we want to make a note of 2385+ * (which allows a single process to do all the writing to the db). */ 2386+ if (msg_bytes != MSG_CHECKSUM_LEN) 2387+ goto overflow; 2388+ raw_read_buf(data, MSG_CHECKSUM_LEN); 2389+ if (am_generator && same_db) { 2390+ iobuf.in_multiplexed = 1; 2391+ send_msg(MSG_CHECKSUM, data, MSG_CHECKSUM_LEN, 0); 2392+ } if (am_receiver || (am_sender && !local_server)) 2393+ goto unexpected; 2394+ else { 2395+ /* The received data is a set of numbers followed by the checksum. */ 2396+ STRUCT_STAT st; 2397+ st.st_dev = IVAL64(data, 0); 2398+ st.st_ino = IVAL64(data, 8); 2399+ st.st_size = IVAL64(data, 16); 2400+ st.st_mtime = IVAL64(data, 24); 2401+ st.st_ctime = IVAL64(data, 32); 2402+#if MSG_CHECKSUM_LONGS != 5 2403+#error Fix the parsing of checksum long values 2404+#endif 2405+ iobuf.in_multiplexed = 1; 2406+ db_set_checksum(IVAL(data, MSG_CHECKSUM_LONGS*8), &st, data + MSG_CHECKSUM_LONGS*8 + 4); 2407+ } 2408+ break; 2409 case MSG_DELETED: 2410 if (msg_bytes >= sizeof data) 2411 goto overflow; 2412@@ -1643,6 +1671,7 @@ static void read_a_msg(void) 2413 * with a duplicate exit message. */ 2414 _exit_cleanup(val, __FILE__, 0 - __LINE__); 2415 default: 2416+ unexpected: 2417 rprintf(FERROR, "unexpected tag %d [%s%s]\n", 2418 tag, who_am_i(), inc_recurse ? "/inc" : ""); 2419 exit_cleanup(RERR_STREAMIO); 2420diff --git a/main.c b/main.c 2421--- a/main.c 2422+++ b/main.c 2423@@ -39,6 +39,7 @@ extern int am_root; 2424 extern int am_server; 2425 extern int am_sender; 2426 extern int am_daemon; 2427+extern int am_dbadmin; 2428 extern int inc_recurse; 2429 extern int blocking_io; 2430 extern int always_checksum; 2431@@ -57,6 +58,7 @@ extern int copy_unsafe_links; 2432 extern int keep_dirlinks; 2433 extern int preserve_hard_links; 2434 extern int protocol_version; 2435+extern int always_checksum; 2436 extern int mkpath_dest_arg; 2437 extern int file_total; 2438 extern int recurse; 2439@@ -93,6 +95,7 @@ extern char *logfile_format; 2440 extern char *filesfrom_host; 2441 extern char *partial_dir; 2442 extern char *rsync_path; 2443+extern char *db_config; 2444 extern char *shell_cmd; 2445 extern char *password_file; 2446 extern char *backup_dir; 2447@@ -1241,6 +1244,9 @@ void start_server(int f_in, int f_out, int argc, char *argv[]) 2448 if (am_daemon && io_timeout && protocol_version >= 31) 2449 send_msg_int(MSG_IO_TIMEOUT, io_timeout); 2450 2451+ if (db_config) 2452+ db_read_config(FERROR, db_config); 2453+ 2454 if (am_sender) { 2455 keep_dirlinks = 0; /* Must be disabled on the sender. */ 2456 if (need_messages_from_generator) 2457@@ -1531,6 +1537,9 @@ static int start_client(int argc, char *argv[]) 2458 else 2459 env_port = rsync_port; 2460 2461+ if (db_config) 2462+ db_read_config(FERROR, db_config); 2463+ 2464 if (daemon_connection < 0) 2465 return start_socket_client(shell_machine, remote_argc, remote_argv, argc, argv); 2466 2467diff --git a/options.c b/options.c 2468--- a/options.c 2469+++ b/options.c 2470@@ -83,6 +83,7 @@ int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */ 2471 int am_server = 0; 2472 int am_sender = 0; 2473 int am_starting_up = 1; 2474+int am_dbadmin = 0; 2475 int relative_paths = -1; 2476 int implied_dirs = 1; 2477 int missing_args = 0; /* 0 = FERROR_XFER, 1 = ignore, 2 = delete */ 2478@@ -96,6 +97,7 @@ int use_qsort = 0; 2479 char *files_from = NULL; 2480 int filesfrom_fd = -1; 2481 char *filesfrom_host = NULL; 2482+char *db_config = NULL; 2483 int eol_nulls = 0; 2484 int protect_args = -1; 2485 int human_readable = 1; 2486@@ -104,6 +106,9 @@ int mkpath_dest_arg = 0; 2487 int allow_inc_recurse = 1; 2488 int xfer_dirs = -1; 2489 int am_daemon = 0; 2490+int db_clean, db_check, db_do_md4, db_do_md5, db_update = 1, db_lax, db_init, db_mounts; 2491+int db_output_name, db_output_sum, db_output_info, db_output_unchanged, db_output_dirs, db_output_msgs; 2492+int saw_db_output_opt, saw_db_sum_opt; 2493 int connect_timeout = 0; 2494 int keep_partial = 0; 2495 int safe_symlinks = 0; 2496@@ -282,6 +287,7 @@ static struct output_struct debug_words[COUNT_DEBUG+1] = { 2497 DEBUG_WORD(CHDIR, W_CLI|W_SRV, "Debug when the current directory changes"), 2498 DEBUG_WORD(CONNECT, W_CLI, "Debug connection events (levels 1-2)"), 2499 DEBUG_WORD(CMD, W_CLI, "Debug commands+options that are issued (levels 1-2)"), 2500+ DEBUG_WORD(DB, W_SND|W_REC, "Debug DB operations (levels 1-5)"), 2501 DEBUG_WORD(DEL, W_REC, "Debug delete actions (levels 1-3)"), 2502 DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"), 2503 DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"), 2504@@ -573,6 +579,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, 2505 OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD, 2506 OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE, 2507 OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE, 2508+ OPT_NO_DB, OPT_DBONLY, 2509 OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, 2510 OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, 2511 OPT_STOP_AFTER, OPT_STOP_AT, 2512@@ -729,6 +736,10 @@ static struct poptOption long_options[] = { 2513 {"no-c", 0, POPT_ARG_VAL, &always_checksum, 0, 0, 0 }, 2514 {"checksum-choice", 0, POPT_ARG_STRING, &checksum_choice, 0, 0, 0 }, 2515 {"cc", 0, POPT_ARG_STRING, &checksum_choice, 0, 0, 0 }, 2516+ {"db", 0, POPT_ARG_STRING, &db_config, 0, 0, 0 }, 2517+ {"no-db", 0, POPT_ARG_NONE, 0, OPT_NO_DB, 0, 0 }, 2518+ {"db-lax", 0, POPT_ARG_VAL, &db_lax, 1, 0, 0 }, 2519+ {"no-db-lax", 0, POPT_ARG_VAL, &db_lax, 0, 0, 0 }, 2520 {"block-size", 'B', POPT_ARG_STRING, 0, OPT_BLOCK_SIZE, 0, 0 }, 2521 {"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 }, 2522 {"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 }, 2523@@ -825,6 +836,9 @@ static struct poptOption long_options[] = { 2524 {"dparam", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 }, 2525 {"detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 }, 2526 {"no-detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 }, 2527+ /* All the following options switch us into DB-admin option-parsing. */ 2528+ {"db-help", 0, POPT_ARG_NONE, 0, OPT_DBONLY, 0, 0 }, 2529+ {"db-only", 0, POPT_ARG_STRING, 0, OPT_DBONLY, 0, 0 }, 2530 {0,0,0,0, 0, 0, 0} 2531 }; 2532 2533@@ -853,6 +867,31 @@ static struct poptOption long_daemon_options[] = { 2534 {0,0,0,0, 0, 0, 0} 2535 }; 2536 2537+static struct poptOption long_dbonly_options[] = { 2538+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ 2539+ {"check", 'c', POPT_ARG_NONE, &db_check, 0, 0, 0}, 2540+ {"clean", 0, POPT_ARG_NONE, &db_clean, 0, 0, 0}, 2541+ {"db", 0, POPT_ARG_STRING, &db_config, 0, 0, 0 }, 2542+ {"db-only", 0, POPT_ARG_STRING, &db_config, 0, 0, 0 }, 2543+ {"db-lax", 0, POPT_ARG_VAL, &db_lax, 1, 0, 0 }, 2544+ {"no-db-lax", 0, POPT_ARG_VAL, &db_lax, 0, 0, 0 }, 2545+ {"info", 0, POPT_ARG_STRING, 0, OPT_INFO, 0, 0 }, 2546+ {"debug", 0, POPT_ARG_STRING, 0, OPT_DEBUG, 0, 0 }, 2547+ {"update", 'u', POPT_ARG_VAL, &db_update, 1, 0, 0 }, 2548+ {"no-update", 'N', POPT_ARG_VAL, &db_update, 0, 0, 0 }, 2549+ {"no-u", 0, POPT_ARG_VAL, &db_update, 0, 0, 0 }, 2550+ {"output", 'o', POPT_ARG_STRING, 0, 'o', 0, 0 }, 2551+ {"recursive", 'r', POPT_ARG_VAL, &recurse, 1, 0, 0 }, 2552+ {"no-recursive", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 }, 2553+ {"no-r", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 }, 2554+ {"sums", 's', POPT_ARG_STRING, 0, 's', 0, 0 }, 2555+ {"init", 0, POPT_ARG_NONE, &db_init, 0, 0, 0 }, 2556+ {"mounts", 0, POPT_ARG_NONE, &db_mounts, 0, 0, 0 }, 2557+ {"quiet", 'q', POPT_ARG_NONE, &quiet, 0, 0, 0 }, 2558+ {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 }, 2559+ {"db-help", 0, POPT_ARG_NONE, 0, 'h', 0, 0 }, 2560+ {0,0,0,0, 0, 0, 0} 2561+}; 2562 2563 static char err_buf[200]; 2564 2565@@ -978,6 +1017,8 @@ static void set_refuse_options(void) 2566 parse_one_refuse_match(0, "iconv", list_end); 2567 #endif 2568 parse_one_refuse_match(0, "log-file*", list_end); 2569+ parse_one_refuse_match(0, "db", list_end); 2570+ parse_one_refuse_match(0, "db-lax", list_end); 2571 } 2572 2573 #ifndef SUPPORT_ATIMES 2574@@ -1285,6 +1326,102 @@ static void create_refuse_error(int which) 2575 snprintf(err_buf + n, sizeof err_buf - n, " (-%c)\n", op->shortName); 2576 } 2577 2578+static NORETURN void parse_dbonly_args(int argc, const char **argv) 2579+{ 2580+ poptContext pc = poptGetContext(RSYNC_NAME, argc, argv, long_dbonly_options, 0); 2581+ const char *arg; 2582+ int opt; 2583+ 2584+ recurse = 1; 2585+ am_dbadmin = 1; 2586+ 2587+ while ((opt = poptGetNextOpt(pc)) != -1) { 2588+ const char *cp; 2589+ switch (opt) { 2590+ case 'o': 2591+ for (cp = poptGetOptArg(pc); *cp; cp++) { 2592+ switch (toLower(cp)) { 2593+ case 'n': 2594+ db_output_name = 1; 2595+ break; 2596+ case 's': 2597+ case 'c': 2598+ db_output_sum = db_output_name = 1; 2599+ break; 2600+ case 'i': 2601+ db_output_info = db_output_name = 1; 2602+ break; 2603+ case 'u': 2604+ db_output_unchanged = db_output_name = 1; 2605+ break; 2606+ case 'd': 2607+ db_output_dirs = 1; 2608+ break; 2609+ } 2610+ } 2611+ saw_db_output_opt = 1; 2612+ break; 2613+ 2614+ case 's': 2615+ for (cp = poptGetOptArg(pc); *cp; cp++) { 2616+ switch (*cp) { 2617+ case '4': 2618+ db_do_md4 = 1; 2619+ break; 2620+ case '5': 2621+ db_do_md5 = 1; 2622+ break; 2623+ } 2624+ } 2625+ saw_db_sum_opt = 1; 2626+ break; 2627+ 2628+ case 'h': 2629+ dbonly_usage(FINFO); 2630+ exit_cleanup(0); 2631+ 2632+ case OPT_INFO: 2633+ arg = poptGetOptArg(pc); 2634+ parse_output_words(info_words, info_levels, arg, USER_PRIORITY); 2635+ break; 2636+ 2637+ case OPT_DEBUG: 2638+ arg = poptGetOptArg(pc); 2639+ parse_output_words(debug_words, debug_levels, arg, USER_PRIORITY); 2640+ break; 2641+ 2642+ default: 2643+ rprintf(FERROR, 2644+ "rsyncdb: %s: %s\n", 2645+ poptBadOption(pc, POPT_BADOPTION_NOALIAS), 2646+ poptStrerror(opt)); 2647+ goto dbonly_usage; 2648+ } 2649+ } 2650+ 2651+ if (!db_config) { 2652+ rprintf(FERROR, "You must specify the --db=FILE option.\n"); 2653+ dbonly_usage: 2654+ rprintf(FERROR, 2655+ "(Type \"rsyncdb --help\" for assistance.)\n"); 2656+ exit_cleanup(RERR_SYNTAX); 2657+ } 2658+ 2659+ if (!saw_db_output_opt && !quiet) { 2660+ db_output_dirs = db_output_name = 1; 2661+ if (db_check) 2662+ db_output_info = 1; 2663+ } 2664+ if (!quiet) 2665+ db_output_msgs = 1; 2666+ if (!saw_db_sum_opt) 2667+ db_do_md5 = 1; 2668+ 2669+ am_starting_up = 0; 2670+ run_dbonly(poptGetArgs(pc)); 2671+ exit(42); /* NOT REACHED */ 2672+} 2673+ 2674 /* This is used to make sure that --daemon & --server cannot be aliased to 2675 * something else. These options have always disabled popt aliases for the 2676 * parsing of a daemon or server command-line, but we have to make sure that 2677@@ -1341,6 +1478,12 @@ int parse_arguments(int *argc_p, const char ***argv_p) 2678 return 0; 2679 } 2680 2681+ arg = *argv + strlen(*argv); 2682+ if (arg - *argv > 2 && strcmp(arg-2, "db") == 0) { 2683+ parse_dbonly_args(argc, argv); 2684+ /* NOT REACHED */ 2685+ } 2686+ 2687 set_refuse_options(); 2688 2689 #ifdef ICONV_OPTION 2690@@ -1459,6 +1602,12 @@ int parse_arguments(int *argc_p, const char ***argv_p) 2691 am_daemon = 1; 2692 return 1; 2693 2694+ case OPT_DBONLY: 2695+ protect_args = 0; 2696+ poptFreeContext(pc); 2697+ parse_dbonly_args(argc, argv); 2698+ break; /* NOT REACHED */ 2699+ 2700 case OPT_MODIFY_WINDOW: 2701 /* The value has already been set by popt, but 2702 * we need to remember that we're using a 2703@@ -1531,6 +1680,10 @@ int parse_arguments(int *argc_p, const char ***argv_p) 2704 preserve_devices = preserve_specials = 0; 2705 break; 2706 2707+ case OPT_NO_DB: 2708+ db_config = NULL; 2709+ break; 2710+ 2711 case 'h': 2712 human_readable++; 2713 break; 2714diff --git a/pipe.c b/pipe.c 2715--- a/pipe.c 2716+++ b/pipe.c 2717@@ -27,11 +27,16 @@ extern int am_server; 2718 extern int blocking_io; 2719 extern int filesfrom_fd; 2720 extern int munge_symlinks; 2721+extern int always_checksum; 2722+extern int use_db; 2723+extern char *db_config; 2724 extern char *logfile_name; 2725 extern int remote_option_cnt; 2726 extern const char **remote_options; 2727 extern struct chmod_mode_struct *chmod_modes; 2728 2729+int same_db = 0; 2730+ 2731 /** 2732 * Create a child connected to us via its stdin/stdout. 2733 * 2734@@ -141,13 +146,22 @@ pid_t local_child(int argc, char **argv, int *f_in, int *f_out, 2735 } 2736 2737 if (remote_option_cnt) { 2738+ const char *db_config_save = db_config; 2739 int rc = remote_option_cnt + 1; 2740 const char **rv = remote_options; 2741 if (!parse_arguments(&rc, &rv)) { 2742 option_error(); 2743 exit_cleanup(RERR_SYNTAX); 2744 } 2745- } 2746+ if (db_config == db_config_save) 2747+ same_db = db_config != NULL; 2748+ else if (!db_config || !db_config_save || strcmp(db_config, db_config_save) != 0) { 2749+ use_db = 0; 2750+ if (db_config) 2751+ db_read_config(FERROR, db_config); 2752+ } 2753+ } else if (use_db) 2754+ same_db = 1; 2755 2756 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 2757 || close(to_child_pipe[1]) < 0 2758diff --git a/receiver.c b/receiver.c 2759--- a/receiver.c 2760+++ b/receiver.c 2761@@ -24,6 +24,8 @@ 2762 2763 extern int dry_run; 2764 extern int do_xfers; 2765+extern int use_db; 2766+extern int db_lax; 2767 extern int am_root; 2768 extern int am_server; 2769 extern int inc_recurse; 2770@@ -433,6 +435,11 @@ static void handle_delayed_updates(char *local_name) 2771 "rename failed for %s (from %s)", 2772 full_fname(fname), partialptr); 2773 } else { 2774+ if (use_db && !db_lax) { 2775+ STRUCT_STAT st; 2776+ if (do_lstat(fname, &st) == 0) 2777+ db_update_ctime(5, &st); 2778+ } 2779 if (remove_source_files 2780 || (preserve_hard_links && F_IS_HLINKED(file))) 2781 send_msg_int(MSG_SUCCESS, ndx); 2782@@ -539,6 +546,9 @@ int recv_files(int f_in, int f_out, char *local_name) 2783 if (delay_updates) 2784 delayed_bits = bitbag_create(cur_flist->used + 1); 2785 2786+ if (use_db && (append_mode == 1 || protocol_version < 30)) 2787+ use_db = 0; /* We can't note finished md5 values */ 2788+ 2789 progress_init(); 2790 2791 while (1) { 2792@@ -878,6 +888,8 @@ int recv_files(int f_in, int f_out, char *local_name) 2793 do_unlink(partialptr); 2794 handle_partial_dir(partialptr, PDIR_DELETE); 2795 } 2796+ if (use_db && do_lstat(fname, &st) == 0) 2797+ db_set_checksum(5, &st, sender_file_sum); 2798 } else if (keep_partial && partialptr && !one_inplace) { 2799 if (!handle_partial_dir(partialptr, PDIR_CREATE)) { 2800 rprintf(FERROR, 2801@@ -891,6 +903,8 @@ int recv_files(int f_in, int f_out, char *local_name) 2802 recv_ok = -1; 2803 else if (delay_updates && recv_ok) { 2804 bitbag_set_bit(delayed_bits, ndx); 2805+ if (use_db && do_lstat(partialptr, &st) == 0) 2806+ db_set_checksum(5, &st, sender_file_sum); 2807 recv_ok = 2; 2808 } else 2809 partialptr = NULL; 2810diff --git a/rsync.1.md b/rsync.1.md 2811--- a/rsync.1.md 2812+++ b/rsync.1.md 2813@@ -384,6 +384,9 @@ detailed description below for a complete description. 2814 --dry-run, -n perform a trial run with no changes made 2815 --whole-file, -W copy files whole (w/o delta-xfer algorithm) 2816 --checksum-choice=STR choose the checksum algorithm (aka --cc) 2817+--db=CONFIG_FILE specify a CONFIG_FILE for DB checksums 2818+--db-only=CONFIG_FILE behave like rsyncdb 2819+--db-lax ignore ctime changes (use with CAUTION) 2820 --one-file-system, -x don't cross filesystem boundaries 2821 --block-size=SIZE, -B force a fixed checksum block-size 2822 --rsh=COMMAND, -e specify the remote shell to use 2823diff --git a/rsync.c b/rsync.c 2824--- a/rsync.c 2825+++ b/rsync.c 2826@@ -39,6 +39,7 @@ extern int am_daemon; 2827 extern int am_sender; 2828 extern int am_receiver; 2829 extern int am_generator; 2830+extern int am_dbadmin; 2831 extern int am_starting_up; 2832 extern int allow_8bit_chars; 2833 extern int protocol_version; 2834@@ -807,6 +808,8 @@ struct file_list *flist_for_ndx(int ndx, const char *fatal_error_loc) 2835 2836 const char *who_am_i(void) 2837 { 2838+ if (am_dbadmin) 2839+ return "rsyncdb"; 2840 if (am_starting_up) 2841 return am_server ? "server" : "client"; 2842 return am_sender ? "sender" 2843diff --git a/rsync.h b/rsync.h 2844--- a/rsync.h 2845+++ b/rsync.h 2846@@ -261,12 +261,16 @@ enum msgcode { 2847 MSG_IO_ERROR=22,/* the sending side had an I/O error */ 2848 MSG_IO_TIMEOUT=33,/* tell client about a daemon's timeout value */ 2849 MSG_NOOP=42, /* a do-nothing message (legacy protocol-30 only) */ 2850+ MSG_CHECKSUM=55,/* sent via rcvr -> gen pipe and local-host-only gen -> sender */ 2851 MSG_ERROR_EXIT=86, /* synchronize an error exit (siblings and protocol >= 31) */ 2852 MSG_SUCCESS=100,/* successfully updated indicated flist index */ 2853 MSG_DELETED=101,/* successfully deleted a file on receiving side */ 2854 MSG_NO_SEND=102,/* sender failed to open a file we wanted */ 2855 }; 2856 2857+#define MSG_CHECKSUM_LONGS 5 2858+#define MSG_CHECKSUM_LEN (MSG_CHECKSUM_LONGS*8 + 4 + MAX_DIGEST_LEN) 2859+ 2860 #define NDX_DONE -1 2861 #define NDX_FLIST_EOF -2 2862 #define NDX_DEL_STATS -3 2863@@ -1419,7 +1423,8 @@ extern short info_levels[], debug_levels[]; 2864 #define DEBUG_CHDIR (DEBUG_BIND+1) 2865 #define DEBUG_CONNECT (DEBUG_CHDIR+1) 2866 #define DEBUG_CMD (DEBUG_CONNECT+1) 2867-#define DEBUG_DEL (DEBUG_CMD+1) 2868+#define DEBUG_DB (DEBUG_CMD+1) 2869+#define DEBUG_DEL (DEBUG_DB+1) 2870 #define DEBUG_DELTASUM (DEBUG_DEL+1) 2871 #define DEBUG_DUP (DEBUG_DELTASUM+1) 2872 #define DEBUG_EXIT (DEBUG_DUP+1) 2873diff --git a/rsyncdb-mountinfo b/rsyncdb-mountinfo 2874new file mode 100755 2875--- /dev/null 2876+++ b/rsyncdb-mountinfo 2877@@ -0,0 +1,82 @@ 2878+#!/usr/bin/perl 2879+ 2880+# This script outputs data for rsyncdb --mounts. It must output a complete 2881+# list of the mounts for the current host in a strict format -- 2 fields 2882+# with a Tab between: $MOUNT_UNIQ\t$PATH 2883+# 2884+# The list of mounts MUST NOT contain any entry that has the same devnum 2885+# (st_dev) as any other entry in the list (as checked via its PATH). 2886+# 2887+# MOUNT_UNIQ is a unique string that identifies the mount on this host. 2888+# This cannot be the devnum (st_dev) because that can vary depending on the 2889+# mount order or be reused for different mounts if they are not mounted at 2890+# the same time. Ideally this would be its UUID value, if that is available 2891+# on this OS. This script looks in /dev/disk/by-uuid for the current UUID 2892+# mappings). If the UUID is not found, the fallback default is the string 2893+# "Mount of $devname", which should be adequate for situations that don't 2894+# use removable media (though you may need to take steps to weed-out removable 2895+# mounts). 2896+# 2897+# You can override the MOUNT_UNIQ value by putting a .rsyncdb_mount_uniq 2898+# file in the root directory of any mount, at which point it is up to you 2899+# to make sure that the value stays unique (note that all sequences of 2900+# whitespace are transformed into a single space, and leading/trailing 2901+# whitespace is removed). 2902+# 2903+# MOUNT_UNIQ may never contain a Tab but it would be legal for PATH to have 2904+# a Tab (just really weird). Neither may have a CR or LF in it. 2905+# 2906+# The maximum size for MOUNT_UNIQ is 256 characters. 2907+# 2908+# If this script doesn't meet your needs, feel free to edit/replace it and 2909+# choose some other method of finding a unique value for each mount. If you 2910+# come up with a good idiom that might be useful to others, please share it 2911+# with the rsync mailing list. 2912+ 2913+use strict; 2914+use warnings; 2915+use Cwd 'abs_path'; 2916+ 2917+my @MOUNT_FILES = qw( /proc/mounts /etc/mtab ); 2918+my $VALID_DEVICE_REGEX = qr{^/dev|^rootfs$}; 2919+my $UUID_DIR = '/dev/disk/by-uuid'; 2920+my $OVERRIDE_FILE = '.rsyncdb_mount_uniq'; 2921+ 2922+my (%hash, %uuid); 2923+ 2924+if (-d $UUID_DIR) { 2925+ foreach my $uuid (glob "$UUID_DIR/*") { 2926+ my $lnk = readlink($uuid); 2927+ if ($lnk !~ m{^/}) { 2928+ $lnk = abs_path("$UUID_DIR/$lnk"); 2929+ } 2930+ $uuid =~ s{.*/}{}; 2931+ $uuid{$lnk} = $uuid; 2932+ } 2933+} 2934+ 2935+foreach my $mount_file (@MOUNT_FILES) { 2936+ if (open MOUNTS, $mount_file) { 2937+ while (<MOUNTS>) { 2938+ my ($devname, $path) = (split)[0,1]; 2939+ next unless $devname =~ /$VALID_DEVICE_REGEX/; 2940+ 2941+ my ($devno) = (stat($path))[0]; 2942+ next unless defined $devno; # Skip if mount is invalid. 2943+ next if $hash{$devno}++; # SKip if we've seen this devno earlier. 2944+ 2945+ my $mount_uniq = $uuid{$devname} ? $uuid{$devname} : "Mount of $devname"; 2946+ if (open UNIQ, '<', "$path/$OVERRIDE_FILE") { 2947+ $mount_uniq = <UNIQ>; 2948+ close UNIQ; 2949+ $mount_uniq =~ s/\s+/ /g; # This ensures no tab, CR, nor LF. 2950+ $mount_uniq =~ s/^ | $//g; # .. and no leading or trailing whitespace. 2951+ } 2952+ print $mount_uniq, "\t", $path, "\n"; 2953+ } 2954+ close MOUNTS; 2955+ exit; 2956+ } 2957+} 2958+ 2959+die "Failed to to open any mount files: @MOUNT_FILES\n"; 2960diff --git a/rsyncdb.1.md b/rsyncdb.1.md 2961new file mode 100644 2962--- /dev/null 2963+++ b/rsyncdb.1.md 2964@@ -0,0 +1,217 @@ 2965+# NAME 2966+ 2967+rsyncdb - Maintain an rsync checksum DB 2968+ 2969+# SYNOPSIS 2970+ 2971+``` 2972+rsyncdb --db=CONFIG [OPTION...] [DIR...] 2973+``` 2974+ 2975+# DESCRIPTION 2976+ 2977+Rsyncdb can maintain a checksum-caching DB that rsync can use to make its 2978+`--checksum` option more optimal. You must specify a config file via 2979+the `--db=CONFIG_FILE` option in order for rsyncdb to know what DB to 2980+manipulate. See the rsync manpage's `--db` option for full details on 2981+the file's format. 2982+ 2983+You can specify one or more directory args for rsyncdb to scan. If no 2984+DIR args are specified, the current directory is assumed to be the spot 2985+to start scanning. 2986+ 2987+Note that the rsyncdb program is usually just a symlink to the rsync program. 2988+You can force rsync to behave as rsyncdb either by having a symlink (or 2989+hardlink) name that ends with "db" or by `starting` the rsync args with 2990+`--db-only=CONFIG` (and that option works just like `--db=CONFIG` to 2991+a program named rsyncdb). 2992+ 2993+# EXAMPLES 2994+ 2995+The following command will update checksum information in the database 2996+described in the /etc/db.conf file: 2997+ 2998+> rsyncdb --db=/etc/db.conf -o n --clean /dir1 /dir2 2999+ 3000+It scans 2 directory hierarchies (/dir1 & /dir2) and cleans out any 3001+checksums whose inodes are no longer found in those directories (so that 3002+directory args are presumed to be complete for this host's DB contents). 3003+ 3004+The following command will scan all the files in the /dir2 directory (without 3005+recursive scanning, due to the `--no-r` option) and check them against 3006+the DB: 3007+ 3008+> rsyncdb --db=/etc/db.conf --check --no-r /dir2 3009+ 3010+Any errors found are output as well as being fixed in the DB. (See 3011+`--no-update` for how to check without updating.) 3012+ 3013+The following command will output MD5 sums for all the files found in the 3014+directories mentioned, even if they are unchanged (due to the 3015+`--output=us` option): 3016+ 3017+> rsyncdb --db=/etc/db.conf -rous /dir* >/tmp/md5sums.txt 3018+ 3019+This is just like running md5sum, only faster. Unlike md5sum, you can't 3020+specify a single file, so use `--no-r` and grep the output if you just 3021+want to see a single file's value. 3022+ 3023+The following command initializes a new DB, and is required for any new DB: 3024+ 3025+> rsyncdb --db=/etc/db.conf --init --mounts 3026+ 3027+The `--init` option should only be used once (unless you want to 3028+destroy existing data). The `--mounts` option may need to be used 3029+periodically, and makes use of a helper script (see below). 3030+ 3031+# OPTIONS SUMMARY 3032+ 3033+Rsyncdb accepts the following options: 3034+ 3035+[comment]: # (help-rsyncdb.h) 3036+ 3037+``` 3038+--db=CONFIG Specify the CONFIG file to read for the DB info 3039+--db-lax Ignore ctime changes (use with CAUTION) 3040+--recursive, -r Scan files in subdirs (the default w/o --no-recursive) 3041+--sums=SUMS, -s List which checksums to update (default: 4,5) 3042+--output=STR, -o One or more letters of what to output (default: "") 3043+--check, -c Check checksums (by reading the files) and fix any 3044+ issues. Makes --output default to "dni". 3045+--clean Note all inodes in the DIRS and remove DB extras 3046+--no-update, -N Avoids updating/adding info w/--check and/or --clean 3047+--init Initialize a DB by (re-)creating its tables 3048+--mounts Scan for mounted filesystems and update the DB 3049+--quiet, -q Disable the default non-error output 3050+--help, -h Display this help message 3051+``` 3052+ 3053+# OPTIONS 3054+ 3055+Rsyncdb accepts both long (double-dash + word) and short (single-dash + letter) 3056+options. The full list of the available options are described below. If an 3057+option can be specified in more than one way, the choices are comma-separated. 3058+Some options only have a long variant, not a short. If the option takes a 3059+parameter, the parameter is only listed after the long variant, even though it 3060+must also be specified for the short. When specifying a parameter, you can 3061+either use the form --option=param or replace the '=' with whitespace. The 3062+parameter may need to be quoted in some manner for it to survive the shell's 3063+command-line parsing. 3064+ 3065+0. `--db=CONFIG_FILE` 3066+ 3067+ This tells rsyncdb what DB-config file to read for the DB setup. This is 3068+ the same as the option in rsync, so refer to that manpage for full details. 3069+ 3070+0. `--db-lax` 3071+ 3072+ This option works just like it does in rsync, so refer to that manpage for 3073+ full details. 3074+ 3075+0. `--no-recursive, --no-r` 3076+ 3077+ This disables the default recursive directory scan that is performed on the 3078+ listed directory args. The options `--recursive` and `-r` are also 3079+ accepted, if someone wants to override an earlier `--no-r` override. 3080+ 3081+0. `--sums=SUMS, -s` 3082+ 3083+ Only output/update the listed checksum types. By default we deal with just 3084+ the newer md5 checksums (i.e. `--sums=5`). 3085+ 3086+ Note that this option does NOT affect the order that checksums are output 3087+ if "-o s" is enabled, so `-s5,4` is the same as `-s4,5`. 3088+ 3089+0. `--output=STR, -o` 3090+ 3091+ The output option lets you specify one or more letters indicating what 3092+ information should be output. If `--output` is not specified, the default 3093+ is either "dn" or (with `--check`) "dni". 3094+ 3095+ The following letters are accepted in the string: 3096+ 3097+ - `d` outputs "... dir_name ..." lines for each directory in our scan. if 3098+ "d" is omitted, then this progress indictor is not output. 3099+ - `n` includes the file's name in the per-file output. These lines are only 3100+ output for changed files unless "u" is given. The "n" option is implied 3101+ by every other output option letter except "d". 3102+ - `s` includes the checksum info in the per-file output. 3103+ - `c` is a synonym for 's'. 3104+ - `i` includes itemized change info in the per-file output. 3105+ - `!i` indicates that the time and/or size is wrong. 3106+ - `+4` indicates the MD4 sum is missing. 3107+ - `+5` indicates the MD5 sum is missing. 3108+ - `!4` indicates the MD4 sum is wrong. 3109+ - `!5` indicates the MD5 sum is wrong. 3110+ - `?4` indicates an unknown MD4 difference. This can happen if we didn't 3111+ need to read the file; i.e. if the time/size is wrong and no sum info 3112+ was requested. 3113+ - `?5` indicates an unknown MD5 difference. 3114+ - `u` includes unchanged files in the per-file output lines. 3115+ 3116+0. `--check, -c` 3117+ 3118+ Check the checksums (forcing the reading of all the files) and fix any 3119+ issues that are found. Makes `--output` default to "dni". 3120+ 3121+0. `--clean` 3122+ 3123+ Makes a temp-DB of all the inodes that we find in all the listed 3124+ directories and removes any extraneous checksums from the DB. You will 3125+ need to specify all the mounted directories that are present (and listed as 3126+ mounted) in the DB on this host or else the checksums from the unvisited 3127+ directories will be discarded from the DB. If you want to just --clean 3128+ without adding or updating the info of new or changed files, specify 3129+ `--no-update` as well. 3130+ 3131+0. `--no-update, -N` 3132+ 3133+ Avoids updating/adding info with `--check` and/or `--clean`. 3134+ 3135+0. `--quiet, -q` 3136+ 3137+ Disable the default (non-error) output settings. This turns off the 3138+ messages that `--init`, `--mount`, and `--clean` output, and makes the 3139+ default for `--output` be nothing (though an explicit `--output` option is 3140+ not affected). 3141+ 3142+0. `--init` 3143+ 3144+ Create the tables in the DB. If it is used on an existing DB, all the 3145+ existing tables are dropped and re-created. 3146+ 3147+This option cannot be combined with the updating or reporting of checksum 3148+information, but may be combined with `--mounts`. 3149+ 3150+0. `--mounts` 3151+ 3152+ Populate the "disk" DB with the available device numbers and change any 3153+ mounted/unmount information for devices. This should be run every time a 3154+ mount-change happens that may affect a directory hierarchy in the DB. 3155+ Rsyncdb will not save any checksums for a device that is not listed in the 3156+ "disk" table. 3157+ 3158+ The helper script "rsyncdb-mountinfo" is used as the source of the mount 3159+ information on the host, which it derives from various system files and 3160+ UUID directories (if available). That script supports the use of an 3161+ override file named ".rsyncdb_mount_uniq" in the root of the mount as one 3162+ way to manually assign unique values to a shared (mountable) device's 3163+ various disks. 3164+ 3165+ Some advanced users may want to maintain the disk table themselves in order 3166+ to support mounting a drive in different (or multiple) locations, etc. 3167+ 3168+ Specifying the `--mounts` option cannot be combined with updating or 3169+ reporting of checksum information, but may be combined with `--init`. 3170+ 3171+0. `--help, -h` 3172+ 3173+ Display a summary of the options. 3174+ 3175+# SEE ALSO 3176+ 3177+**rsync**(1) 3178+ 3179+# AUTHOR 3180+ 3181+Rsyncdb was written by Wayne Davison. 3182diff --git a/usage.c b/usage.c 3183--- a/usage.c 3184+++ b/usage.c 3185@@ -131,6 +131,16 @@ static void print_info_flags(enum logcode f) 3186 #endif 3187 "crtimes", 3188 3189+#if !defined HAVE_MYSQL_MYSQL_H || !defined HAVE_LIBMYSQLCLIENT 3190+ "no " 3191+#endif 3192+ "MySQL", 3193+ 3194+#if !defined HAVE_SQLITE3_H || !defined HAVE_LIBSQLITE3 3195+ "no " 3196+#endif 3197+ "SQLite", 3198+ 3199 "*Optimizations", 3200 3201 #ifndef HAVE_SIMD 3202@@ -250,6 +260,14 @@ void daemon_usage(enum logcode F) 3203 rprintf(F,"daemon-specific rsync options. See also the rsyncd.conf(5) man page.\n"); 3204 } 3205 3206+void dbonly_usage(enum logcode F) 3207+{ 3208+ rprintf(F,"Usage: rsyncdb --db=CONFIG_FILE [OPTIONS] [DIRS]\n"); 3209+ rprintf(F,"\n"); 3210+ rprintf(F,"Options:\n"); 3211+#include "help-rsyncdb.h" 3212+} 3213+ 3214 const char *rsync_version(void) 3215 { 3216 return RSYNC_GITVER; 3217diff -Nurp a/config.h.in b/config.h.in 3218--- a/config.h.in 3219+++ b/config.h.in 3220@@ -228,6 +228,9 @@ 3221 /* Define to 1 if you have the `inet' library (-linet). */ 3222 #undef HAVE_LIBINET 3223 3224+/* Define to 1 if you have the `mysqlclient' library (-lmysqlclient). */ 3225+#undef HAVE_LIBMYSQLCLIENT 3226+ 3227 /* Define to 1 if you have the `nsl' library (-lnsl). */ 3228 #undef HAVE_LIBNSL 3229 3230@@ -246,6 +249,9 @@ 3231 /* Define to 1 if you have the `socket' library (-lsocket). */ 3232 #undef HAVE_LIBSOCKET 3233 3234+/* Define to 1 if you have the `sqlite3' library (-lsqlite3). */ 3235+#undef HAVE_LIBSQLITE3 3236+ 3237 /* Define to 1 if you have the `z' library (-lz). */ 3238 #undef HAVE_LIBZ 3239 3240@@ -320,6 +326,9 @@ 3241 /* Define to 1 if you have the `mtrace' function. */ 3242 #undef HAVE_MTRACE 3243 3244+/* Define to 1 if you have the <mysql/mysql.h> header file. */ 3245+#undef HAVE_MYSQL_MYSQL_H 3246+ 3247 /* Define to 1 if you have the `nanosleep' function. */ 3248 #undef HAVE_NANOSLEEP 3249 3250@@ -440,6 +449,15 @@ 3251 /* True if you have Solaris xattrs */ 3252 #undef HAVE_SOLARIS_XATTRS 3253 3254+/* Define to 1 if you have the <sqlite3.h> header file. */ 3255+#undef HAVE_SQLITE3_H 3256+ 3257+/* Define to 1 if you have the `sqlite3_open_v2' function. */ 3258+#undef HAVE_SQLITE3_OPEN_V2 3259+ 3260+/* Define to 1 if you have the `sqlite3_prepare_v2' function. */ 3261+#undef HAVE_SQLITE3_PREPARE_V2 3262+ 3263 /* Define to 1 if you have the <stdint.h> header file. */ 3264 #undef HAVE_STDINT_H 3265 3266diff -Nurp a/configure.sh b/configure.sh 3267--- a/configure.sh 3268+++ b/configure.sh 3269@@ -625,6 +625,7 @@ ac_includes_default="\ 3270 3271 ac_header_list= 3272 ac_subst_vars='LTLIBOBJS 3273+MYSQL_CONFIG 3274 MAKE_MAN 3275 BUILD_ZLIB 3276 BUILD_POPT 3277@@ -733,6 +734,8 @@ enable_iconv_open 3278 enable_iconv 3279 enable_acl_support 3280 enable_xattr_support 3281+enable_mysql 3282+enable_sqlite 3283 ' 3284 ac_precious_vars='build_alias 3285 host_alias 3286@@ -1388,6 +1391,8 @@ Optional Features: 3287 --disable-iconv disable rsync's --iconv option 3288 --disable-acl-support disable ACL support 3289 --disable-xattr-support disable extended attributes 3290+ --disable-mysql disable mysql DB support 3291+ --disable-sqlite disable sqlite DB support 3292 3293 Optional Packages: 3294 --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] 3295@@ -6249,6 +6254,7 @@ for ac_header in sys/fcntl.h sys/select. 3296 unistd.h utime.h grp.h compat.h sys/param.h ctype.h sys/wait.h \ 3297 sys/ioctl.h sys/filio.h string.h stdlib.h sys/socket.h sys/mode.h \ 3298 sys/un.h sys/attr.h mcheck.h arpa/inet.h arpa/nameser.h locale.h \ 3299+ mysql/mysql.h sqlite3.h \ 3300 netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h \ 3301 sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h dl.h \ 3302 popt.h popt/popt.h linux/falloc.h netinet/in_systm.h netinet/ip.h \ 3303@@ -10086,6 +10092,196 @@ $as_echo "$rsync_warn_flag" >&6; } 3304 fi 3305 fi 3306 3307+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include mysql DB support" >&5 3308+$as_echo_n "checking whether to include mysql DB support... " >&6; } 3309+# Check whether --enable-mysql was given. 3310+if test "${enable_mysql+set}" = set; then : 3311+ enableval=$enable_mysql; 3312+fi 3313+ 3314+ 3315+if test x"$enable_mysql" = x"no"; then 3316+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 3317+$as_echo "no" >&6; } 3318+else 3319+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 3320+$as_echo "yes" >&6; } 3321+ # Extract the first word of "mysql_config", so it can be a program name with args. 3322+set dummy mysql_config; ac_word=$2 3323+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 3324+$as_echo_n "checking for $ac_word... " >&6; } 3325+if ${ac_cv_prog_MYSQL_CONFIG+:} false; then : 3326+ $as_echo_n "(cached) " >&6 3327+else 3328+ if test -n "$MYSQL_CONFIG"; then 3329+ ac_cv_prog_MYSQL_CONFIG="$MYSQL_CONFIG" # Let the user override the test. 3330+else 3331+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR 3332+for as_dir in $PATH 3333+do 3334+ IFS=$as_save_IFS 3335+ test -z "$as_dir" && as_dir=. 3336+ for ac_exec_ext in '' $ac_executable_extensions; do 3337+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then 3338+ ac_cv_prog_MYSQL_CONFIG="1" 3339+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 3340+ break 2 3341+ fi 3342+done 3343+ done 3344+IFS=$as_save_IFS 3345+ 3346+ test -z "$ac_cv_prog_MYSQL_CONFIG" && ac_cv_prog_MYSQL_CONFIG="0" 3347+fi 3348+fi 3349+MYSQL_CONFIG=$ac_cv_prog_MYSQL_CONFIG 3350+if test -n "$MYSQL_CONFIG"; then 3351+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MYSQL_CONFIG" >&5 3352+$as_echo "$MYSQL_CONFIG" >&6; } 3353+else 3354+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 3355+$as_echo "no" >&6; } 3356+fi 3357+ 3358+ 3359+ if test x$MYSQL_CONFIG = x1; then 3360+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mysql version >= 4" >&5 3361+$as_echo_n "checking for mysql version >= 4... " >&6; } 3362+ mysql_version=`mysql_config --version` 3363+ mysql_major_version=`echo $mysql_version | sed 's/\..*//'` 3364+ if test $mysql_major_version -lt 4; then 3365+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no.. skipping MySQL" >&5 3366+$as_echo "no.. skipping MySQL" >&6; } 3367+ else 3368+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 3369+$as_echo "yes" >&6; } 3370+ 3371+ MYSQL_CFLAGS=`mysql_config --cflags` 3372+ MYSQL_LIBS=`mysql_config --libs` 3373+ 3374+ CPPFLAGS="$CPPFLAGS $MYSQL_CFLAGS" 3375+ LIBS="$MYSQL_LIBS $LIBS" 3376+ 3377+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mysql_init in -lmysqlclient" >&5 3378+$as_echo_n "checking for mysql_init in -lmysqlclient... " >&6; } 3379+if ${ac_cv_lib_mysqlclient_mysql_init+:} false; then : 3380+ $as_echo_n "(cached) " >&6 3381+else 3382+ ac_check_lib_save_LIBS=$LIBS 3383+LIBS="-lmysqlclient $LIBS" 3384+cat confdefs.h - <<_ACEOF >conftest.$ac_ext 3385+/* end confdefs.h. */ 3386+ 3387+/* Override any GCC internal prototype to avoid an error. 3388+ Use char because int might match the return type of a GCC 3389+ builtin and then its argument prototype would still apply. */ 3390+#ifdef __cplusplus 3391+extern "C" 3392+#endif 3393+char mysql_init (); 3394+int 3395+main () 3396+{ 3397+return mysql_init (); 3398+ ; 3399+ return 0; 3400+} 3401+_ACEOF 3402+if ac_fn_c_try_link "$LINENO"; then : 3403+ ac_cv_lib_mysqlclient_mysql_init=yes 3404+else 3405+ ac_cv_lib_mysqlclient_mysql_init=no 3406+fi 3407+rm -f core conftest.err conftest.$ac_objext \ 3408+ conftest$ac_exeext conftest.$ac_ext 3409+LIBS=$ac_check_lib_save_LIBS 3410+fi 3411+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mysqlclient_mysql_init" >&5 3412+$as_echo "$ac_cv_lib_mysqlclient_mysql_init" >&6; } 3413+if test "x$ac_cv_lib_mysqlclient_mysql_init" = xyes; then : 3414+ cat >>confdefs.h <<_ACEOF 3415+#define HAVE_LIBMYSQLCLIENT 1 3416+_ACEOF 3417+ 3418+ LIBS="-lmysqlclient $LIBS" 3419+ 3420+fi 3421+ 3422+ fi 3423+ fi 3424+fi 3425+ 3426+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include sqlite DB support" >&5 3427+$as_echo_n "checking whether to include sqlite DB support... " >&6; } 3428+# Check whether --enable-sqlite was given. 3429+if test "${enable_sqlite+set}" = set; then : 3430+ enableval=$enable_sqlite; 3431+fi 3432+ 3433+ 3434+if test x"$enable_sqlite" = x"no"; then 3435+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 3436+$as_echo "no" >&6; } 3437+else 3438+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_open in -lsqlite3" >&5 3439+$as_echo_n "checking for sqlite3_open in -lsqlite3... " >&6; } 3440+if ${ac_cv_lib_sqlite3_sqlite3_open+:} false; then : 3441+ $as_echo_n "(cached) " >&6 3442+else 3443+ ac_check_lib_save_LIBS=$LIBS 3444+LIBS="-lsqlite3 $LIBS" 3445+cat confdefs.h - <<_ACEOF >conftest.$ac_ext 3446+/* end confdefs.h. */ 3447+ 3448+/* Override any GCC internal prototype to avoid an error. 3449+ Use char because int might match the return type of a GCC 3450+ builtin and then its argument prototype would still apply. */ 3451+#ifdef __cplusplus 3452+extern "C" 3453+#endif 3454+char sqlite3_open (); 3455+int 3456+main () 3457+{ 3458+return sqlite3_open (); 3459+ ; 3460+ return 0; 3461+} 3462+_ACEOF 3463+if ac_fn_c_try_link "$LINENO"; then : 3464+ ac_cv_lib_sqlite3_sqlite3_open=yes 3465+else 3466+ ac_cv_lib_sqlite3_sqlite3_open=no 3467+fi 3468+rm -f core conftest.err conftest.$ac_objext \ 3469+ conftest$ac_exeext conftest.$ac_ext 3470+LIBS=$ac_check_lib_save_LIBS 3471+fi 3472+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sqlite3_sqlite3_open" >&5 3473+$as_echo "$ac_cv_lib_sqlite3_sqlite3_open" >&6; } 3474+if test "x$ac_cv_lib_sqlite3_sqlite3_open" = xyes; then : 3475+ cat >>confdefs.h <<_ACEOF 3476+#define HAVE_LIBSQLITE3 1 3477+_ACEOF 3478+ 3479+ LIBS="-lsqlite3 $LIBS" 3480+ 3481+fi 3482+ 3483+ for ac_func in sqlite3_open_v2 sqlite3_prepare_v2 3484+do : 3485+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` 3486+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" 3487+if eval test \"x\$"$as_ac_var"\" = x"yes"; then : 3488+ cat >>confdefs.h <<_ACEOF 3489+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 3490+_ACEOF 3491+ 3492+fi 3493+done 3494+ 3495+fi 3496+ 3497 case "$CC" in 3498 ' checker'*|checker*) 3499 3500diff -Nurp a/rsync.1 b/rsync.1 3501--- a/rsync.1 3502+++ b/rsync.1 3503@@ -460,6 +460,9 @@ detailed description below for a complet 3504 --dry-run, -n perform a trial run with no changes made 3505 --whole-file, -W copy files whole (w/o delta-xfer algorithm) 3506 --checksum-choice=STR choose the checksum algorithm (aka --cc) 3507+--db=CONFIG_FILE specify a CONFIG_FILE for DB checksums 3508+--db-only=CONFIG_FILE behave like rsyncdb 3509+--db-lax ignore ctime changes (use with CAUTION) 3510 --one-file-system, -x don't cross filesystem boundaries 3511 --block-size=SIZE, -B force a fixed checksum block-size 3512 --rsh=COMMAND, -e specify the remote shell to use 3513diff -Nurp a/rsync.1.html b/rsync.1.html 3514--- a/rsync.1.html 3515+++ b/rsync.1.html 3516@@ -375,6 +375,9 @@ detailed description below for a complet 3517 --dry-run, -n perform a trial run with no changes made 3518 --whole-file, -W copy files whole (w/o delta-xfer algorithm) 3519 --checksum-choice=STR choose the checksum algorithm (aka --cc) 3520+--db=CONFIG_FILE specify a CONFIG_FILE for DB checksums 3521+--db-only=CONFIG_FILE behave like rsyncdb 3522+--db-lax ignore ctime changes (use with CAUTION) 3523 --one-file-system, -x don't cross filesystem boundaries 3524 --block-size=SIZE, -B force a fixed checksum block-size 3525 --rsh=COMMAND, -e specify the remote shell to use 3526diff -Nurp a/rsyncdb.1 b/rsyncdb.1 3527--- a/rsyncdb.1 3528+++ b/rsyncdb.1 3529@@ -0,0 +1,230 @@ 3530+.TH "rsyncdb" "1" "06 Aug 2020" "rsyncdb 3.2.3" "User Commands" 3531+.P 3532+.SH "NAME" 3533+.P 3534+rsyncdb \- Maintain an rsync checksum DB 3535+.P 3536+.SH "SYNOPSIS" 3537+.P 3538+.nf 3539+rsyncdb --db=CONFIG [OPTION...] [DIR...] 3540+.fi 3541+.P 3542+.SH "DESCRIPTION" 3543+.P 3544+Rsyncdb can maintain a checksum-caching DB that rsync can use to make its 3545+\fB\-\-checksum\fP option more optimal. You must specify a config file via 3546+the \fB\-\-db=CONFIG_FILE\fP option in order for rsyncdb to know what DB to 3547+manipulate. See the rsync manpage's \fB\-\-db\fP option for full details on 3548+the file's format. 3549+.P 3550+You can specify one or more directory args for rsyncdb to scan. If no 3551+DIR args are specified, the current directory is assumed to be the spot 3552+to start scanning. 3553+.P 3554+Note that the rsyncdb program is usually just a symlink to the rsync program. 3555+You can force rsync to behave as rsyncdb either by having a symlink (or 3556+hardlink) name that ends with "db" or by \fBstarting\fP the rsync args with 3557+\fB\-\-db-only=CONFIG\fP (and that option works just like \fB\-\-db=CONFIG\fP to 3558+a program named rsyncdb). 3559+.P 3560+.SH "EXAMPLES" 3561+.P 3562+The following command will update checksum information in the database 3563+described in the /etc/db.conf file: 3564+.RS 4 3565+.P 3566+.nf 3567+rsyncdb --db=/etc/db.conf -o n --clean /dir1 /dir2 3568+.fi 3569+.RE 3570+.P 3571+It scans 2 directory hierarchies (/dir1 & /dir2) and cleans out any 3572+checksums whose inodes are no longer found in those directories (so that 3573+directory args are presumed to be complete for this host's DB contents). 3574+.P 3575+The following command will scan all the files in the /dir2 directory (without 3576+recursive scanning, due to the \fB\-\-no-r\fP option) and check them against 3577+the DB: 3578+.RS 4 3579+.P 3580+.nf 3581+rsyncdb --db=/etc/db.conf --check --no-r /dir2 3582+.fi 3583+.RE 3584+.P 3585+Any errors found are output as well as being fixed in the DB. (See 3586+\fB\-\-no-update\fP for how to check without updating.) 3587+.P 3588+The following command will output MD5 sums for all the files found in the 3589+directories mentioned, even if they are unchanged (due to the 3590+\fB\-\-output=us\fP option): 3591+.RS 4 3592+.P 3593+.nf 3594+rsyncdb --db=/etc/db.conf -rous /dir* >/tmp/md5sums.txt 3595+.fi 3596+.RE 3597+.P 3598+This is just like running md5sum, only faster. Unlike md5sum, you can't 3599+specify a single file, so use \fB\-\-no-r\fP and grep the output if you just 3600+want to see a single file's value. 3601+.P 3602+The following command initializes a new DB, and is required for any new DB: 3603+.RS 4 3604+.P 3605+.nf 3606+rsyncdb --db=/etc/db.conf --init --mounts 3607+.fi 3608+.RE 3609+.P 3610+The \fB\-\-init\fP option should only be used once (unless you want to 3611+destroy existing data). The \fB\-\-mounts\fP option may need to be used 3612+periodically, and makes use of a helper script (see below). 3613+.P 3614+.SH "OPTIONS SUMMARY" 3615+.P 3616+Rsyncdb accepts the following options: 3617+.P 3618+.nf 3619+--db=CONFIG Specify the CONFIG file to read for the DB info 3620+--db-lax Ignore ctime changes (use with CAUTION) 3621+--recursive, -r Scan files in subdirs (the default w/o --no-recursive) 3622+--sums=SUMS, -s List which checksums to update (default: 4,5) 3623+--output=STR, -o One or more letters of what to output (default: "") 3624+--check, -c Check checksums (by reading the files) and fix any 3625+ issues. Makes --output default to "dni". 3626+--clean Note all inodes in the DIRS and remove DB extras 3627+--no-update, -N Avoids updating/adding info w/--check and/or --clean 3628+--init Initialize a DB by (re-)creating its tables 3629+--mounts Scan for mounted filesystems and update the DB 3630+--quiet, -q Disable the default non-error output 3631+--help, -h Display this help message 3632+.fi 3633+.P 3634+.SH "OPTIONS" 3635+.P 3636+Rsyncdb accepts both long (double-dash + word) and short (single-dash + letter) 3637+options. The full list of the available options are described below. If an 3638+option can be specified in more than one way, the choices are comma-separated. 3639+Some options only have a long variant, not a short. If the option takes a 3640+parameter, the parameter is only listed after the long variant, even though it 3641+must also be specified for the short. When specifying a parameter, you can 3642+either use the form \-\-option=param or replace the '=' with whitespace. The 3643+parameter may need to be quoted in some manner for it to survive the shell's 3644+command-line parsing. 3645+.P 3646+.IP "\fB\-\-db=CONFIG_FILE\fP" 3647+This tells rsyncdb what DB-config file to read for the DB setup. This is 3648+the same as the option in rsync, so refer to that manpage for full details. 3649+.IP "\fB\-\-db-lax\fP" 3650+This option works just like it does in rsync, so refer to that manpage for 3651+full details. 3652+.IP "\fB\-\-no-recursive,\ \-\-no-r\fP" 3653+This disables the default recursive directory scan that is performed on the 3654+listed directory args. The options \fB\-\-recursive\fP and \fB\-r\fP are also 3655+accepted, if someone wants to override an earlier \fB\-\-no-r\fP override. 3656+.IP "\fB\-\-sums=SUMS,\ \-s\fP" 3657+Only output/update the listed checksum types. By default we deal with just 3658+the newer md5 checksums (i.e. \fB\-\-sums=5\fP). 3659+.IP 3660+Note that this option does NOT affect the order that checksums are output 3661+if "\-o s" is enabled, so \fB\-s5,4\fP is the same as \fB\-s4,5\fP. 3662+.IP "\fB\-\-output=STR,\ \-o\fP" 3663+The output option lets you specify one or more letters indicating what 3664+information should be output. If \fB\-\-output\fP is not specified, the default 3665+is either "dn" or (with \fB\-\-check\fP) "dni". 3666+.IP 3667+The following letters are accepted in the string: 3668+.IP 3669+.RS 3670+.IP o 3671+\fBd\fP outputs "... dir_name ..." lines for each directory in our scan. if 3672+"d" is omitted, then this progress indictor is not output. 3673+.IP o 3674+\fBn\fP includes the file's name in the per-file output. These lines are only 3675+output for changed files unless "u" is given. The "n" option is implied 3676+by every other output option letter except "d". 3677+.IP o 3678+\fBs\fP includes the checksum info in the per-file output. 3679+.IP o 3680+\fBc\fP is a synonym for 's'. 3681+.IP o 3682+.IP 3683+.RS 3684+.IP o 3685+\fBi\fP includes itemized change info in the per-file output. 3686+ 3687+\fB!i\fP indicates that the time and/or size is wrong. 3688+.IP o 3689+\fB+4\fP indicates the MD4 sum is missing. 3690+.IP o 3691+\fB+5\fP indicates the MD5 sum is missing. 3692+.IP o 3693+\fB!4\fP indicates the MD4 sum is wrong. 3694+.IP o 3695+\fB!5\fP indicates the MD5 sum is wrong. 3696+.IP o 3697+\fB?4\fP indicates an unknown MD4 difference. This can happen if we didn't 3698+need to read the file; i.e. if the time/size is wrong and no sum info 3699+was requested. 3700+.IP o 3701+\fB?5\fP indicates an unknown MD5 difference. 3702+.RE 3703+.IP o 3704+\fBu\fP includes unchanged files in the per-file output lines. 3705+.RE 3706+.IP "\fB\-\-check,\ \-c\fP" 3707+Check the checksums (forcing the reading of all the files) and fix any 3708+issues that are found. Makes \fB\-\-output\fP default to "dni". 3709+.IP "\fB\-\-clean\fP" 3710+Makes a temp-DB of all the inodes that we find in all the listed 3711+directories and removes any extraneous checksums from the DB. You will 3712+need to specify all the mounted directories that are present (and listed as 3713+mounted) in the DB on this host or else the checksums from the unvisited 3714+directories will be discarded from the DB. If you want to just \-\-clean 3715+without adding or updating the info of new or changed files, specify 3716+\fB\-\-no-update\fP as well. 3717+.IP "\fB\-\-no-update,\ \-N\fP" 3718+Avoids updating/adding info with \fB\-\-check\fP and/or \fB\-\-clean\fP. 3719+.IP "\fB\-\-quiet,\ \-q\fP" 3720+Disable the default (non-error) output settings. This turns off the 3721+messages that \fB\-\-init\fP, \fB\-\-mount\fP, and \fB\-\-clean\fP output, and makes the 3722+default for \fB\-\-output\fP be nothing (though an explicit \fB\-\-output\fP option is 3723+not affected). 3724+.IP "\fB\-\-init\fP" 3725+Create the tables in the DB. If it is used on an existing DB, all the 3726+existing tables are dropped and re-created. 3727+.P 3728+This option cannot be combined with the updating or reporting of checksum 3729+information, but may be combined with \fB\-\-mounts\fP. 3730+.P 3731+.IP "\fB\-\-mounts\fP" 3732+Populate the "disk" DB with the available device numbers and change any 3733+mounted/unmount information for devices. This should be run every time a 3734+mount-change happens that may affect a directory hierarchy in the DB. 3735+Rsyncdb will not save any checksums for a device that is not listed in the 3736+"disk" table. 3737+.IP 3738+The helper script "rsyncdb-mountinfo" is used as the source of the mount 3739+information on the host, which it derives from various system files and 3740+UUID directories (if available). That script supports the use of an 3741+override file named ".rsyncdb_mount_uniq" in the root of the mount as one 3742+way to manually assign unique values to a shared (mountable) device's 3743+various disks. 3744+.IP 3745+Some advanced users may want to maintain the disk table themselves in order 3746+to support mounting a drive in different (or multiple) locations, etc. 3747+.IP 3748+Specifying the \fB\-\-mounts\fP option cannot be combined with updating or 3749+reporting of checksum information, but may be combined with \fB\-\-init\fP. 3750+.IP "\fB\-\-help,\ \-h\fP" 3751+Display a summary of the options. 3752+.P 3753+.SH "SEE ALSO" 3754+.P 3755+\fBrsync\fP(1) 3756+.P 3757+.SH "AUTHOR" 3758+.P 3759+Rsyncdb was written by Wayne Davison. 3760diff -Nurp a/rsyncdb.1.html b/rsyncdb.1.html 3761--- a/rsyncdb.1.html 3762+++ b/rsyncdb.1.html 3763@@ -0,0 +1,228 @@ 3764+<html><head> 3765+<title>rsyncdb(1) man page</title> 3766+<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet"> 3767+<style> 3768+body { 3769+ max-width: 50em; 3770+ margin: auto; 3771+} 3772+body, b, strong, u { 3773+ font-family: 'Roboto', sans-serif; 3774+} 3775+code { 3776+ font-family: 'Roboto Mono', monospace; 3777+ font-weight: bold; 3778+ white-space: pre; 3779+} 3780+pre code { 3781+ display: block; 3782+ font-weight: normal; 3783+} 3784+blockquote pre code { 3785+ background: #f1f1f1; 3786+} 3787+dd p:first-of-type { 3788+ margin-block-start: 0em; 3789+} 3790+</style> 3791+</head><body> 3792+<h1>NAME</h1> 3793+<p>rsyncdb -⁠ Maintain an rsync checksum DB</p> 3794+<h1>SYNOPSIS</h1> 3795+<pre><code>rsyncdb --db=CONFIG [OPTION...] [DIR...] 3796+</code></pre> 3797+<h1>DESCRIPTION</h1> 3798+<p>Rsyncdb can maintain a checksum-caching DB that rsync can use to make its 3799+<code>--checksum</code> option more optimal. You must specify a config file via 3800+the <code>--db=CONFIG_FILE</code> option in order for rsyncdb to know what DB to 3801+manipulate. See the rsync manpage's <code>--db</code> option for full details on 3802+the file's format.</p> 3803+<p>You can specify one or more directory args for rsyncdb to scan. If no 3804+DIR args are specified, the current directory is assumed to be the spot 3805+to start scanning.</p> 3806+<p>Note that the rsyncdb program is usually just a symlink to the rsync program. 3807+You can force rsync to behave as rsyncdb either by having a symlink (or 3808+hardlink) name that ends with "db" or by <code>starting</code> the rsync args with 3809+<code>--db-only=CONFIG</code> (and that option works just like <code>--db=CONFIG</code> to 3810+a program named rsyncdb).</p> 3811+<h1>EXAMPLES</h1> 3812+<p>The following command will update checksum information in the database 3813+described in the /etc/db.conf file:</p> 3814+<blockquote> 3815+<pre><code>rsyncdb --db=/etc/db.conf -o n --clean /dir1 /dir2 3816+</code></pre> 3817+</blockquote> 3818+<p>It scans 2 directory hierarchies (/dir1 & /dir2) and cleans out any 3819+checksums whose inodes are no longer found in those directories (so that 3820+directory args are presumed to be complete for this host's DB contents).</p> 3821+<p>The following command will scan all the files in the /dir2 directory (without 3822+recursive scanning, due to the <code>--no-r</code> option) and check them against 3823+the DB:</p> 3824+<blockquote> 3825+<pre><code>rsyncdb --db=/etc/db.conf --check --no-r /dir2 3826+</code></pre> 3827+</blockquote> 3828+<p>Any errors found are output as well as being fixed in the DB. (See 3829+<code>--no-update</code> for how to check without updating.)</p> 3830+<p>The following command will output MD5 sums for all the files found in the 3831+directories mentioned, even if they are unchanged (due to the 3832+<code>--output=us</code> option):</p> 3833+<blockquote> 3834+<pre><code>rsyncdb --db=/etc/db.conf -rous /dir* >/tmp/md5sums.txt 3835+</code></pre> 3836+</blockquote> 3837+<p>This is just like running md5sum, only faster. Unlike md5sum, you can't 3838+specify a single file, so use <code>--no-r</code> and grep the output if you just 3839+want to see a single file's value.</p> 3840+<p>The following command initializes a new DB, and is required for any new DB:</p> 3841+<blockquote> 3842+<pre><code>rsyncdb --db=/etc/db.conf --init --mounts 3843+</code></pre> 3844+</blockquote> 3845+<p>The <code>--init</code> option should only be used once (unless you want to 3846+destroy existing data). The <code>--mounts</code> option may need to be used 3847+periodically, and makes use of a helper script (see below).</p> 3848+<h1>OPTIONS SUMMARY</h1> 3849+<p>Rsyncdb accepts the following options:</p> 3850+<pre><code>--db=CONFIG Specify the CONFIG file to read for the DB info 3851+--db-lax Ignore ctime changes (use with CAUTION) 3852+--recursive, -r Scan files in subdirs (the default w/o --no-recursive) 3853+--sums=SUMS, -s List which checksums to update (default: 4,5) 3854+--output=STR, -o One or more letters of what to output (default: "") 3855+--check, -c Check checksums (by reading the files) and fix any 3856+ issues. Makes --output default to "dni". 3857+--clean Note all inodes in the DIRS and remove DB extras 3858+--no-update, -N Avoids updating/adding info w/--check and/or --clean 3859+--init Initialize a DB by (re-)creating its tables 3860+--mounts Scan for mounted filesystems and update the DB 3861+--quiet, -q Disable the default non-error output 3862+--help, -h Display this help message 3863+</code></pre> 3864+<h1>OPTIONS</h1> 3865+<p>Rsyncdb accepts both long (double-dash + word) and short (single-dash + letter) 3866+options. The full list of the available options are described below. If an 3867+option can be specified in more than one way, the choices are comma-separated. 3868+Some options only have a long variant, not a short. If the option takes a 3869+parameter, the parameter is only listed after the long variant, even though it 3870+must also be specified for the short. When specifying a parameter, you can 3871+either use the form -⁠-⁠option=param or replace the '=' with whitespace. The 3872+parameter may need to be quoted in some manner for it to survive the shell's 3873+command-line parsing.</p> 3874+<dl> 3875+ 3876+<dt><code>--db=CONFIG_FILE</code></dt><dd> 3877+<p>This tells rsyncdb what DB-config file to read for the DB setup. This is 3878+the same as the option in rsync, so refer to that manpage for full details.</p> 3879+</dd> 3880+ 3881+<dt><code>--db-lax</code></dt><dd> 3882+<p>This option works just like it does in rsync, so refer to that manpage for 3883+full details.</p> 3884+</dd> 3885+ 3886+<dt><code>--no-recursive, --no-r</code></dt><dd> 3887+<p>This disables the default recursive directory scan that is performed on the 3888+listed directory args. The options <code>--recursive</code> and <code>-r</code> are also 3889+accepted, if someone wants to override an earlier <code>--no-r</code> override.</p> 3890+</dd> 3891+ 3892+<dt><code>--sums=SUMS, -s</code></dt><dd> 3893+<p>Only output/update the listed checksum types. By default we deal with just 3894+the newer md5 checksums (i.e. <code>--sums=5</code>).</p> 3895+<p>Note that this option does NOT affect the order that checksums are output 3896+if "-⁠o s" is enabled, so <code>-s5,4</code> is the same as <code>-s4,5</code>.</p> 3897+</dd> 3898+ 3899+<dt><code>--output=STR, -o</code></dt><dd> 3900+<p>The output option lets you specify one or more letters indicating what 3901+information should be output. If <code>--output</code> is not specified, the default 3902+is either "dn" or (with <code>--check</code>) "dni".</p> 3903+<p>The following letters are accepted in the string:</p> 3904+<ul> 3905+<li><code>d</code> outputs "... dir_name ..." lines for each directory in our scan. if 3906+"d" is omitted, then this progress indictor is not output.</li> 3907+<li><code>n</code> includes the file's name in the per-file output. These lines are only 3908+output for changed files unless "u" is given. The "n" option is implied 3909+by every other output option letter except "d".</li> 3910+<li><code>s</code> includes the checksum info in the per-file output.</li> 3911+<li><code>c</code> is a synonym for 's'.</li> 3912+<li><code>i</code> includes itemized change info in the per-file output. 3913+<ul> 3914+<li><code>!i</code> indicates that the time and/or size is wrong.</li> 3915+<li><code>+4</code> indicates the MD4 sum is missing.</li> 3916+<li><code>+5</code> indicates the MD5 sum is missing.</li> 3917+<li><code>!4</code> indicates the MD4 sum is wrong.</li> 3918+<li><code>!5</code> indicates the MD5 sum is wrong.</li> 3919+<li><code>?4</code> indicates an unknown MD4 difference. This can happen if we didn't 3920+need to read the file; i.e. if the time/size is wrong and no sum info 3921+was requested.</li> 3922+<li><code>?5</code> indicates an unknown MD5 difference.</li> 3923+</ul> 3924+</li> 3925+<li><code>u</code> includes unchanged files in the per-file output lines.</li> 3926+</ul> 3927+</dd> 3928+ 3929+<dt><code>--check, -c</code></dt><dd> 3930+<p>Check the checksums (forcing the reading of all the files) and fix any 3931+issues that are found. Makes <code>--output</code> default to "dni".</p> 3932+</dd> 3933+ 3934+<dt><code>--clean</code></dt><dd> 3935+<p>Makes a temp-DB of all the inodes that we find in all the listed 3936+directories and removes any extraneous checksums from the DB. You will 3937+need to specify all the mounted directories that are present (and listed as 3938+mounted) in the DB on this host or else the checksums from the unvisited 3939+directories will be discarded from the DB. If you want to just -⁠-⁠clean 3940+without adding or updating the info of new or changed files, specify 3941+<code>--no-update</code> as well.</p> 3942+</dd> 3943+ 3944+<dt><code>--no-update, -N</code></dt><dd> 3945+<p>Avoids updating/adding info with <code>--check</code> and/or <code>--clean</code>.</p> 3946+</dd> 3947+ 3948+<dt><code>--quiet, -q</code></dt><dd> 3949+<p>Disable the default (non-error) output settings. This turns off the 3950+messages that <code>--init</code>, <code>--mount</code>, and <code>--clean</code> output, and makes the 3951+default for <code>--output</code> be nothing (though an explicit <code>--output</code> option is 3952+not affected).</p> 3953+</dd> 3954+ 3955+<dt><code>--init</code></dt><dd> 3956+<p>Create the tables in the DB. If it is used on an existing DB, all the 3957+existing tables are dropped and re-created.</p> 3958+</dd> 3959+</dl> 3960+<p>This option cannot be combined with the updating or reporting of checksum 3961+information, but may be combined with <code>--mounts</code>.</p> 3962+<dl> 3963+ 3964+<dt><code>--mounts</code></dt><dd> 3965+<p>Populate the "disk" DB with the available device numbers and change any 3966+mounted/unmount information for devices. This should be run every time a 3967+mount-change happens that may affect a directory hierarchy in the DB. 3968+Rsyncdb will not save any checksums for a device that is not listed in the 3969+"disk" table.</p> 3970+<p>The helper script "rsyncdb-mountinfo" is used as the source of the mount 3971+information on the host, which it derives from various system files and 3972+UUID directories (if available). That script supports the use of an 3973+override file named ".rsyncdb_mount_uniq" in the root of the mount as one 3974+way to manually assign unique values to a shared (mountable) device's 3975+various disks.</p> 3976+<p>Some advanced users may want to maintain the disk table themselves in order 3977+to support mounting a drive in different (or multiple) locations, etc.</p> 3978+<p>Specifying the <code>--mounts</code> option cannot be combined with updating or 3979+reporting of checksum information, but may be combined with <code>--init</code>.</p> 3980+</dd> 3981+ 3982+<dt><code>--help, -h</code></dt><dd> 3983+<p>Display a summary of the options.</p> 3984+</dd> 3985+</dl> 3986+<h1>SEE ALSO</h1> 3987+<p><strong>rsync</strong>(1)</p> 3988+<h1>AUTHOR</h1> 3989+<p>Rsyncdb was written by Wayne Davison.</p> 3990+<div style="float: right"><p><i>06 Aug 2020</i></p></div> 3991+</body></html> 3992