Annotation of embedaddon/rsync/patches/db.diff, revision 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 -⁠ Maintain an rsync checksum DB</p>
! 3794: +<h1>SYNOPSIS</h1>
! 3795: +<pre><code>rsyncdb --db=CONFIG [OPTION...] [DIR...]
! 3796: +</code></pre>
! 3797: +<h1>DESCRIPTION</h1>
! 3798: +<p>Rsyncdb can maintain a checksum-caching DB that rsync can use to make its
! 3799: +<code>--checksum</code> option more optimal. You must specify a config file via
! 3800: +the <code>--db=CONFIG_FILE</code> option in order for rsyncdb to know what DB to
! 3801: +manipulate. See the rsync manpage's <code>--db</code> option for full details on
! 3802: +the file's format.</p>
! 3803: +<p>You can specify one or more directory args for rsyncdb to scan. If no
! 3804: +DIR args are specified, the current directory is assumed to be the spot
! 3805: +to start scanning.</p>
! 3806: +<p>Note that the rsyncdb program is usually just a symlink to the rsync program.
! 3807: +You can force rsync to behave as rsyncdb either by having a symlink (or
! 3808: +hardlink) name that ends with "db" or by <code>starting</code> the rsync args with
! 3809: +<code>--db-only=CONFIG</code> (and that option works just like <code>--db=CONFIG</code> to
! 3810: +a program named rsyncdb).</p>
! 3811: +<h1>EXAMPLES</h1>
! 3812: +<p>The following command will update checksum information in the database
! 3813: +described in the /etc/db.conf file:</p>
! 3814: +<blockquote>
! 3815: +<pre><code>rsyncdb --db=/etc/db.conf -o n --clean /dir1 /dir2
! 3816: +</code></pre>
! 3817: +</blockquote>
! 3818: +<p>It scans 2 directory hierarchies (/dir1 & /dir2) and cleans out any
! 3819: +checksums whose inodes are no longer found in those directories (so that
! 3820: +directory args are presumed to be complete for this host's DB contents).</p>
! 3821: +<p>The following command will scan all the files in the /dir2 directory (without
! 3822: +recursive scanning, due to the <code>--no-r</code> option) and check them against
! 3823: +the DB:</p>
! 3824: +<blockquote>
! 3825: +<pre><code>rsyncdb --db=/etc/db.conf --check --no-r /dir2
! 3826: +</code></pre>
! 3827: +</blockquote>
! 3828: +<p>Any errors found are output as well as being fixed in the DB. (See
! 3829: +<code>--no-update</code> for how to check without updating.)</p>
! 3830: +<p>The following command will output MD5 sums for all the files found in the
! 3831: +directories mentioned, even if they are unchanged (due to the
! 3832: +<code>--output=us</code> option):</p>
! 3833: +<blockquote>
! 3834: +<pre><code>rsyncdb --db=/etc/db.conf -rous /dir* >/tmp/md5sums.txt
! 3835: +</code></pre>
! 3836: +</blockquote>
! 3837: +<p>This is just like running md5sum, only faster. Unlike md5sum, you can't
! 3838: +specify a single file, so use <code>--no-r</code> and grep the output if you just
! 3839: +want to see a single file's value.</p>
! 3840: +<p>The following command initializes a new DB, and is required for any new DB:</p>
! 3841: +<blockquote>
! 3842: +<pre><code>rsyncdb --db=/etc/db.conf --init --mounts
! 3843: +</code></pre>
! 3844: +</blockquote>
! 3845: +<p>The <code>--init</code> option should only be used once (unless you want to
! 3846: +destroy existing data). The <code>--mounts</code> option may need to be used
! 3847: +periodically, and makes use of a helper script (see below).</p>
! 3848: +<h1>OPTIONS SUMMARY</h1>
! 3849: +<p>Rsyncdb accepts the following options:</p>
! 3850: +<pre><code>--db=CONFIG Specify the CONFIG file to read for the DB info
! 3851: +--db-lax Ignore ctime changes (use with CAUTION)
! 3852: +--recursive, -r Scan files in subdirs (the default w/o --no-recursive)
! 3853: +--sums=SUMS, -s List which checksums to update (default: 4,5)
! 3854: +--output=STR, -o One or more letters of what to output (default: "")
! 3855: +--check, -c Check checksums (by reading the files) and fix any
! 3856: + issues. Makes --output default to "dni".
! 3857: +--clean Note all inodes in the DIRS and remove DB extras
! 3858: +--no-update, -N Avoids updating/adding info w/--check and/or --clean
! 3859: +--init Initialize a DB by (re-)creating its tables
! 3860: +--mounts Scan for mounted filesystems and update the DB
! 3861: +--quiet, -q Disable the default non-error output
! 3862: +--help, -h Display this help message
! 3863: +</code></pre>
! 3864: +<h1>OPTIONS</h1>
! 3865: +<p>Rsyncdb accepts both long (double-dash + word) and short (single-dash + letter)
! 3866: +options. The full list of the available options are described below. If an
! 3867: +option can be specified in more than one way, the choices are comma-separated.
! 3868: +Some options only have a long variant, not a short. If the option takes a
! 3869: +parameter, the parameter is only listed after the long variant, even though it
! 3870: +must also be specified for the short. When specifying a parameter, you can
! 3871: +either use the form -⁠-⁠option=param or replace the '=' with whitespace. The
! 3872: +parameter may need to be quoted in some manner for it to survive the shell's
! 3873: +command-line parsing.</p>
! 3874: +<dl>
! 3875: +
! 3876: +<dt><code>--db=CONFIG_FILE</code></dt><dd>
! 3877: +<p>This tells rsyncdb what DB-config file to read for the DB setup. This is
! 3878: +the same as the option in rsync, so refer to that manpage for full details.</p>
! 3879: +</dd>
! 3880: +
! 3881: +<dt><code>--db-lax</code></dt><dd>
! 3882: +<p>This option works just like it does in rsync, so refer to that manpage for
! 3883: +full details.</p>
! 3884: +</dd>
! 3885: +
! 3886: +<dt><code>--no-recursive, --no-r</code></dt><dd>
! 3887: +<p>This disables the default recursive directory scan that is performed on the
! 3888: +listed directory args. The options <code>--recursive</code> and <code>-r</code> are also
! 3889: +accepted, if someone wants to override an earlier <code>--no-r</code> override.</p>
! 3890: +</dd>
! 3891: +
! 3892: +<dt><code>--sums=SUMS, -s</code></dt><dd>
! 3893: +<p>Only output/update the listed checksum types. By default we deal with just
! 3894: +the newer md5 checksums (i.e. <code>--sums=5</code>).</p>
! 3895: +<p>Note that this option does NOT affect the order that checksums are output
! 3896: +if "-⁠o s" is enabled, so <code>-s5,4</code> is the same as <code>-s4,5</code>.</p>
! 3897: +</dd>
! 3898: +
! 3899: +<dt><code>--output=STR, -o</code></dt><dd>
! 3900: +<p>The output option lets you specify one or more letters indicating what
! 3901: +information should be output. If <code>--output</code> is not specified, the default
! 3902: +is either "dn" or (with <code>--check</code>) "dni".</p>
! 3903: +<p>The following letters are accepted in the string:</p>
! 3904: +<ul>
! 3905: +<li><code>d</code> outputs "... dir_name ..." lines for each directory in our scan. if
! 3906: +"d" is omitted, then this progress indictor is not output.</li>
! 3907: +<li><code>n</code> includes the file's name in the per-file output. These lines are only
! 3908: +output for changed files unless "u" is given. The "n" option is implied
! 3909: +by every other output option letter except "d".</li>
! 3910: +<li><code>s</code> includes the checksum info in the per-file output.</li>
! 3911: +<li><code>c</code> is a synonym for 's'.</li>
! 3912: +<li><code>i</code> includes itemized change info in the per-file output.
! 3913: +<ul>
! 3914: +<li><code>!i</code> indicates that the time and/or size is wrong.</li>
! 3915: +<li><code>+4</code> indicates the MD4 sum is missing.</li>
! 3916: +<li><code>+5</code> indicates the MD5 sum is missing.</li>
! 3917: +<li><code>!4</code> indicates the MD4 sum is wrong.</li>
! 3918: +<li><code>!5</code> indicates the MD5 sum is wrong.</li>
! 3919: +<li><code>?4</code> indicates an unknown MD4 difference. This can happen if we didn't
! 3920: +need to read the file; i.e. if the time/size is wrong and no sum info
! 3921: +was requested.</li>
! 3922: +<li><code>?5</code> indicates an unknown MD5 difference.</li>
! 3923: +</ul>
! 3924: +</li>
! 3925: +<li><code>u</code> includes unchanged files in the per-file output lines.</li>
! 3926: +</ul>
! 3927: +</dd>
! 3928: +
! 3929: +<dt><code>--check, -c</code></dt><dd>
! 3930: +<p>Check the checksums (forcing the reading of all the files) and fix any
! 3931: +issues that are found. Makes <code>--output</code> default to "dni".</p>
! 3932: +</dd>
! 3933: +
! 3934: +<dt><code>--clean</code></dt><dd>
! 3935: +<p>Makes a temp-DB of all the inodes that we find in all the listed
! 3936: +directories and removes any extraneous checksums from the DB. You will
! 3937: +need to specify all the mounted directories that are present (and listed as
! 3938: +mounted) in the DB on this host or else the checksums from the unvisited
! 3939: +directories will be discarded from the DB. If you want to just -⁠-⁠clean
! 3940: +without adding or updating the info of new or changed files, specify
! 3941: +<code>--no-update</code> as well.</p>
! 3942: +</dd>
! 3943: +
! 3944: +<dt><code>--no-update, -N</code></dt><dd>
! 3945: +<p>Avoids updating/adding info with <code>--check</code> and/or <code>--clean</code>.</p>
! 3946: +</dd>
! 3947: +
! 3948: +<dt><code>--quiet, -q</code></dt><dd>
! 3949: +<p>Disable the default (non-error) output settings. This turns off the
! 3950: +messages that <code>--init</code>, <code>--mount</code>, and <code>--clean</code> output, and makes the
! 3951: +default for <code>--output</code> be nothing (though an explicit <code>--output</code> option is
! 3952: +not affected).</p>
! 3953: +</dd>
! 3954: +
! 3955: +<dt><code>--init</code></dt><dd>
! 3956: +<p>Create the tables in the DB. If it is used on an existing DB, all the
! 3957: +existing tables are dropped and re-created.</p>
! 3958: +</dd>
! 3959: +</dl>
! 3960: +<p>This option cannot be combined with the updating or reporting of checksum
! 3961: +information, but may be combined with <code>--mounts</code>.</p>
! 3962: +<dl>
! 3963: +
! 3964: +<dt><code>--mounts</code></dt><dd>
! 3965: +<p>Populate the "disk" DB with the available device numbers and change any
! 3966: +mounted/unmount information for devices. This should be run every time a
! 3967: +mount-change happens that may affect a directory hierarchy in the DB.
! 3968: +Rsyncdb will not save any checksums for a device that is not listed in the
! 3969: +"disk" table.</p>
! 3970: +<p>The helper script "rsyncdb-mountinfo" is used as the source of the mount
! 3971: +information on the host, which it derives from various system files and
! 3972: +UUID directories (if available). That script supports the use of an
! 3973: +override file named ".rsyncdb_mount_uniq" in the root of the mount as one
! 3974: +way to manually assign unique values to a shared (mountable) device's
! 3975: +various disks.</p>
! 3976: +<p>Some advanced users may want to maintain the disk table themselves in order
! 3977: +to support mounting a drive in different (or multiple) locations, etc.</p>
! 3978: +<p>Specifying the <code>--mounts</code> option cannot be combined with updating or
! 3979: +reporting of checksum information, but may be combined with <code>--init</code>.</p>
! 3980: +</dd>
! 3981: +
! 3982: +<dt><code>--help, -h</code></dt><dd>
! 3983: +<p>Display a summary of the options.</p>
! 3984: +</dd>
! 3985: +</dl>
! 3986: +<h1>SEE ALSO</h1>
! 3987: +<p><strong>rsync</strong>(1)</p>
! 3988: +<h1>AUTHOR</h1>
! 3989: +<p>Rsyncdb was written by Wayne Davison.</p>
! 3990: +<div style="float: right"><p><i>06 Aug 2020</i></p></div>
! 3991: +</body></html>
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>