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