Annotation of embedaddon/rsync/patches/db.diff, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>