Fix failed hunks.
[rsync-patches.git] / db.diff
1 Added some DB-access routines to help rsync keep extra filesystem info
2 about the files it is dealing with.  This adds both the --db=CONFIG_FILE
3 option and the "db config" daemon parameter.
4
5 Future 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
17 To 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
24 based-on: dbb1c2d10c127b9a5f99e3e4ee5981f5222fc7e7
25 diff --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
36 diff --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
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         util1.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: version.h latest-year.h help-rsync.h help-rsyncd.h git-version.h default-cvsignore.h
89 +usage.o: version.h 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                 git-version.h rounding rounding.h *.old rsync*.1 rsync*.5 rsync*.html \
124                 daemon-parm.h help-*.h default-*.h proto.h proto.h-tstamp
125  
126 diff --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:
155 diff --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);
179 diff --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 @@ -843,6 +846,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  #if defined HAVE_SETENV || defined 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 @@ -1055,6 +1063,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)
219 diff --git a/configure.ac b/configure.ac
220 --- a/configure.ac
221 +++ b/configure.ac
222 @@ -10,6 +10,7 @@ AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \
223      unistd.h utime.h compat.h sys/param.h ctype.h sys/wait.h sys/stat.h \
224      sys/ioctl.h sys/filio.h string.h stdlib.h sys/socket.h sys/mode.h grp.h \
225      sys/un.h sys/attr.h arpa/inet.h arpa/nameser.h locale.h sys/types.h \
226 +    mysql/mysql.h sqlite3.h \
227      netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h mcheck.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 netgroup.h \
230 @@ -1474,6 +1475,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.])
279 diff --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
298 diff --git a/db.c b/db.c
299 new 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 +}
2246 diff --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  
2320 diff --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 @@ -624,6 +625,8 @@ int quick_check_ok(enum filetype ftype, const char *fn, struct file_struct *file
2332                 if (always_checksum > 0) {
2333                         char sum[MAX_DIGEST_LEN];
2334                         file_checksum(fn, st, sum);
2335 +                       if (!use_db || !db_get_checksum(st, sum))
2336 +                               file_checksum(fn, st, sum);
2337                         return memcmp(sum, F_SUM(file), flist_csum_len) == 0;
2338                 }
2339  
2340 @@ -2265,6 +2268,13 @@ void generate_files(int f_out, const char *local_name)
2341                         : "enabled");
2342         }
2343  
2344 +       if (use_db) {
2345 +               if (always_checksum || (append_mode != 1 && protocol_version >= 30))
2346 +                       db_connect(0); /* Will reset use_db on error. */
2347 +               else
2348 +                       use_db = 0;
2349 +       }
2350 +
2351         dflt_perms = (ACCESSPERMS & ~orig_umask);
2352  
2353         do {
2354 @@ -2390,6 +2400,9 @@ void generate_files(int f_out, const char *local_name)
2355                         wait_for_receiver();
2356         }
2357  
2358 +       if (use_db)
2359 +               db_disconnect(True);
2360 +
2361         info_levels[INFO_FLIST] = save_info_flist;
2362         info_levels[INFO_PROGRESS] = save_info_progress;
2363  
2364 diff --git a/io.c b/io.c
2365 --- a/io.c
2366 +++ b/io.c
2367 @@ -41,8 +41,10 @@ extern int am_server;
2368  extern int am_sender;
2369  extern int am_receiver;
2370  extern int am_generator;
2371 +extern int local_server;
2372  extern int msgs2stderr;
2373  extern int inc_recurse;
2374 +extern int same_db;
2375  extern int io_error;
2376  extern int batch_fd;
2377  extern int eol_nulls;
2378 @@ -1492,6 +1494,32 @@ static void read_a_msg(void)
2379                 if (am_sender)
2380                         maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
2381                 break;
2382 +       case MSG_CHECKSUM:
2383 +               /* This receives some checksum info that we want to make a note of
2384 +                * (which allows a single process to do all the writing to the db). */
2385 +               if (msg_bytes != MSG_CHECKSUM_LEN)
2386 +                       goto overflow;
2387 +               raw_read_buf(data, MSG_CHECKSUM_LEN);
2388 +               if (am_generator && same_db) {
2389 +                       iobuf.in_multiplexed = 1;
2390 +                       send_msg(MSG_CHECKSUM, data, MSG_CHECKSUM_LEN, 0);
2391 +               } if (am_receiver || (am_sender && !local_server))
2392 +                       goto unexpected;
2393 +               else {
2394 +                       /* The received data is a set of numbers followed by the checksum. */
2395 +                       STRUCT_STAT st;
2396 +                       st.st_dev = IVAL64(data, 0);
2397 +                       st.st_ino = IVAL64(data, 8);
2398 +                       st.st_size = IVAL64(data, 16);
2399 +                       st.st_mtime = IVAL64(data, 24);
2400 +                       st.st_ctime = IVAL64(data, 32);
2401 +#if MSG_CHECKSUM_LONGS != 5
2402 +#error Fix the parsing of checksum long values
2403 +#endif
2404 +                       iobuf.in_multiplexed = 1;
2405 +                       db_set_checksum(IVAL(data, MSG_CHECKSUM_LONGS*8), &st, data + MSG_CHECKSUM_LONGS*8 + 4);
2406 +               }
2407 +               break;
2408         case MSG_DELETED:
2409                 if (msg_bytes >= sizeof data)
2410                         goto overflow;
2411 @@ -1643,6 +1671,7 @@ static void read_a_msg(void)
2412                  * with a duplicate exit message. */
2413                 _exit_cleanup(val, __FILE__, 0 - __LINE__);
2414         default:
2415 +       unexpected:
2416                 rprintf(FERROR, "unexpected tag %d [%s%s]\n",
2417                         tag, who_am_i(), inc_recurse ? "/inc" : "");
2418                 exit_cleanup(RERR_STREAMIO);
2419 diff --git a/main.c b/main.c
2420 --- a/main.c
2421 +++ b/main.c
2422 @@ -39,6 +39,7 @@ extern int am_root;
2423  extern int am_server;
2424  extern int am_sender;
2425  extern int am_daemon;
2426 +extern int am_dbadmin;
2427  extern int inc_recurse;
2428  extern int blocking_io;
2429  extern int always_checksum;
2430 @@ -57,6 +58,7 @@ extern int copy_unsafe_links;
2431  extern int keep_dirlinks;
2432  extern int preserve_hard_links;
2433  extern int protocol_version;
2434 +extern int always_checksum;
2435  extern int mkpath_dest_arg;
2436  extern int file_total;
2437  extern int recurse;
2438 @@ -94,6 +96,7 @@ extern char *logfile_format;
2439  extern char *filesfrom_host;
2440  extern char *partial_dir;
2441  extern char *rsync_path;
2442 +extern char *db_config;
2443  extern char *shell_cmd;
2444  extern char *password_file;
2445  extern char *backup_dir;
2446 @@ -1245,6 +1248,9 @@ void start_server(int f_in, int f_out, int argc, char *argv[])
2447         if (am_daemon && io_timeout && protocol_version >= 31)
2448                 send_msg_int(MSG_IO_TIMEOUT, io_timeout);
2449  
2450 +       if (db_config)
2451 +               db_read_config(FERROR, db_config);
2452 +
2453         if (am_sender) {
2454                 keep_dirlinks = 0; /* Must be disabled on the sender. */
2455                 if (need_messages_from_generator)
2456 @@ -1535,6 +1541,9 @@ static int start_client(int argc, char *argv[])
2457         else
2458                 env_port = rsync_port;
2459  
2460 +       if (db_config)
2461 +               db_read_config(FERROR, db_config);
2462 +
2463         if (daemon_connection < 0)
2464                 return start_socket_client(shell_machine, remote_argc, remote_argv, argc, argv);
2465  
2466 diff --git a/options.c b/options.c
2467 --- a/options.c
2468 +++ b/options.c
2469 @@ -83,6 +83,7 @@ int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
2470  int am_server = 0;
2471  int am_sender = 0;
2472  int am_starting_up = 1;
2473 +int am_dbadmin = 0;
2474  int relative_paths = -1;
2475  int implied_dirs = 1;
2476  int missing_args = 0; /* 0 = FERROR_XFER, 1 = ignore, 2 = delete */
2477 @@ -96,6 +97,7 @@ int use_qsort = 0;
2478  char *files_from = NULL;
2479  int filesfrom_fd = -1;
2480  char *filesfrom_host = NULL;
2481 +char *db_config = NULL;
2482  int eol_nulls = 0;
2483  int protect_args = -1;
2484  int human_readable = 1;
2485 @@ -104,6 +106,9 @@ int mkpath_dest_arg = 0;
2486  int allow_inc_recurse = 1;
2487  int xfer_dirs = -1;
2488  int am_daemon = 0;
2489 +int db_clean, db_check, db_do_md4, db_do_md5, db_update = 1, db_lax, db_init, db_mounts;
2490 +int db_output_name, db_output_sum, db_output_info, db_output_unchanged, db_output_dirs, db_output_msgs;
2491 +int saw_db_output_opt, saw_db_sum_opt;
2492  int connect_timeout = 0;
2493  int keep_partial = 0;
2494  int safe_symlinks = 0;
2495 @@ -282,6 +287,7 @@ static struct output_struct debug_words[COUNT_DEBUG+1] = {
2496         DEBUG_WORD(CHDIR, W_CLI|W_SRV, "Debug when the current directory changes"),
2497         DEBUG_WORD(CONNECT, W_CLI, "Debug connection events (levels 1-2)"),
2498         DEBUG_WORD(CMD, W_CLI, "Debug commands+options that are issued (levels 1-2)"),
2499 +       DEBUG_WORD(DB, W_SND|W_REC, "Debug DB operations (levels 1-5)"),
2500         DEBUG_WORD(DEL, W_REC, "Debug delete actions (levels 1-3)"),
2501         DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"),
2502         DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"),
2503 @@ -573,6 +579,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
2504        OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
2505        OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
2506        OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE,
2507 +      OPT_NO_DB, OPT_DBONLY,
2508        OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR,
2509        OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS,
2510        OPT_STOP_AFTER, OPT_STOP_AT,
2511 @@ -729,6 +736,10 @@ static struct poptOption long_options[] = {
2512    {"no-c",             0,  POPT_ARG_VAL,    &always_checksum, 0, 0, 0 },
2513    {"checksum-choice",  0,  POPT_ARG_STRING, &checksum_choice, 0, 0, 0 },
2514    {"cc",               0,  POPT_ARG_STRING, &checksum_choice, 0, 0, 0 },
2515 +  {"db",               0,  POPT_ARG_STRING, &db_config, 0, 0, 0 },
2516 +  {"no-db",            0,  POPT_ARG_NONE,   0, OPT_NO_DB, 0, 0 },
2517 +  {"db-lax",           0,  POPT_ARG_VAL,    &db_lax, 1, 0, 0 },
2518 +  {"no-db-lax",        0,  POPT_ARG_VAL,    &db_lax, 0, 0, 0 },
2519    {"block-size",      'B', POPT_ARG_STRING, 0, OPT_BLOCK_SIZE, 0, 0 },
2520    {"compare-dest",     0,  POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
2521    {"copy-dest",        0,  POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
2522 @@ -825,6 +836,9 @@ static struct poptOption long_options[] = {
2523    {"dparam",           0,  POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
2524    {"detach",           0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
2525    {"no-detach",        0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
2526 +  /* All the following options switch us into DB-admin option-parsing. */
2527 +  {"db-help",          0,  POPT_ARG_NONE,   0, OPT_DBONLY, 0, 0 },
2528 +  {"db-only",          0,  POPT_ARG_STRING, 0, OPT_DBONLY, 0, 0 },
2529    {0,0,0,0, 0, 0, 0}
2530  };
2531  
2532 @@ -853,6 +867,31 @@ static struct poptOption long_daemon_options[] = {
2533    {0,0,0,0, 0, 0, 0}
2534  };
2535  
2536 +static struct poptOption long_dbonly_options[] = {
2537 +  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
2538 +  {"check",           'c', POPT_ARG_NONE,   &db_check, 0, 0, 0},
2539 +  {"clean",            0,  POPT_ARG_NONE,   &db_clean, 0, 0, 0},
2540 +  {"db",               0,  POPT_ARG_STRING, &db_config, 0, 0, 0 },
2541 +  {"db-only",          0,  POPT_ARG_STRING, &db_config, 0, 0, 0 },
2542 +  {"db-lax",           0,  POPT_ARG_VAL,    &db_lax, 1, 0, 0 },
2543 +  {"no-db-lax",        0,  POPT_ARG_VAL,    &db_lax, 0, 0, 0 },
2544 +  {"info",             0,  POPT_ARG_STRING, 0, OPT_INFO, 0, 0 },
2545 +  {"debug",            0,  POPT_ARG_STRING, 0, OPT_DEBUG, 0, 0 },
2546 +  {"update",          'u', POPT_ARG_VAL,    &db_update, 1, 0, 0 },
2547 +  {"no-update",       'N', POPT_ARG_VAL,    &db_update, 0, 0, 0 },
2548 +  {"no-u",             0,  POPT_ARG_VAL,    &db_update, 0, 0, 0 },
2549 +  {"output",          'o', POPT_ARG_STRING, 0, 'o', 0, 0 },
2550 +  {"recursive",       'r', POPT_ARG_VAL,    &recurse, 1, 0, 0 },
2551 +  {"no-recursive",     0,  POPT_ARG_VAL,    &recurse, 0, 0, 0 },
2552 +  {"no-r",             0,  POPT_ARG_VAL,    &recurse, 0, 0, 0 },
2553 +  {"sums",            's', POPT_ARG_STRING, 0, 's', 0, 0 },
2554 +  {"init",             0,  POPT_ARG_NONE,   &db_init, 0, 0, 0 },
2555 +  {"mounts",           0,  POPT_ARG_NONE,   &db_mounts, 0, 0, 0 },
2556 +  {"quiet",           'q', POPT_ARG_NONE,   &quiet, 0, 0, 0 },
2557 +  {"help",            'h', POPT_ARG_NONE,   0, 'h', 0, 0 },
2558 +  {"db-help",          0,  POPT_ARG_NONE,   0, 'h', 0, 0 },
2559 +  {0,0,0,0, 0, 0, 0}
2560 +};
2561  
2562  static char err_buf[200];
2563  
2564 @@ -978,6 +1017,8 @@ static void set_refuse_options(void)
2565                         parse_one_refuse_match(0, "iconv", list_end);
2566  #endif
2567                 parse_one_refuse_match(0, "log-file*", list_end);
2568 +               parse_one_refuse_match(0, "db", list_end);
2569 +               parse_one_refuse_match(0, "db-lax", list_end);
2570         }
2571  
2572  #ifndef SUPPORT_ATIMES
2573 @@ -1285,6 +1326,102 @@ static void create_refuse_error(int which)
2574                 snprintf(err_buf + n, sizeof err_buf - n, " (-%c)\n", op->shortName);
2575  }
2576  
2577 +static NORETURN void parse_dbonly_args(int argc, const char **argv)
2578 +{
2579 +       poptContext pc = poptGetContext(RSYNC_NAME, argc, argv, long_dbonly_options, 0);
2580 +       const char *arg;
2581 +       int opt;
2582 +
2583 +       recurse = 1;
2584 +       am_dbadmin = 1;
2585 +
2586 +       while ((opt = poptGetNextOpt(pc)) != -1) {
2587 +               const char *cp;
2588 +               switch (opt) {
2589 +               case 'o':
2590 +                       for (cp = poptGetOptArg(pc); *cp; cp++) {
2591 +                               switch (toLower(cp)) {
2592 +                               case 'n':
2593 +                                       db_output_name = 1;
2594 +                                       break;
2595 +                               case 's':
2596 +                               case 'c':
2597 +                                       db_output_sum = db_output_name = 1;
2598 +                                       break;
2599 +                               case 'i':
2600 +                                       db_output_info = db_output_name = 1;
2601 +                                       break;
2602 +                               case 'u':
2603 +                                       db_output_unchanged = db_output_name = 1;
2604 +                                       break;
2605 +                               case 'd':
2606 +                                       db_output_dirs = 1;
2607 +                                       break;
2608 +                               }
2609 +                       }
2610 +                       saw_db_output_opt = 1;
2611 +                       break;
2612 +
2613 +               case 's':
2614 +                       for (cp = poptGetOptArg(pc); *cp; cp++) {
2615 +                               switch (*cp) {
2616 +                               case '4':
2617 +                                       db_do_md4 = 1;
2618 +                                       break;
2619 +                               case '5':
2620 +                                       db_do_md5 = 1;
2621 +                                       break;
2622 +                               }
2623 +                       }
2624 +                       saw_db_sum_opt = 1;
2625 +                       break;
2626 +
2627 +               case 'h':
2628 +                       dbonly_usage(FINFO);
2629 +                       exit_cleanup(0);
2630 +
2631 +               case OPT_INFO:
2632 +                       arg = poptGetOptArg(pc);
2633 +                       parse_output_words(info_words, info_levels, arg, USER_PRIORITY);
2634 +                       break;
2635 +
2636 +               case OPT_DEBUG:
2637 +                       arg = poptGetOptArg(pc);
2638 +                       parse_output_words(debug_words, debug_levels, arg, USER_PRIORITY);
2639 +                       break;
2640 +
2641 +               default:
2642 +                       rprintf(FERROR,
2643 +                               "rsyncdb: %s: %s\n",
2644 +                               poptBadOption(pc, POPT_BADOPTION_NOALIAS),
2645 +                               poptStrerror(opt));
2646 +                       goto dbonly_usage;
2647 +               }
2648 +       }
2649 +
2650 +       if (!db_config) {
2651 +               rprintf(FERROR, "You must specify the --db=FILE option.\n");
2652 +         dbonly_usage:
2653 +               rprintf(FERROR,
2654 +                       "(Type \"rsyncdb --help\" for assistance.)\n");
2655 +               exit_cleanup(RERR_SYNTAX);
2656 +       }
2657 +
2658 +       if (!saw_db_output_opt && !quiet) {
2659 +               db_output_dirs = db_output_name = 1;
2660 +               if (db_check)
2661 +                       db_output_info = 1;
2662 +       }
2663 +       if (!quiet)
2664 +               db_output_msgs = 1;
2665 +       if (!saw_db_sum_opt)
2666 +               db_do_md5 = 1;
2667 +
2668 +       am_starting_up = 0;
2669 +       run_dbonly(poptGetArgs(pc));
2670 +       exit(42); /* NOT REACHED */
2671 +}
2672 +
2673  /* This is used to make sure that --daemon & --server cannot be aliased to
2674   * something else. These options have always disabled popt aliases for the
2675   * parsing of a daemon or server command-line, but we have to make sure that
2676 @@ -1341,6 +1478,12 @@ int parse_arguments(int *argc_p, const char ***argv_p)
2677                 return 0;
2678         }
2679  
2680 +       arg = *argv + strlen(*argv);
2681 +       if (arg - *argv > 2 && strcmp(arg-2, "db") == 0) {
2682 +               parse_dbonly_args(argc, argv);
2683 +               /* NOT REACHED */
2684 +       }
2685 +
2686         set_refuse_options();
2687  
2688  #ifdef ICONV_OPTION
2689 @@ -1459,6 +1602,12 @@ int parse_arguments(int *argc_p, const char ***argv_p)
2690                         am_daemon = 1;
2691                         return 1;
2692  
2693 +               case OPT_DBONLY:
2694 +                       protect_args = 0;
2695 +                       poptFreeContext(pc);
2696 +                       parse_dbonly_args(argc, argv);
2697 +                       break; /* NOT REACHED */
2698 +
2699                 case OPT_MODIFY_WINDOW:
2700                         /* The value has already been set by popt, but
2701                          * we need to remember that we're using a
2702 @@ -1531,6 +1680,10 @@ int parse_arguments(int *argc_p, const char ***argv_p)
2703                         preserve_devices = preserve_specials = 0;
2704                         break;
2705  
2706 +               case OPT_NO_DB:
2707 +                       db_config = NULL;
2708 +                       break;
2709 +
2710                 case 'h':
2711                         human_readable++;
2712                         break;
2713 diff --git a/pipe.c b/pipe.c
2714 --- a/pipe.c
2715 +++ b/pipe.c
2716 @@ -27,11 +27,16 @@ extern int am_server;
2717  extern int blocking_io;
2718  extern int filesfrom_fd;
2719  extern int munge_symlinks;
2720 +extern int always_checksum;
2721 +extern int use_db;
2722 +extern char *db_config;
2723  extern char *logfile_name;
2724  extern int remote_option_cnt;
2725  extern const char **remote_options;
2726  extern struct chmod_mode_struct *chmod_modes;
2727  
2728 +int same_db = 0;
2729 +
2730  /**
2731   * Create a child connected to us via its stdin/stdout.
2732   *
2733 @@ -141,13 +146,22 @@ pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
2734                 }
2735  
2736                 if (remote_option_cnt) {
2737 +                       const char *db_config_save = db_config;
2738                         int rc = remote_option_cnt + 1;
2739                         const char **rv = remote_options;
2740                         if (!parse_arguments(&rc, &rv)) {
2741                                 option_error();
2742                                 exit_cleanup(RERR_SYNTAX);
2743                         }
2744 -               }
2745 +                       if (db_config == db_config_save)
2746 +                               same_db = db_config != NULL;
2747 +                       else if (!db_config || !db_config_save || strcmp(db_config, db_config_save) != 0) {
2748 +                               use_db = 0;
2749 +                               if (db_config)
2750 +                                       db_read_config(FERROR, db_config);
2751 +                       }
2752 +               } else if (use_db)
2753 +                       same_db = 1;
2754  
2755                 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0
2756                  || close(to_child_pipe[1]) < 0
2757 diff --git a/receiver.c b/receiver.c
2758 --- a/receiver.c
2759 +++ b/receiver.c
2760 @@ -24,6 +24,8 @@
2761  
2762  extern int dry_run;
2763  extern int do_xfers;
2764 +extern int use_db;
2765 +extern int db_lax;
2766  extern int am_root;
2767  extern int am_server;
2768  extern int inc_recurse;
2769 @@ -433,6 +435,11 @@ static void handle_delayed_updates(char *local_name)
2770                                         "rename failed for %s (from %s)",
2771                                         full_fname(fname), partialptr);
2772                         } else {
2773 +                               if (use_db && !db_lax) {
2774 +                                       STRUCT_STAT st;
2775 +                                       if (do_lstat(fname, &st) == 0)
2776 +                                               db_update_ctime(5, &st);
2777 +                               }
2778                                 if (remove_source_files
2779                                  || (preserve_hard_links && F_IS_HLINKED(file)))
2780                                         send_msg_int(MSG_SUCCESS, ndx);
2781 @@ -542,6 +549,9 @@ int recv_files(int f_in, int f_out, char *local_name)
2782         if (whole_file < 0)
2783                 whole_file = 0;
2784  
2785 +       if (use_db && (append_mode == 1 || protocol_version < 30))
2786 +               use_db = 0; /* We can't note finished md5 values */
2787 +
2788         progress_init();
2789  
2790         while (1) {
2791 @@ -881,6 +891,8 @@ int recv_files(int f_in, int f_out, char *local_name)
2792                                         do_unlink(partialptr);
2793                                 handle_partial_dir(partialptr, PDIR_DELETE);
2794                         }
2795 +                       if (use_db && do_lstat(fname, &st) == 0)
2796 +                               db_set_checksum(5, &st, sender_file_sum);
2797                 } else if (keep_partial && partialptr && !one_inplace) {
2798                         if (!handle_partial_dir(partialptr, PDIR_CREATE)) {
2799                                 rprintf(FERROR,
2800 @@ -894,6 +906,8 @@ int recv_files(int f_in, int f_out, char *local_name)
2801                                 recv_ok = -1;
2802                         else if (delay_updates && recv_ok) {
2803                                 bitbag_set_bit(delayed_bits, ndx);
2804 +                               if (use_db && do_lstat(partialptr, &st) == 0)
2805 +                                       db_set_checksum(5, &st, sender_file_sum);
2806                                 recv_ok = 2;
2807                         } else
2808                                 partialptr = NULL;
2809 diff --git a/rsync.1.md b/rsync.1.md
2810 --- a/rsync.1.md
2811 +++ b/rsync.1.md
2812 @@ -384,6 +384,9 @@ detailed description below for a complete description.
2813  --dry-run, -n            perform a trial run with no changes made
2814  --whole-file, -W         copy files whole (w/o delta-xfer algorithm)
2815  --checksum-choice=STR    choose the checksum algorithm (aka --cc)
2816 +--db=CONFIG_FILE         specify a CONFIG_FILE for DB checksums
2817 +--db-only=CONFIG_FILE    behave like rsyncdb
2818 +--db-lax                 ignore ctime changes (use with CAUTION)
2819  --one-file-system, -x    don't cross filesystem boundaries
2820  --block-size=SIZE, -B    force a fixed checksum block-size
2821  --rsh=COMMAND, -e        specify the remote shell to use
2822 diff --git a/rsync.c b/rsync.c
2823 --- a/rsync.c
2824 +++ b/rsync.c
2825 @@ -39,6 +39,7 @@ extern int am_daemon;
2826  extern int am_sender;
2827  extern int am_receiver;
2828  extern int am_generator;
2829 +extern int am_dbadmin;
2830  extern int am_starting_up;
2831  extern int allow_8bit_chars;
2832  extern int protocol_version;
2833 @@ -807,6 +808,8 @@ struct file_list *flist_for_ndx(int ndx, const char *fatal_error_loc)
2834  
2835  const char *who_am_i(void)
2836  {
2837 +       if (am_dbadmin)
2838 +               return "rsyncdb";
2839         if (am_starting_up)
2840                 return am_server ? "server" : "client";
2841         return am_sender ? "sender"
2842 diff --git a/rsync.h b/rsync.h
2843 --- a/rsync.h
2844 +++ b/rsync.h
2845 @@ -271,6 +271,7 @@ enum msgcode {
2846         MSG_IO_ERROR=22,/* the sending side had an I/O error */
2847         MSG_IO_TIMEOUT=33,/* tell client about a daemon's timeout value */
2848         MSG_NOOP=42,    /* a do-nothing message (legacy protocol-30 only) */
2849 +       MSG_CHECKSUM=55,/* sent via rcvr -> gen pipe and local-host-only gen -> sender */
2850         MSG_ERROR_EXIT=86, /* synchronize an error exit (siblings and protocol >= 31) */
2851         MSG_SUCCESS=100,/* successfully updated indicated flist index */
2852         MSG_DELETED=101,/* successfully deleted a file on receiving side */
2853 @@ -281,6 +282,9 @@ enum filetype {
2854         FT_UNSUPPORTED, FT_REG, FT_DIR, FT_SYMLINK, FT_SPECIAL, FT_DEVICE
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 @@ -1432,7 +1436,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)
2873 diff --git a/rsyncdb-mountinfo b/rsyncdb-mountinfo
2874 new 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";
2960 diff --git a/rsyncdb.1.md b/rsyncdb.1.md
2961 new 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.
3182 diff --git a/usage.c b/usage.c
3183 --- a/usage.c
3184 +++ b/usage.c
3185 @@ -137,6 +137,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 @@ -256,6 +266,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  #ifdef RSYNC_GITVER