File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / db.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:32:36 2021 UTC (3 years, 3 months ago) by misho
Branches: rsync, MAIN
CVS tags: v3_2_3, HEAD
rsync 3.2.3

    1: /*
    2:  * Routines to access extended file info via DB.
    3:  *
    4:  * Copyright (C) 2008-2013 Wayne Davison
    5:  *
    6:  * This program is free software; you can redistribute it and/or modify
    7:  * it under the terms of the GNU General Public License as published by
    8:  * the Free Software Foundation; either version 3 of the License, or
    9:  * (at your option) any later version.
   10:  *
   11:  * This program is distributed in the hope that it will be useful,
   12:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14:  * GNU General Public License for more details.
   15:  *
   16:  * You should have received a copy of the GNU General Public License along
   17:  * with this program; if not, visit the http://fsf.org website.
   18:  */
   19: 
   20: #include "rsync.h"
   21: #include "ifuncs.h"
   22: #include "itypes.h"
   23: #include "inums.h"
   24: #ifdef USE_OPENSSL
   25: #include "openssl/md4.h"
   26: #include "openssl/md5.h"
   27: #endif
   28: 
   29: extern int recurse;
   30: extern int same_db;
   31: extern int am_receiver;
   32: extern int am_generator;
   33: extern int checksum_type;
   34: extern int db_clean, db_check, db_do_md4, db_do_md5, db_update, db_lax, db_init, db_mounts;
   35: extern int db_output_name, db_output_sum, db_output_info, db_output_unchanged, db_output_dirs, db_output_msgs;
   36: extern int saw_db_output_opt, saw_db_sum_opt;
   37: extern char *db_config;
   38: 
   39: #define MOUNT_HELPER_SCRIPT "/usr/sbin/rsyncdb-mountinfo"
   40: 
   41: #if defined HAVE_MYSQL_MYSQL_H && defined HAVE_LIBMYSQLCLIENT
   42: #define USE_MYSQL
   43: #include <mysql/mysql.h>
   44: #include <mysql/errmsg.h>
   45: #endif
   46: 
   47: #if defined HAVE_SQLITE3_H && defined HAVE_LIBSQLITE3
   48: #define USE_SQLITE
   49: #include <sqlite3.h>
   50: #ifndef HAVE_SQLITE3_OPEN_V2
   51: #define sqlite3_open_v2(dbname, dbhptr, flags, vfs) \
   52: 	sqlite3_open(dbname, dbhptr)
   53: #endif
   54: #ifndef HAVE_SQLITE3_PREPARE_V2
   55: #define sqlite3_prepare_v2 sqlite3_prepare
   56: #endif
   57: #define MAX_LOCK_FAILURES 10
   58: #define LOCK_FAIL_MSLEEP 100
   59: #endif
   60: 
   61: #ifndef USE_OPENSSL
   62: #define MD5_CTX md_context
   63: #define MD5_Init md5_begin
   64: #define MD5_Update md5_update
   65: #define MD5_Final(digest, cptr) md5_result(cptr, digest)
   66: #endif
   67: 
   68: #define DB_TYPE_NONE 0
   69: #define DB_TYPE_MYSQL 1
   70: #define DB_TYPE_SQLITE 2
   71: 
   72: int use_db = DB_TYPE_NONE;
   73: int select_many_sums = 0;
   74: 
   75: #define PREP_NORM 0
   76: #define PREP_MOUNT 1
   77: 
   78: static const char *dbhost = NULL, *dbuser = NULL, *dbpass = NULL, *dbname = NULL;
   79: static unsigned int dbport = 0;
   80: static int transaction_state = -1;
   81: 
   82: static union {
   83: #ifdef USE_MYSQL
   84:     MYSQL *mysql;
   85: #endif
   86: #ifdef USE_SQLITE
   87:     sqlite3 *sqlite;
   88: #endif
   89:     void *all;
   90: } dbh;
   91: 
   92: #define SEL_DEV 0
   93: #define SEL_SUM 1
   94: #define REP_SUM 2
   95: #define UPD_CTIME 3
   96: #define INS_MOUNT 4
   97: #define UPD_MOUNT 5 /* SQLite only */
   98: #define SEL_MOUNT 6
   99: #define UN_MOUNT 7
  100: #define DEL_SUMS 8
  101: #define INS_PRESENT 9
  102: #define MAX_PREP_CNT 10
  103: 
  104: #define MAX_BIND_CNT 7
  105: #define MAX_RESULT_BINDS 32
  106: 
  107: static union {
  108: #ifdef USE_MYSQL
  109:     MYSQL_STMT *mysql;
  110: #endif
  111: #ifdef USE_SQLITE
  112:     sqlite3_stmt *sqlite;
  113: #endif
  114:     void *all;
  115: } statements[MAX_PREP_CNT];
  116: 
  117: static int md_num;
  118: static enum logcode log_code;
  119: 
  120: #ifdef USE_MYSQL
  121: static unsigned int bind_disk_id, bind_mdnum;
  122: static int64 bind_devno, bind_ino, bind_size, bind_mtime, bind_ctime;
  123: static char bind_sum[MAX_DIGEST_LEN];
  124: static unsigned long result_length[MAX_RESULT_BINDS];
  125: static bool result_is_null[MAX_RESULT_BINDS], result_error[MAX_RESULT_BINDS];
  126: #elif defined USE_SQLITE
  127: static int64 bind_mtime;
  128: #endif
  129: static char bind_thishost[128+1];
  130: static unsigned long bind_thishost_len;
  131: static char *mount_helper_script = NULL;
  132: 
  133: static char *error_log;
  134: #if defined USE_SQLITE && defined SQLITE_CONFIG_LOG
  135: static char bind_mount_uniq[128+1];
  136: static unsigned long bind_mount_uniq_len;
  137: static FILE *error_log_fp;
  138: #endif
  139: 
  140: #define PTR_SIZE (sizeof (struct file_struct *))
  141: 
  142: #if defined USE_MYSQL || defined USE_SQLITE
  143: static void update_mounts(void);
  144: #endif
  145: 
  146: struct name_list {
  147: 	struct name_list *next;
  148: 	char name[1];
  149: } *dirs_list;
  150: 
  151: int db_read_config(enum logcode code, const char *config_file)
  152: {
  153: 	char buf[2048], *cp;
  154: 	FILE *fp;
  155: 	int lineno = 0;
  156: 
  157: 	log_code = code;
  158: 
  159: 	bind_thishost_len = strlcpy(bind_thishost, "localhost", sizeof bind_thishost);
  160: 
  161: 	if (!(fp = fopen(config_file, "r"))) {
  162: 		rsyserr(log_code, errno, "unable to open %s", config_file);
  163: 		return 0;
  164: 	}
  165: 	if (DEBUG_GTE(DB, 1))
  166: 		rprintf(FCLIENT, "[%s] Reading DB config from %s\n", who_am_i(), config_file);
  167: 	while (fgets(buf, sizeof buf, fp)) {
  168: 		lineno++;
  169: 		if ((cp = strchr(buf, '#')) == NULL
  170: 		 && (cp = strchr(buf, '\r')) == NULL
  171: 		 && (cp = strchr(buf, '\n')) == NULL)
  172: 			cp = buf + strlen(buf);
  173: 		while (cp != buf && isSpace(cp-1)) cp--;
  174: 		*cp = '\0';
  175: 
  176: 		if (!*buf)
  177: 			continue;
  178: 
  179: 		if (!(cp = strchr(buf, ':')))
  180: 			goto invalid_line;
  181: 		*cp++ = '\0';
  182: 
  183: 		while (isSpace(cp)) cp++;
  184: 		if (strcasecmp(buf, "dbhost") == 0)
  185: 			dbhost = strdup(cp);
  186: 		else if (strcasecmp(buf, "dbuser") == 0)
  187: 			dbuser = strdup(cp);
  188: 		else if (strcasecmp(buf, "dbpass") == 0)
  189: 			dbpass = strdup(cp);
  190: 		else if (strcasecmp(buf, "dbname") == 0)
  191: 			dbname = strdup(cp);
  192: 		else if (strcasecmp(buf, "dbport") == 0)
  193: 			dbport = atoi(cp);
  194: 		else if (strcasecmp(buf, "transaction") == 0)
  195: 			transaction_state = atoi(cp) ? 0 : -1;
  196: 		else if (strcasecmp(buf, "mountHelper") == 0)
  197: 			mount_helper_script = strdup(cp);
  198: 		else if (strcasecmp(buf, "errlog") == 0)
  199: 			error_log = strdup(cp);
  200: 		else if (strcasecmp(buf, "thishost") == 0)
  201: 			bind_thishost_len = strlcpy(bind_thishost, cp, sizeof bind_thishost);
  202: 		else if (strcasecmp(buf, "dbtype") == 0) {
  203: #ifdef USE_MYSQL
  204: 			if (strcasecmp(cp, "mysql") == 0) {
  205: 				use_db = DB_TYPE_MYSQL;
  206: 				continue;
  207: 			}
  208: #endif
  209: #ifdef USE_SQLITE
  210: 			if (strcasecmp(cp, "sqlite") == 0) {
  211: 				use_db = DB_TYPE_SQLITE;
  212: 				continue;
  213: 			}
  214: #endif
  215: 			rprintf(log_code,
  216: 			    "Unsupported dbtype on line #%d in %s.\n",
  217: 			    lineno, config_file);
  218: 			use_db = DB_TYPE_NONE;
  219: 			return 0;
  220: 		} else {
  221: 		  invalid_line:
  222: 			rprintf(log_code, "Invalid line #%d in %s\n",
  223: 				lineno, config_file);
  224: 			use_db = DB_TYPE_NONE;
  225: 			return 0;
  226: 		}
  227: 	}
  228: 	fclose(fp);
  229: 
  230: 	if (bind_thishost_len >= (int)sizeof bind_thishost)
  231: 		bind_thishost_len = sizeof bind_thishost - 1;
  232: 
  233: 	if (!use_db || !dbname) {
  234: 		rprintf(log_code, "Please specify at least dbtype and dbname in %s.\n", config_file);
  235: 		use_db = DB_TYPE_NONE;
  236: 		return 0;
  237: 	}
  238: 
  239: 	md_num = checksum_type == 5 ? 5 : 4;
  240: 
  241: 	if (error_log) {
  242: 		if (use_db != DB_TYPE_SQLITE)
  243: 			rprintf(log_code, "Ignoring errlog setting for non-SQLite DB.\n");
  244: #ifndef SQLITE_CONFIG_LOG
  245: 		else
  246: 			rprintf(log_code, "Your sqlite doesn't support SQLITE_CONFIG_LOG.\n");
  247: #endif
  248: 	}
  249: 
  250: 	if (!mount_helper_script)
  251: 		mount_helper_script = MOUNT_HELPER_SCRIPT;
  252: 
  253: 	return 1;
  254: }
  255: 
  256: #if defined USE_SQLITE && defined SQLITE_CONFIG_LOG
  257: static void errorLogCallback(UNUSED(void *pArg), int iErrCode, const char *zMsg)
  258: {
  259: 	fprintf(error_log_fp, "[%d] %s (%d)\n", (int)getpid(), zMsg, iErrCode);
  260: }
  261: #endif
  262: 
  263: static int run_sql(const char *fmt, ...)
  264: {
  265: 	va_list ap;
  266: 	char *query;
  267: 	int ok = 0, qlen;
  268: 
  269: 	va_start(ap, fmt);
  270: 	qlen = vasprintf(&query, fmt, ap);
  271: 	va_end(ap);
  272: 	if (qlen < 0)
  273: 		out_of_memory("run_sql");
  274: 	if (DEBUG_GTE(DB, 3))
  275: 		rprintf(FCLIENT, "[%s] SQL being run: %s\n", who_am_i(), query);
  276: 
  277: 	switch (use_db) {
  278: #ifdef USE_MYSQL
  279: 	case DB_TYPE_MYSQL:
  280: 		if (mysql_query(dbh.mysql, query) < 0) {
  281: 			rprintf(FERROR, "Failed to run sql: %s\n", mysql_error(dbh.mysql));
  282: 			rprintf(FERROR, "%s\n", query);
  283: 		} else
  284: 			ok = 1;
  285: 		break;
  286: #endif
  287: #ifdef USE_SQLITE
  288: 	case DB_TYPE_SQLITE: {
  289: 		int rc, lock_failures = 0;
  290: 		while (1) {
  291: 			if ((rc = sqlite3_exec(dbh.sqlite, query, NULL, NULL, NULL)) == 0)
  292: 				break;
  293: 			if (rc != SQLITE_BUSY && rc != SQLITE_LOCKED)
  294: 				break;
  295: 			if (++lock_failures > MAX_LOCK_FAILURES)
  296: 				break;
  297: 			msleep(LOCK_FAIL_MSLEEP);
  298: 		}
  299: 		if (rc) {
  300: 			rprintf(FERROR, "[%s] Failed to run sql: %s\n", who_am_i(), sqlite3_errmsg(dbh.sqlite));
  301: 			rprintf(FERROR, "%s\n", query);
  302: 		} else
  303: 			ok = 1;
  304: 		break;
  305: 	    }
  306: #endif
  307: 	}
  308: 
  309: 	free(query);
  310: 
  311: 	return ok;
  312: }
  313: 
  314: #ifdef USE_MYSQL
  315: static int prepare_mysql(int ndx, MYSQL_BIND *binds, int bind_cnt, const char *fmt, ...)
  316: {
  317: 	va_list ap;
  318: 	char *query;
  319: 	int qlen, param_cnt;
  320: 	MYSQL_STMT *stmt = mysql_stmt_init(dbh.mysql);
  321: 
  322: 	if (stmt == NULL)
  323: 		out_of_memory("prepare_mysql");
  324: 
  325: 	va_start(ap, fmt);
  326: 	qlen = vasprintf(&query, fmt, ap);
  327: 	va_end(ap);
  328: 	if (qlen < 0)
  329: 		out_of_memory("prepare_mysql");
  330: 	if (DEBUG_GTE(DB, 3))
  331: 		rprintf(FCLIENT, "[%s] SQL being prepared: %s\n", who_am_i(), query);
  332: 
  333: 	if (mysql_stmt_prepare(stmt, query, qlen) != 0) {
  334: 		rprintf(log_code, "[%s] Prepare failed: %s\n", who_am_i(), mysql_stmt_error(stmt));
  335: 		rprintf(log_code, "%s\n", query);
  336: 		free(query);
  337: 		return 0;
  338: 	}
  339: 
  340: 	if ((param_cnt = mysql_stmt_param_count(stmt)) != bind_cnt) {
  341: 		rprintf(log_code, "[%s] Parameters in statement = %d, bind vars = %d\n",
  342: 			who_am_i(), param_cnt, bind_cnt);
  343: 		rprintf(log_code, "%s\n", query);
  344: 		free(query);
  345: 		return 0;
  346: 	}
  347: 	if (bind_cnt)
  348: 		mysql_stmt_bind_param(stmt, binds);
  349: 
  350: 	statements[ndx].mysql = stmt;
  351: 	free(query);
  352: 
  353: 	return 1;
  354: }
  355: #endif
  356: 
  357: #ifdef USE_MYSQL
  358: static int prepare_mysql_queries(int type)
  359: {
  360: 	MYSQL_BIND binds[MAX_BIND_CNT];
  361: 	char *sql;
  362: 
  363: 	switch (type) {
  364: 	case PREP_NORM:
  365: 		sql="SELECT disk_id"
  366: 		    " FROM disk"
  367: 		    " WHERE host = ? AND devno = ?";
  368: 		memset(binds, 0, sizeof binds);
  369: 		binds[0].buffer_type = MYSQL_TYPE_STRING;
  370: 		binds[0].buffer = &bind_thishost;
  371: 		binds[0].buffer_length = bind_thishost_len;
  372: 		binds[1].buffer_type = MYSQL_TYPE_LONGLONG;
  373: 		binds[1].buffer = &bind_devno;
  374: 		if (!prepare_mysql(SEL_DEV, binds, 2, sql))
  375: 			return 0;
  376: 
  377: 		memset(binds, 0, sizeof binds);
  378: 		binds[0].buffer_type = MYSQL_TYPE_LONG;
  379: 		binds[0].buffer = &bind_disk_id;
  380: 		binds[1].buffer_type = MYSQL_TYPE_LONGLONG;
  381: 		binds[1].buffer = &bind_ino;
  382: 		if (select_many_sums) {
  383: 			sql="SELECT checksum, sum_type, size, mtime, ctime"
  384: 			    " FROM inode_map"
  385: 			    " WHERE disk_id = ? AND ino = ?";
  386: 			if (!prepare_mysql(SEL_SUM, binds, 2, sql))
  387: 				return 0;
  388: 		} else {
  389: 			sql="SELECT checksum"
  390: 			    " FROM inode_map"
  391: 			    " WHERE disk_id = ? AND ino = ? AND sum_type = %d"
  392: 			    "   AND size = ? AND mtime = ? %s"; /* optional: AND ctime = ? */
  393: 			binds[2].buffer_type = MYSQL_TYPE_LONGLONG;
  394: 			binds[2].buffer = &bind_size;
  395: 			binds[3].buffer_type = MYSQL_TYPE_LONGLONG;
  396: 			binds[3].buffer = &bind_mtime;
  397: 			if (!db_lax) {
  398: 				binds[4].buffer_type = MYSQL_TYPE_LONGLONG;
  399: 				binds[4].buffer = &bind_ctime;
  400: 			}
  401: 			if (!prepare_mysql(SEL_SUM, binds, 4 + !db_lax, sql, md_num, db_lax ? "" : "AND ctime = ?"))
  402: 				return 0;
  403: 		}
  404: 
  405: 		sql="INSERT INTO inode_map"
  406: 		    " SET disk_id = ?, ino = ?, sum_type = ?,"
  407: 		    "     size = ?, mtime = ?, ctime = ?, checksum = ?"
  408: 		    " ON DUPLICATE KEY"
  409: 		    " UPDATE size = VALUES(size), mtime = VALUES(mtime),"
  410: 		    "        ctime = VALUES(ctime), checksum = VALUES(checksum)";
  411: 		memset(binds, 0, sizeof binds);
  412: 		binds[0].buffer_type = MYSQL_TYPE_LONG;
  413: 		binds[0].buffer = &bind_disk_id;
  414: 		binds[1].buffer_type = MYSQL_TYPE_LONGLONG;
  415: 		binds[1].buffer = &bind_ino;
  416: 		binds[2].buffer_type = MYSQL_TYPE_LONG;
  417: 		binds[2].buffer = &bind_mdnum;
  418: 		binds[3].buffer_type = MYSQL_TYPE_LONGLONG;
  419: 		binds[3].buffer = &bind_size;
  420: 		binds[4].buffer_type = MYSQL_TYPE_LONGLONG;
  421: 		binds[4].buffer = &bind_mtime;
  422: 		binds[5].buffer_type = MYSQL_TYPE_LONGLONG;
  423: 		binds[5].buffer = &bind_ctime;
  424: 		binds[6].buffer_type = MYSQL_TYPE_BLOB;
  425: 		binds[6].buffer = &bind_sum;
  426: 		binds[6].buffer_length = MD5_DIGEST_LEN; /* Same as MD4_DIGEST_LEN */
  427: 		if (!prepare_mysql(REP_SUM, binds, 7, sql))
  428: 			return 0;
  429: 
  430: 		sql="UPDATE inode_map"
  431: 		    " SET ctime = ?"
  432: 		    " WHERE disk_id = ? AND ino = ? AND sum_type = ? AND size = ? AND mtime = ?";
  433: 		memset(binds, 0, sizeof binds);
  434: 		binds[0].buffer_type = MYSQL_TYPE_LONGLONG;
  435: 		binds[0].buffer = &bind_ctime;
  436: 		binds[1].buffer_type = MYSQL_TYPE_LONG;
  437: 		binds[1].buffer = &bind_disk_id;
  438: 		binds[2].buffer_type = MYSQL_TYPE_LONGLONG;
  439: 		binds[2].buffer = &bind_ino;
  440: 		binds[3].buffer_type = MYSQL_TYPE_LONG;
  441: 		binds[3].buffer = &bind_mdnum;
  442: 		binds[4].buffer_type = MYSQL_TYPE_LONGLONG;
  443: 		binds[4].buffer = &bind_size;
  444: 		binds[5].buffer_type = MYSQL_TYPE_LONGLONG;
  445: 		binds[5].buffer = &bind_mtime;
  446: 		if (!prepare_mysql(UPD_CTIME, binds, 6, sql))
  447: 			return 0;
  448: 		break;
  449: 
  450: 	case PREP_MOUNT:
  451: 		sql="INSERT INTO disk"
  452: 		    " SET host = ?, last_seen = ?, mount_uniq = ?, devno = ?"
  453: 		    " ON DUPLICATE KEY"
  454: 		    " UPDATE last_seen = VALUES(last_seen), devno = VALUES(devno)";
  455: 		memset(binds, 0, sizeof binds);
  456: 		binds[0].buffer_type = MYSQL_TYPE_STRING;
  457: 		binds[0].buffer = &bind_thishost;
  458: 		binds[0].buffer_length = bind_thishost_len;
  459: 		binds[1].buffer_type = MYSQL_TYPE_LONGLONG;
  460: 		binds[1].buffer = &bind_mtime; /* we abuse mtime to hold the last_seen value */
  461: 		binds[2].buffer_type = MYSQL_TYPE_STRING;
  462: 		binds[2].buffer = &bind_mount_uniq;
  463: 		binds[2].buffer_length = sizeof bind_mount_uniq;
  464: 		binds[2].length = &bind_mount_uniq_len;
  465: 		binds[3].buffer_type = MYSQL_TYPE_LONGLONG;
  466: 		binds[3].buffer = &bind_devno;
  467: 		if (!prepare_mysql(INS_MOUNT, binds, 4, sql))
  468: 			return 0;
  469: 
  470: 		sql="SELECT mount_uniq"
  471: 		    " FROM disk"
  472: 		    " WHERE host = ? AND last_seen < ? AND devno != 0";
  473: 		/* Reusing first 2 binds from INS_MOUNT */
  474: 		if (!prepare_mysql(SEL_MOUNT, binds, 2, sql))
  475: 			return 0;
  476: 
  477: 		sql="UPDATE disk"
  478: 		    " SET devno = 0"
  479: 		    " WHERE host = ? AND last_seen < ? AND devno != 0";
  480: 		/* Reusing binds from SEL_MOUNT */
  481: 		if (!prepare_mysql(UN_MOUNT, binds, 2, sql))
  482: 			return 0;
  483: 		break;
  484: 	}
  485: 
  486: 	return 1;
  487: }
  488: #endif
  489: 
  490: #ifdef USE_MYSQL
  491: static int db_connect_mysql(void)
  492: {
  493: 	const char *open_dbname = db_init ? "mysql" : dbname;
  494: 
  495: 	if (!(dbh.mysql = mysql_init(NULL)))
  496: 		out_of_memory("db_read_config");
  497: 
  498: 	if (DEBUG_GTE(DB, 1)) {
  499: 		rprintf(FCLIENT, "[%s] connecting: host=%s user=%s db=%s port=%d\n",
  500: 			who_am_i(), dbhost, dbuser, open_dbname, dbport);
  501: 	}
  502: 	if (!mysql_real_connect(dbh.mysql, dbhost, dbuser, dbpass, open_dbname, dbport, NULL, 0)) {
  503: 		rprintf(log_code, "[%s] Unable to connect to DB: %s\n", who_am_i(), mysql_error(dbh.mysql));
  504: 		return 0;
  505: 	}
  506: 
  507: 	if (db_init) {
  508: 		if (db_output_msgs)
  509: 			rprintf(FCLIENT, "Creating DB %s (if it does not exist)\n", dbname);
  510: 		if (!run_sql("CREATE DATABASE IF NOT EXISTS `%s`", dbname)
  511: 		 || !run_sql("USE `%s`", dbname))
  512: 			exit_cleanup(RERR_IPC);
  513: 
  514: 		if (db_output_msgs)
  515: 			rprintf(FCLIENT, "Dropping old tables (if they exist))\n");
  516: 		if (!run_sql("DROP TABLE IF EXISTS disk")
  517: 		 || !run_sql("DROP TABLE IF EXISTS inode_map"))
  518: 			exit_cleanup(RERR_IPC);
  519: 
  520: 		if (db_output_msgs)
  521: 			rprintf(FCLIENT, "Creating empty tables ...\n");
  522: 		if (!run_sql(
  523: 		    "CREATE TABLE disk (\n"
  524: 		    "  disk_id integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,\n"
  525: 		    "  host varchar(128) NOT NULL default 'localhost',\n"
  526: 		    "  mount_uniq varchar(128) default NULL,\n"
  527: 		    "  devno bigint unsigned NOT NULL,\n" /* This is 0 when not mounted */
  528: 		    "  last_seen bigint NOT NULL,\n"
  529: 		    "  UNIQUE KEY mount_lookup (host, mount_uniq),\n"
  530: 		    "  KEY dev_lookup (devno, host)\n"
  531: 		    ")"))
  532: 			exit_cleanup(RERR_IPC);
  533: 
  534: 		if (!run_sql(
  535: 		    "CREATE TABLE inode_map (\n"
  536: 		    "  disk_id integer unsigned NOT NULL,\n"
  537: 		    "  ino bigint unsigned NOT NULL,\n"
  538: 		    "  sum_type tinyint NOT NULL default '0',\n"
  539: 		    "  size bigint unsigned NOT NULL,\n"
  540: 		    "  mtime bigint NOT NULL,\n"
  541: 		    "  ctime bigint NOT NULL,\n"
  542: 		    "  checksum binary(16) NOT NULL,\n"
  543: 		    "  PRIMARY KEY (disk_id,ino,sum_type)\n"
  544: 		    ")"))
  545: 			exit_cleanup(RERR_IPC);
  546: 
  547: 		if (!db_mounts)
  548: 			exit_cleanup(0);
  549: 	}
  550: 
  551: 	if (db_mounts) {
  552: 		if (!prepare_mysql_queries(PREP_MOUNT))
  553: 			exit_cleanup(RERR_IPC);
  554: 		update_mounts();
  555: 		exit_cleanup(0);
  556: 	}
  557: 
  558: 	if (!prepare_mysql_queries(PREP_NORM))
  559: 		return 0;
  560: 
  561: 	return 1;
  562: }
  563: #endif
  564: 
  565: #ifdef USE_SQLITE
  566: static int prepare_sqlite(int ndx, const char *fmt, ...)
  567: {
  568: 	va_list ap;
  569: 	char *query;
  570: 	int rc, qlen, lock_failures = 0;
  571: 
  572: 	va_start(ap, fmt);
  573: 	qlen = vasprintf(&query, fmt, ap);
  574: 	va_end(ap);
  575: 	if (qlen < 0)
  576: 		out_of_memory("prepare_sqlite");
  577: 	if (DEBUG_GTE(DB, 3))
  578: 		rprintf(FCLIENT, "[%s] SQL being prepared: %s\n", who_am_i(), query);
  579: 
  580: 	while ((rc = sqlite3_prepare_v2(dbh.sqlite, query, -1, &statements[ndx].sqlite, NULL)) != 0) {
  581: 		if (DEBUG_GTE(DB, 4)) {
  582: 			rprintf(FCLIENT, "[%s] sqlite3_prepare_v2(,%s,,) returned %d\n",
  583: 				who_am_i(), query, rc);
  584: 		}
  585: 		if (rc != SQLITE_BUSY && rc != SQLITE_LOCKED)
  586: 			break;
  587: 		if (++lock_failures > MAX_LOCK_FAILURES)
  588: 			break;
  589: 		msleep(LOCK_FAIL_MSLEEP);
  590: 	}
  591: 	if (rc) {
  592: 		rprintf(log_code, "[%s] Failed to prepare SQL: %s (%d)\n", who_am_i(), sqlite3_errmsg(dbh.sqlite), rc);
  593: 		rprintf(log_code, "%s\n", query);
  594: 		free(query);
  595: 		return 0;
  596: 	}
  597: 	free(query);
  598: 
  599: 	return 1;
  600: }
  601: #endif
  602: 
  603: #ifdef USE_SQLITE
  604: static int prepare_sqlite_queries(int type)
  605: {
  606: 	char *sql;
  607: 
  608: 	switch (type) {
  609: 	case PREP_NORM:
  610: 		sql="SELECT disk_id"
  611: 		    " FROM disk"
  612: 		    " WHERE host = ? AND devno = ?";
  613: 		if (!prepare_sqlite(SEL_DEV, sql))
  614: 			return 0;
  615: 
  616: 		if (select_many_sums) {
  617: 			sql="SELECT checksum, sum_type, size, mtime, ctime"
  618: 			    " FROM inode_map"
  619: 			    " WHERE disk_id = ? AND ino = ?";
  620: 			if (!prepare_sqlite(SEL_SUM, sql))
  621: 				return 0;
  622: 		} else {
  623: 			sql="SELECT checksum"
  624: 			    " FROM inode_map"
  625: 			    " WHERE disk_id = ? AND ino = ? AND sum_type = %d"
  626: 			    "   AND size = ? AND mtime = ? %s";
  627: 			if (!prepare_sqlite(SEL_SUM, sql, md_num, db_lax ? "" : "AND ctime = ?"))
  628: 				return 0;
  629: 		}
  630: 
  631: 		sql="INSERT OR REPLACE INTO inode_map"
  632: 		    " (disk_id, ino, sum_type, size, mtime, ctime, checksum)"
  633: 		    " VALUES (?, ?, ?, ?, ?, ?, ?)";
  634: 		if (!prepare_sqlite(REP_SUM, sql))
  635: 			return 0;
  636: 
  637: 		sql="UPDATE inode_map"
  638: 		    " SET ctime = ?"
  639: 		    " WHERE disk_id = ? AND ino = ? AND sum_type = ? AND size = ? AND mtime = ?";
  640: 		if (!prepare_sqlite(UPD_CTIME, sql))
  641: 			return 0;
  642: 		break;
  643: 
  644: 	case PREP_MOUNT:
  645: 		sql="INSERT OR IGNORE INTO disk"
  646: 		    " (host, last_seen, mount_uniq, devno)"
  647: 		    " VALUES (?, ?, ?, ?)";
  648: 		if (!prepare_sqlite(INS_MOUNT, sql))
  649: 			return 0;
  650: 
  651: 		sql="UPDATE disk"
  652: 		    " SET last_seen = ?, devno = ?"
  653: 		    " WHERE host = ? AND mount_uniq = ?";
  654: 		if (!prepare_sqlite(UPD_MOUNT, sql))
  655: 			return 0;
  656: 
  657: 		sql="SELECT mount_uniq"
  658: 		    " FROM disk"
  659: 		    " WHERE host = ? AND last_seen < ? AND devno != 0";
  660: 		if (!prepare_sqlite(SEL_MOUNT, sql))
  661: 			return 0;
  662: 
  663: 		sql="UPDATE disk"
  664: 		    " SET devno = 0"
  665: 		    " WHERE host = ? AND last_seen < ? AND devno != 0";
  666: 		if (!prepare_sqlite(UN_MOUNT, sql))
  667: 			return 0;
  668: 		break;
  669: 	}
  670: 
  671: 	return 1;
  672: }
  673: #endif
  674: 
  675: #ifdef USE_SQLITE
  676: static int db_connect_sqlite(void)
  677: {
  678: 	int lock_failures = 0;
  679: 	int rc;
  680: 
  681: #ifdef SQLITE_CONFIG_LOG
  682: 	if (error_log) {
  683: 		if (DEBUG_GTE(DB, 1))
  684: 			rprintf(FCLIENT, "[%s] Setting sqlite errlog to %s\n", who_am_i(), error_log);
  685: 		if (!(error_log_fp = fopen(error_log, "a"))) {
  686: 			rsyserr(log_code, errno, "unable to append to logfile %s", error_log);
  687: 			error_log = NULL;
  688: 		} else if (sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, NULL) != 0)
  689: 			rprintf(log_code, "Failed to set errorLogCallback: %s\n", sqlite3_errmsg(dbh.sqlite));
  690: 	}
  691: #endif
  692: 
  693: 	while (1) {
  694: 		int open_flags = SQLITE_OPEN_READWRITE;
  695: 		if (db_init)
  696: 			open_flags |= SQLITE_OPEN_CREATE;
  697: 		if (DEBUG_GTE(DB, 1))
  698: 			rprintf(FCLIENT, "[%s] opening %s (%d)\n", who_am_i(), dbname, open_flags);
  699: 		if ((rc = sqlite3_open_v2(dbname, &dbh.sqlite, open_flags, NULL)) == 0) {
  700: 			break;
  701: 		}
  702: 		if (DEBUG_GTE(DB, 4)) {
  703: 			rprintf(FCLIENT, "[%s] sqlite3_open_v2(%s,,%d,NULL) returned %d\n",
  704: 				who_am_i(), dbname, open_flags, rc);
  705: 		}
  706: 		if (rc != SQLITE_BUSY && rc != SQLITE_LOCKED)
  707: 			break;
  708: 		if (++lock_failures > MAX_LOCK_FAILURES)
  709: 			break;
  710: 		msleep(LOCK_FAIL_MSLEEP);
  711: 	}
  712: 
  713: 	if (rc) {
  714: 		rprintf(log_code, "Unable to connect to DB: %s (%d)\n", sqlite3_errmsg(dbh.sqlite), rc);
  715: 		return 0;
  716: 	}
  717: 
  718: 	if (db_init) {
  719: 		char *sql;
  720: 		if (db_output_msgs)
  721: 			rprintf(FCLIENT, "Dropping old tables (if they exist) ...\n");
  722: 		if (!run_sql("DROP TABLE IF EXISTS disk")
  723: 		 || !run_sql("DROP TABLE IF EXISTS inode_map"))
  724: 			exit_cleanup(RERR_IPC);
  725: 
  726: 		if (db_output_msgs)
  727: 			rprintf(FCLIENT, "Creating empty tables ...\n");
  728: 		sql="CREATE TABLE disk (\n"
  729: 		    "  disk_id integer NOT NULL PRIMARY KEY AUTOINCREMENT,\n"
  730: 		    "  host varchar(128) NOT NULL default 'localhost',\n"
  731: 		    "  mount_uniq varchar(128) default NULL,\n"
  732: 		    "  devno bigint NOT NULL,\n" /* This is 0 when not mounted */
  733: 		    "  last_seen bigint NOT NULL,\n"
  734: 		    "  UNIQUE (host, mount_uniq)\n"
  735: 		    ")";
  736: 		if (!run_sql(sql))
  737: 			exit_cleanup(RERR_IPC);
  738: 
  739: 		sql="CREATE TABLE inode_map (\n"
  740: 		    "  disk_id integer NOT NULL,\n"
  741: 		    "  ino bigint NOT NULL,\n"
  742: 		    "  size bigint NOT NULL,\n"
  743: 		    "  mtime bigint NOT NULL,\n"
  744: 		    "  ctime bigint NOT NULL,\n"
  745: 		    "  sum_type tinyint NOT NULL default '0',\n"
  746: 		    "  checksum binary(16) NOT NULL,\n"
  747: 		    "  PRIMARY KEY (disk_id,ino,sum_type)\n"
  748: 		    ")";
  749: 		if (!run_sql(sql))
  750: 			exit_cleanup(RERR_IPC);
  751: 
  752: #if SQLITE_VERSION_NUMBER >= 3007000
  753: 		/* Using WAL locking makes concurrency much better (requires sqlite 3.7.0). */
  754: 		sql="PRAGMA journal_mode = wal";
  755: 		run_sql(sql); /* We don't check this for success. */
  756: #endif
  757: 
  758: 		if (!db_mounts)
  759: 			exit_cleanup(0);
  760: 	}
  761: 
  762: 	if (db_mounts) {
  763: 		if (!prepare_sqlite_queries(PREP_MOUNT))
  764: 			exit_cleanup(RERR_IPC);
  765: 		update_mounts();
  766: 		exit_cleanup(0);
  767: 	}
  768: 
  769: 	if (!prepare_sqlite_queries(PREP_NORM)) {
  770: 		db_disconnect(False);
  771: 		return 0;
  772: 	}
  773: 
  774: 	return 1;
  775: }
  776: #endif
  777: 
  778: int db_connect(int select_many)
  779: {
  780: 	select_many_sums = select_many;
  781: 
  782: 	switch (use_db) {
  783: #ifdef USE_MYSQL
  784: 	case DB_TYPE_MYSQL:
  785: 		if (db_connect_mysql())
  786: 			return 1;
  787: 		break;
  788: #endif
  789: #ifdef USE_SQLITE
  790: 	case DB_TYPE_SQLITE:
  791: 		if (db_connect_sqlite())
  792: 			return 1;
  793: 		break;
  794: #endif
  795: 	}
  796: 
  797: 	db_disconnect(False);
  798: 
  799: 	return 0;
  800: }
  801: 
  802: void db_disconnect(BOOL commit)
  803: {
  804: 	int ndx;
  805: 
  806: 	if (!dbh.all)
  807: 		return;
  808: 
  809: 	if (transaction_state > 0) {
  810: 		if (DEBUG_GTE(DB, 1)) {
  811: 			rprintf(FCLIENT, "[%s] %s our DB transaction\n",
  812: 				who_am_i(), commit ? "Committing" : "Rolling back");
  813: 		}
  814: 		transaction_state = 0;
  815: 		if (commit)
  816: 			run_sql("COMMIT");
  817: 		else
  818: 			run_sql("ROLLBACK");
  819: 	}
  820: 
  821: 	if (DEBUG_GTE(DB, 1))
  822: 		rprintf(FCLIENT, "[%s] Disconnecting from the DB\n", who_am_i());
  823: 
  824: 	for (ndx = 0; ndx < MAX_PREP_CNT; ndx++) {
  825: 		if (statements[ndx].all) {
  826: 			switch (use_db) {
  827: #ifdef USE_MYSQL
  828: 			case DB_TYPE_MYSQL:
  829: 				mysql_stmt_close(statements[ndx].mysql);
  830: 				break;
  831: #endif
  832: #ifdef USE_SQLITE
  833: 			case DB_TYPE_SQLITE:
  834: 				sqlite3_finalize(statements[ndx].sqlite);
  835: 				break;
  836: #endif
  837: 			}
  838: 			statements[ndx].all = NULL;
  839: 		}
  840: 	}
  841: 
  842: 	switch (use_db) {
  843: #ifdef USE_MYSQL
  844: 	case DB_TYPE_MYSQL:
  845: 		mysql_close(dbh.mysql);
  846: 		break;
  847: #endif
  848: #ifdef USE_SQLITE
  849: 	case DB_TYPE_SQLITE:
  850: 		sqlite3_close(dbh.sqlite);
  851: 		break;
  852: #endif
  853: 	}
  854: 
  855: 	dbh.all = NULL;
  856: 	use_db = DB_TYPE_NONE;
  857: }
  858: 
  859: #ifdef USE_MYSQL
  860: static MYSQL_STMT *exec_mysql(int ndx)
  861: {
  862: 	MYSQL_STMT *stmt = statements[ndx].mysql;
  863: 	int rc;
  864: 
  865: 	if ((rc = mysql_stmt_execute(stmt)) == CR_SERVER_LOST) {
  866: 		db_disconnect(False);
  867: 		use_db = DB_TYPE_MYSQL;
  868: 		if (db_connect(select_many_sums)) {
  869: 			stmt = statements[ndx].mysql;
  870: 			rc = mysql_stmt_execute(stmt);
  871: 		}
  872: 	}
  873: 	if (rc != 0) {
  874: 		rprintf(log_code, "SQL execute failed: %s\n", mysql_stmt_error(stmt));
  875: 		return NULL;
  876: 	}
  877: 
  878: 	return stmt;
  879: }
  880: #endif
  881: 
  882: #ifdef USE_MYSQL
  883: /* This stores up to max_rows into the values pointed to by the bind data arrays.
  884:  * If max_rows is > 1, then all the buffer pointers MUST be set to an array long
  885:  * enough to hold the max count of rows.  The buffer pointer will be incremented
  886:  * to read additional rows (but never past the end).  If stmt_ptr is non-NULL, it
  887:  * will be set to the "stmt" pointer IFF we didn't run out of rows before hitting
  888:  * the max.  In this case, the caller should call mysql_stmt_fetch() to read any
  889:  * remaining rows (the buffer pointers will point at the final array element) and
  890:  * then call mysql_stmt_free_result().  If *stmt_ptr is a NULL value, there were
  891:  * not enough rows to fill the max_rows arrays, and the stmt was already freed. */
  892: static int fetch_mysql(MYSQL_BIND *binds, int bind_cnt, int ndx, int max_rows, MYSQL_STMT **stmt_ptr)
  893: {
  894: 	MYSQL_STMT *stmt;
  895: 	int i, rc, rows = 0;
  896: 
  897: 	if (bind_cnt > MAX_RESULT_BINDS) {
  898: 		fprintf(stderr, "Internal error: MAX_RESULT_BINDS overflow\n");
  899: 		exit_cleanup(RERR_UNSUPPORTED);
  900: 	}
  901: 
  902: 	if ((stmt = exec_mysql(ndx)) == NULL)
  903: 		return 0;
  904: 
  905: 	for (i = 0; i < bind_cnt; i++) {
  906: 		binds[i].is_null = &result_is_null[i];
  907: 		binds[i].length = &result_length[i];
  908: 		binds[i].error = &result_error[i];
  909: 	}
  910: 	mysql_stmt_bind_result(stmt, binds);
  911: 
  912: 	while (rows < max_rows) {
  913: 		if ((rc = mysql_stmt_fetch(stmt)) != 0) {
  914: 			if (rc != MYSQL_NO_DATA)
  915: 				rprintf(log_code, "SELECT fetch failed: %s\n", mysql_stmt_error(stmt));
  916: 			break;
  917: 		}
  918: 		if (++rows >= max_rows)
  919: 			break;
  920: 		for (i = 0; i < bind_cnt; i++) {
  921: 			switch (binds[i].buffer_type) {
  922: 			case MYSQL_TYPE_BLOB:
  923: 			case MYSQL_TYPE_STRING:
  924: 			    binds[i].buffer += binds[i].buffer_length;
  925: 			    break;
  926: 			case MYSQL_TYPE_LONG:
  927: 			    binds[i].buffer += sizeof (int);
  928: 			    break;
  929: 			case MYSQL_TYPE_LONGLONG:
  930: 			    binds[i].buffer += sizeof (int64);
  931: 			    break;
  932: 			default:
  933: 			    fprintf(stderr, "Unknown MYSQL_TYPE_* in multi-row read: %d.\n", binds[i].buffer_type);
  934: 			    exit_cleanup(RERR_UNSUPPORTED);
  935: 			}
  936: 		}
  937: 	}
  938: 
  939: 	if (!stmt_ptr || rows < max_rows) {
  940: 		mysql_stmt_free_result(stmt);
  941: 		stmt = NULL;
  942: 	}
  943: 	if (stmt_ptr)
  944: 		*stmt_ptr = stmt;
  945: 
  946: 	return rows;
  947: }
  948: #endif
  949: 
  950: #if defined USE_MYSQL || defined USE_SQLITE
  951: static void update_mounts(void)
  952: {
  953: 	char buf[2048], *argv[2];
  954: 	int f_from, f_to, len;
  955: 	STRUCT_STAT st;
  956: 	int pid, status;
  957: 
  958: 	if (DEBUG_GTE(DB, 2))
  959: 		printf("Running %s to grab mount info\n", mount_helper_script);
  960: 	argv[0] = mount_helper_script;
  961: 	argv[1] = NULL;
  962: 	pid = piped_child(argv, &f_from, &f_to);
  963: 	close(f_to);
  964: 
  965: 	bind_mtime = time(NULL); /* abuse mtime slightly to hold our last_seen value */
  966: 
  967: 	/* Strict format has 2 items with one tab as separator: MOUNT_UNIQ\tPATH */
  968: 	while ((len = read_line(f_from, buf, sizeof buf, 0)) > 0) {
  969: 		char *mount_uniq, *path;
  970: 
  971: 		if (DEBUG_GTE(DB, 3))
  972: 			printf("Parsing mount info: %s\n", buf);
  973: 		mount_uniq = strtok(buf, "\t");
  974: 		path = mount_uniq ? strtok(NULL, "\r\n") : NULL;
  975: 		if (!path) {
  976: 			fprintf(stderr, "Failed to parse line from %s output\n", mount_helper_script);
  977: 			exit_cleanup(RERR_SYNTAX);
  978: 		}
  979: 
  980: 		if (lstat(path, &st) < 0) {
  981: 			fprintf(stderr, "Failed to lstat(%s): %s\n", path, strerror(errno));
  982: 			exit_cleanup(RERR_IPC);
  983: 		}
  984: 
  985: 		bind_mount_uniq_len = strlcpy(bind_mount_uniq, mount_uniq, sizeof bind_mount_uniq);
  986: 		if (bind_mount_uniq_len >= (int)sizeof bind_mount_uniq)
  987: 			bind_mount_uniq_len = sizeof bind_mount_uniq - 1;
  988: 
  989: 		if (db_output_msgs) {
  990: 			printf("Marking mount \"%s\" (%s) as a recent mount\n",
  991: 				bind_mount_uniq, big_num(st.st_dev));
  992: 		}
  993: 		switch (use_db) {
  994: #ifdef USE_MYSQL
  995: 		case DB_TYPE_MYSQL:
  996: 			bind_devno = st.st_dev;
  997: 			if (exec_mysql(INS_MOUNT) == NULL) {
  998: 				fprintf(stderr, "Failed to update mount info for \"%s\" - %s\n",
  999: 					bind_mount_uniq, mysql_error(dbh.mysql));
 1000: 				exit_cleanup(RERR_IPC);
 1001: 			}
 1002: 			break;
 1003: #endif
 1004: #ifdef USE_SQLITE
 1005: 		case DB_TYPE_SQLITE: {
 1006: 			int rc, change_cnt;
 1007: 			sqlite3_stmt *stmt = statements[INS_MOUNT].sqlite;
 1008: 			sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC);
 1009: 			sqlite3_bind_int64(stmt, 2, bind_mtime);
 1010: 			sqlite3_bind_text(stmt, 3, bind_mount_uniq, bind_mount_uniq_len, SQLITE_STATIC);
 1011: 			sqlite3_bind_int64(stmt, 4, st.st_dev);
 1012: 			rc = sqlite3_step(stmt);
 1013: 			if (rc != SQLITE_DONE) {
 1014: 				fprintf(stderr, "Failed to insert mount info for \"%s\" - %s (%d)\n",
 1015: 					bind_mount_uniq, sqlite3_errmsg(dbh.sqlite), rc);
 1016: 				exit_cleanup(RERR_IPC);
 1017: 			}
 1018: 			change_cnt = sqlite3_changes(dbh.sqlite);
 1019: 			sqlite3_reset(stmt);
 1020: 			if (change_cnt == 0) {
 1021: 				stmt = statements[UPD_MOUNT].sqlite;
 1022: 				sqlite3_bind_int64(stmt, 1, bind_mtime);
 1023: 				sqlite3_bind_int64(stmt, 2, st.st_dev);
 1024: 				sqlite3_bind_text(stmt, 3, bind_thishost, bind_thishost_len, SQLITE_STATIC);
 1025: 				sqlite3_bind_text(stmt, 4, bind_mount_uniq, bind_mount_uniq_len, SQLITE_STATIC);
 1026: 				rc = sqlite3_step(stmt);
 1027: 				if (rc != SQLITE_DONE) {
 1028: 					fprintf(stderr, "Failed to update mount info for \"%s\" - %s (%d)\n",
 1029: 						bind_mount_uniq, sqlite3_errmsg(dbh.sqlite), rc);
 1030: 					exit_cleanup(RERR_IPC);
 1031: 				}
 1032: 				sqlite3_reset(stmt);
 1033: 			}
 1034: 			break;
 1035: 		    }
 1036: #endif
 1037: 		}
 1038: 	}
 1039: 	close(f_from);
 1040: 
 1041: 	waitpid(pid, &status, 0);
 1042: 
 1043: 	switch (use_db) {
 1044: #ifdef USE_MYSQL
 1045: 	case DB_TYPE_MYSQL: {
 1046: 		if (db_output_msgs) {
 1047: 			MYSQL_BIND binds[1];
 1048: 			MYSQL_STMT *stmt;
 1049: 
 1050: 			binds[0].buffer_type = MYSQL_TYPE_BLOB;
 1051: 			binds[0].buffer = bind_mount_uniq;
 1052: 			binds[0].buffer_length = sizeof bind_mount_uniq;
 1053: 			if (fetch_mysql(binds, 1, SEL_MOUNT, 1, &stmt)) {
 1054: 				while (1) {
 1055: 					printf("Marking mount \"%s\" as unmounted.\n", bind_mount_uniq);
 1056: 					if (mysql_stmt_fetch(stmt) != 0)
 1057: 						break;
 1058: 				}
 1059: 				mysql_stmt_free_result(stmt);
 1060: 			}
 1061: 		}
 1062: 
 1063: 		if (exec_mysql(UN_MOUNT) == NULL) {
 1064: 			fprintf(stderr, "Failed to update old mount info - %s\n",
 1065: 				mysql_error(dbh.mysql));
 1066: 			exit_cleanup(RERR_IPC);
 1067: 		}
 1068: 		break;
 1069: 	    }
 1070: #endif
 1071: #ifdef USE_SQLITE
 1072: 	case DB_TYPE_SQLITE: {
 1073: 		sqlite3_stmt *stmt;
 1074: 		int rc;
 1075: 
 1076: 		if (db_output_msgs) {
 1077: 			stmt = statements[SEL_MOUNT].sqlite;
 1078: 			sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC);
 1079: 			sqlite3_bind_int64(stmt, 2, bind_mtime);
 1080: 			while (1) {
 1081: 				if (sqlite3_step(stmt) != SQLITE_ROW)
 1082: 					break;
 1083: 				printf("Marking mount \"%s\" as unmounted.\n", sqlite3_column_text(stmt, 0));
 1084: 			}
 1085: 			sqlite3_reset(stmt);
 1086: 		}
 1087: 
 1088: 		stmt = statements[UN_MOUNT].sqlite;
 1089: 		sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC);
 1090: 		sqlite3_bind_int64(stmt, 2, bind_mtime);
 1091: 		rc = sqlite3_step(stmt);
 1092: 		sqlite3_reset(stmt);
 1093: 		if (rc != SQLITE_DONE) {
 1094: 			fprintf(stderr, "Failed to update old mount info - %s (%d)\n",
 1095: 				sqlite3_errmsg(dbh.sqlite), rc);
 1096: 			exit_cleanup(RERR_IPC);
 1097: 		}
 1098: 		break;
 1099: 	    }
 1100: #endif
 1101: 	}
 1102: }
 1103: #endif
 1104: 
 1105: static unsigned int get_disk_id(int64 devno)
 1106: {
 1107: 	static unsigned int prior_disk_id = 0;
 1108: 	static int64 prior_devno = 0;
 1109: 
 1110: 	if (prior_devno == devno && prior_disk_id) {
 1111: 		if (DEBUG_GTE(DB, 5))
 1112: 			rprintf(FCLIENT, "get_disk_id(%s,%s) = %d (cached)\n", bind_thishost, big_num(devno), prior_disk_id);
 1113: 		return prior_disk_id;
 1114: 	}
 1115: 	prior_devno = devno;
 1116: 
 1117: 	switch (use_db) {
 1118: #ifdef USE_MYSQL
 1119: 	case DB_TYPE_MYSQL: {
 1120: 		MYSQL_BIND binds[1];
 1121: 
 1122: 		bind_devno = devno; /* The one changing SEL_DEV input value. */
 1123: 
 1124: 		/* Bind where to put the output. */
 1125: 		binds[0].buffer_type = MYSQL_TYPE_LONG;
 1126: 		binds[0].buffer = &prior_disk_id;
 1127: 		if (!fetch_mysql(binds, 1, SEL_DEV, 1, NULL))
 1128: 			prior_disk_id = 0;
 1129: 		break;
 1130: 	    }
 1131: #endif
 1132: #ifdef USE_SQLITE
 1133: 	case DB_TYPE_SQLITE: {
 1134: 		sqlite3_stmt *stmt = statements[SEL_DEV].sqlite;
 1135: 		sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC);
 1136: 		sqlite3_bind_int64(stmt, 2, devno);
 1137: 		if (sqlite3_step(stmt) == SQLITE_ROW)
 1138: 			prior_disk_id = sqlite3_column_int(stmt, 0);
 1139: 		else
 1140: 			prior_disk_id = 0;
 1141: 		sqlite3_reset(stmt);
 1142: 		break;
 1143: 	    }
 1144: #endif
 1145: 	}
 1146: 
 1147: 	if (DEBUG_GTE(DB, 2))
 1148: 		rprintf(FCLIENT, "get_disk_id(%s,%s) = %d\n", bind_thishost, big_num(devno), prior_disk_id);
 1149: 	return prior_disk_id;
 1150: }
 1151: 
 1152: int db_get_checksum(const STRUCT_STAT *st_p, char *sum)
 1153: {
 1154: 	unsigned int disk_id = get_disk_id(st_p->st_dev);
 1155: 	int ok = 0;
 1156: 
 1157: 	if (disk_id == 0)
 1158: 		return 0;
 1159: 
 1160: 	switch (use_db) {
 1161: #ifdef USE_MYSQL
 1162: 	case DB_TYPE_MYSQL: {
 1163: 		MYSQL_BIND binds[1];
 1164: 
 1165: 		bind_disk_id = disk_id;
 1166: 		bind_ino = st_p->st_ino;
 1167: 		bind_size = st_p->st_size;
 1168: 		bind_mtime = st_p->st_mtime;
 1169: 		if (!db_lax)
 1170: 			bind_ctime = st_p->st_ctime;
 1171: 
 1172: 		binds[0].buffer_type = MYSQL_TYPE_BLOB;
 1173: 		binds[0].buffer = sum;
 1174: 		binds[0].buffer_length = MD5_DIGEST_LEN;
 1175: 		ok = fetch_mysql(binds, 1, SEL_SUM, 1, NULL);
 1176: 		break;
 1177: 	    }
 1178: #endif
 1179: #ifdef USE_SQLITE
 1180: 	case DB_TYPE_SQLITE: {
 1181: 		sqlite3_stmt *stmt = statements[SEL_SUM].sqlite;
 1182: 		sqlite3_bind_int(stmt, 1, disk_id);
 1183: 		sqlite3_bind_int64(stmt, 2, st_p->st_ino);
 1184: 		sqlite3_bind_int64(stmt, 3, st_p->st_size);
 1185: 		sqlite3_bind_int64(stmt, 4, st_p->st_mtime);
 1186: 		if (!db_lax)
 1187: 			sqlite3_bind_int64(stmt, 5, st_p->st_ctime);
 1188: 		if (sqlite3_step(stmt) == SQLITE_ROW) {
 1189: 			int len = sqlite3_column_bytes(stmt, 0);
 1190: 			if (len > MAX_DIGEST_LEN)
 1191: 				len = MAX_DIGEST_LEN;
 1192: 			memcpy(sum, sqlite3_column_blob(stmt, 0), len);
 1193: 			ok = 1;
 1194: 		}
 1195: 		sqlite3_reset(stmt);
 1196: 		break;
 1197: 	    }
 1198: #endif
 1199: 	}
 1200: 
 1201: 	if (DEBUG_GTE(DB, 2)) {
 1202: 		if (ok) {
 1203: 			rprintf(FCLIENT, "[%s] Found DB checksum for %s,%s,%d: %s\n",
 1204: 				who_am_i(), big_num(st_p->st_dev),
 1205: 				big_num(st_p->st_ino), md_num, sum_as_hex(md_num, sum, 0));
 1206: 		} else {
 1207: 			rprintf(FCLIENT, "[%s] No DB checksum for %s,%s,%d\n",
 1208: 				who_am_i(), big_num(st_p->st_dev),
 1209: 				big_num(st_p->st_ino), md_num);
 1210: 		}
 1211: 	}
 1212: 
 1213: 	return ok;
 1214: }
 1215: 
 1216: int db_get_both_checksums(const STRUCT_STAT *st_p, int *right_sum_cnt, int *wrong_sum_cnt, char **sum4, char **sum5)
 1217: {
 1218: 	static char dbsum[MD5_DIGEST_LEN*2];
 1219: 	int rows, j, sum_type[2];
 1220: 	int64 dbsize[2], dbmtime[2], dbctime[2];
 1221: 	unsigned int disk_id = get_disk_id(st_p->st_dev);
 1222: 
 1223: 	if (disk_id == 0)
 1224: 		return 0;
 1225: 
 1226: 	switch (use_db) {
 1227: #ifdef USE_MYSQL
 1228: 	case DB_TYPE_MYSQL: {
 1229: 		MYSQL_BIND binds[5];
 1230: 
 1231: 		bind_disk_id = disk_id;
 1232: 		bind_ino = st_p->st_ino;
 1233: 
 1234: 		binds[0].buffer_type = MYSQL_TYPE_BLOB;
 1235: 		binds[0].buffer = dbsum;
 1236: 		binds[0].buffer_length = MD5_DIGEST_LEN;
 1237: 		binds[1].buffer_type = MYSQL_TYPE_LONG;
 1238: 		binds[1].buffer = (char*)sum_type;
 1239: 		binds[2].buffer_type = MYSQL_TYPE_LONGLONG;
 1240: 		binds[2].buffer = (char*)dbsize;
 1241: 		binds[3].buffer_type = MYSQL_TYPE_LONGLONG;
 1242: 		binds[3].buffer = (char*)dbmtime;
 1243: 		binds[4].buffer_type = MYSQL_TYPE_LONGLONG;
 1244: 		binds[4].buffer = (char*)dbctime;
 1245: 		rows = fetch_mysql(binds, 5, SEL_SUM, 2, NULL);
 1246: 		break;
 1247: 	    }
 1248: #endif
 1249: #ifdef USE_SQLITE
 1250: 	case DB_TYPE_SQLITE: {
 1251: 		sqlite3_stmt *stmt = statements[SEL_SUM].sqlite;
 1252: 		sqlite3_bind_int(stmt, 1, disk_id);
 1253: 		sqlite3_bind_int64(stmt, 2, st_p->st_ino);
 1254: 		for (j = 0; j < 2; j++) {
 1255: 			int len;
 1256: 			if (sqlite3_step(stmt) != SQLITE_ROW)
 1257: 				break;
 1258: 			len = sqlite3_column_bytes(stmt, 0);
 1259: 			if (len > MD5_DIGEST_LEN)
 1260: 				len = MD5_DIGEST_LEN;
 1261: 			memcpy(dbsum + MD5_DIGEST_LEN*j, sqlite3_column_blob(stmt, 0), len);
 1262: 			sum_type[j] = sqlite3_column_int(stmt, 1);
 1263: 			dbsize[j] = sqlite3_column_int(stmt, 2);
 1264: 			dbmtime[j] = sqlite3_column_int64(stmt, 3);
 1265: 			dbctime[j] = sqlite3_column_int64(stmt, 4);
 1266: 		}
 1267: 		sqlite3_reset(stmt);
 1268: 		rows = j;
 1269: 		break;
 1270: 	    }
 1271: #endif
 1272: 	default:
 1273: 		return 0;
 1274: 	}
 1275: 
 1276: 	if (sum4)
 1277: 		*sum4 = NULL;
 1278: 	if (sum5)
 1279: 		*sum5 = NULL;
 1280: 	*right_sum_cnt = *wrong_sum_cnt = 0;
 1281: 	for (j = 0; j < rows; j++) {
 1282: 		if (DEBUG_GTE(DB, 3)) {
 1283: 			rprintf(FCLIENT, "DB checksum for %s,%s,%d: %s\n",
 1284: 				big_num(st_p->st_dev), big_num(st_p->st_ino), sum_type[j],
 1285: 				sum_as_hex(sum_type[j], dbsum + MD5_DIGEST_LEN*j, 0));
 1286: 		}
 1287: 
 1288: 		if (sum_type[j] == 4) {
 1289: 			if (!sum4)
 1290: 				continue;
 1291: 			*sum4 = dbsum + MD5_DIGEST_LEN*j;
 1292: 		} else {
 1293: 			if (!sum5)
 1294: 				continue;
 1295: 			*sum5 = dbsum + MD5_DIGEST_LEN*j;
 1296: 		}
 1297: 		if (st_p->st_size == dbsize[j] && st_p->st_mtime == dbmtime[j] && (db_lax || st_p->st_ctime == dbctime[j]))
 1298: 			++*right_sum_cnt;
 1299: 		else
 1300: 			++*wrong_sum_cnt;
 1301: 	}
 1302: 
 1303: 	return rows;
 1304: }
 1305: 
 1306: int db_set_checksum(int mdnum, const STRUCT_STAT *st_p, const char *sum)
 1307: {
 1308: 	unsigned int disk_id;
 1309: 	const char *errmsg = NULL;
 1310: 	int rc = 0;
 1311: 
 1312: 	if (am_receiver || (am_generator && same_db)) {
 1313: 		/* Forward the setting to a single process.  The receiver always
 1314: 		 * forwards to the generator, and the generator will forward to
 1315: 		 * the sender ONLY if this is a local transfer. */
 1316: 		char data[MSG_CHECKSUM_LEN];
 1317: 		SIVAL64(data, 0, st_p->st_dev);
 1318: 		SIVAL64(data, 8, st_p->st_ino);
 1319: 		SIVAL64(data, 16, st_p->st_size);
 1320: 		SIVAL64(data, 24, st_p->st_mtime);
 1321: 		SIVAL64(data, 32, st_p->st_ctime);
 1322: #if MSG_CHECKSUM_LONGS != 5
 1323: #error Fix the setting of checksum long values
 1324: #endif
 1325: 		SIVAL(data, MSG_CHECKSUM_LONGS*8, mdnum);
 1326: 		memcpy(data + MSG_CHECKSUM_LONGS*8 + 4, sum, MAX_DIGEST_LEN);
 1327: 		return send_msg(MSG_CHECKSUM, data, sizeof data, 0);
 1328: 	}
 1329: 
 1330: 	if ((disk_id = get_disk_id(st_p->st_dev)) == 0)
 1331: 		return 0;
 1332: 
 1333: 	switch (use_db) {
 1334: #ifdef USE_MYSQL
 1335: 	case DB_TYPE_MYSQL:
 1336: 		if (transaction_state == 0) {
 1337: 			if (!run_sql("BEGIN"))
 1338: 				return 0;
 1339: 			transaction_state = 1;
 1340: 		}
 1341: 
 1342: 		bind_disk_id = disk_id;
 1343: 		bind_ino = st_p->st_ino;
 1344: 		bind_mdnum = mdnum;
 1345: 		bind_size = st_p->st_size;
 1346: 		bind_mtime = st_p->st_mtime;
 1347: 		bind_ctime = st_p->st_ctime;
 1348: 		memcpy(bind_sum, sum, MD5_DIGEST_LEN);
 1349: 		if (exec_mysql(REP_SUM) == NULL)
 1350: 			errmsg = mysql_error(dbh.mysql);
 1351: 		break;
 1352: #endif
 1353: #ifdef USE_SQLITE
 1354: 	case DB_TYPE_SQLITE: {
 1355: 		sqlite3_stmt *stmt = statements[REP_SUM].sqlite;
 1356: 		int lock_failures = 0;
 1357: 
 1358: 		if (transaction_state == 0) {
 1359: 			if (!run_sql("BEGIN"))
 1360: 				return 0;
 1361: 			transaction_state = 1;
 1362: 		}
 1363: 
 1364: 		sqlite3_bind_int(stmt, 1, disk_id);
 1365: 		sqlite3_bind_int64(stmt, 2, st_p->st_ino);
 1366: 		sqlite3_bind_int(stmt, 3, mdnum);
 1367: 		sqlite3_bind_int64(stmt, 4, st_p->st_size);
 1368: 		sqlite3_bind_int64(stmt, 5, st_p->st_mtime);
 1369: 		sqlite3_bind_int64(stmt, 6, st_p->st_ctime);
 1370: 		sqlite3_bind_blob(stmt, 7, sum, MD5_DIGEST_LEN, SQLITE_TRANSIENT);
 1371: 		while (1) {
 1372: 			rc = sqlite3_step(stmt);
 1373: 			if (rc != SQLITE_BUSY && rc != SQLITE_LOCKED)
 1374: 				break;
 1375: 			if (++lock_failures > MAX_LOCK_FAILURES)
 1376: 				break;
 1377: 			sqlite3_reset(stmt);
 1378: 			msleep(LOCK_FAIL_MSLEEP);
 1379: 		}
 1380: 		if (rc != SQLITE_DONE)
 1381: 			errmsg = sqlite3_errmsg(dbh.sqlite);
 1382: 		sqlite3_reset(stmt);
 1383: 		break;
 1384: 	    }
 1385: #endif
 1386: 	}
 1387: 
 1388: 	if (!errmsg) {
 1389: 		if (DEBUG_GTE(DB, 2)) {
 1390: 			rprintf(FCLIENT, "[%s] Set DB checksum for %s,%s,%d: %s\n",
 1391: 				who_am_i(), big_num(st_p->st_dev), big_num(st_p->st_ino),
 1392: 				md_num, sum_as_hex(md_num, sum, 0));
 1393: 		}
 1394: 	} else {
 1395: 		rprintf(log_code, "[%s] Failed to set checksum for %s,%s,%d: %s (%d) -- closing DB\n",
 1396: 			who_am_i(), big_num(st_p->st_dev), big_num(st_p->st_ino),
 1397: 			md_num, errmsg, rc);
 1398: 		db_disconnect(False);
 1399: 	}
 1400: 
 1401: 	return errmsg ? 0 : 1;
 1402: }
 1403: 
 1404: /* For a delayed-update copy, we set the checksum on the file when it was
 1405:  * inside the partial-dir.  Since renaming the file changes its ctime, we need
 1406:  * to update the ctime to its new value (we can skip this in db_lax mode). */
 1407: int db_update_ctime(UNUSED(int mdnum), const STRUCT_STAT *st_p)
 1408: {
 1409: 	unsigned int disk_id = get_disk_id(st_p->st_dev);
 1410: 
 1411: 	if (disk_id == 0)
 1412: 		return 0;
 1413: 
 1414: 	switch (use_db) {
 1415: #ifdef USE_MYSQL
 1416: 	case DB_TYPE_MYSQL:
 1417: 		bind_ctime = st_p->st_ctime;
 1418: 		bind_disk_id = disk_id;
 1419: 		bind_ino = st_p->st_ino;
 1420: 		bind_mdnum = mdnum;
 1421: 		bind_size = st_p->st_size;
 1422: 		bind_mtime = st_p->st_mtime;
 1423: 		return exec_mysql(UPD_CTIME) != NULL;
 1424: #endif
 1425: #ifdef USE_SQLITE
 1426: 	case DB_TYPE_SQLITE: {
 1427: 		int rc;
 1428: 
 1429: 		sqlite3_stmt *stmt = statements[UPD_CTIME].sqlite;
 1430: 		if (stmt == NULL)
 1431: 			return 0;
 1432: 		sqlite3_bind_int64(stmt, 1, st_p->st_ctime);
 1433: 		sqlite3_bind_int(stmt, 2, disk_id);
 1434: 		sqlite3_bind_int64(stmt, 3, st_p->st_ino);
 1435: 		sqlite3_bind_int(stmt, 4, mdnum);
 1436: 		sqlite3_bind_int64(stmt, 5, st_p->st_size);
 1437: 		sqlite3_bind_int64(stmt, 6, st_p->st_mtime);
 1438: 		rc = sqlite3_step(stmt);
 1439: 		sqlite3_reset(stmt);
 1440: 		return rc == SQLITE_DONE;
 1441: 	    }
 1442: #endif
 1443: 	}
 1444: 
 1445: 	return 0;
 1446: }
 1447: 
 1448: static int db_clean_init(void)
 1449: {
 1450: 	switch (use_db) {
 1451: #ifdef USE_MYSQL
 1452: 	case DB_TYPE_MYSQL: {
 1453: 		MYSQL_BIND binds[MAX_BIND_CNT];
 1454: 		char *sql;
 1455: 
 1456: 		mysql_query(dbh.mysql,
 1457: 			"CREATE TEMPORARY TABLE inode_present ("
 1458: 			" disk_id integer unsigned NOT NULL,"
 1459: 			" ino bigint unsigned NOT NULL,"
 1460: 			" PRIMARY KEY (disk_id,ino)"
 1461: 			") ENGINE=MEMORY"
 1462: 			);
 1463: 
 1464: 		sql="INSERT IGNORE INTO inode_present"
 1465: 		    " SET disk_id = ?, ino = ?";
 1466: 		memset(binds, 0, sizeof binds);
 1467: 		binds[0].buffer_type = MYSQL_TYPE_LONG;
 1468: 		binds[0].buffer = &bind_disk_id;
 1469: 		binds[1].buffer_type = MYSQL_TYPE_LONGLONG;
 1470: 		binds[1].buffer = &bind_ino;
 1471: 		if (!prepare_mysql(INS_PRESENT, binds, 2, sql))
 1472: 			exit_cleanup(RERR_SYNTAX);
 1473: 
 1474: 		sql="DELETE m.*"
 1475: 		    " FROM inode_map AS m"
 1476: 		    " LEFT JOIN inode_present AS p USING(disk_id, ino)"
 1477: 		    " JOIN disk AS d ON(m.disk_id = d.disk_id)"
 1478: 		    " WHERE host = ? AND devno != 0 AND p.disk_id IS NULL AND ctime < ?";
 1479: 		memset(binds, 0, sizeof binds);
 1480: 		binds[0].buffer_type = MYSQL_TYPE_STRING;
 1481: 		binds[0].buffer = &bind_thishost;
 1482: 		binds[0].buffer_length = bind_thishost_len;
 1483: 		binds[1].buffer_type = MYSQL_TYPE_LONGLONG;
 1484: 		binds[1].buffer = &bind_ctime;
 1485: 		if (!prepare_mysql(DEL_SUMS, binds, 2, sql))
 1486: 			exit_cleanup(RERR_SYNTAX);
 1487: 
 1488: 		return 1;
 1489: 	    }
 1490: #endif
 1491: #ifdef USE_SQLITE
 1492: 	case DB_TYPE_SQLITE: {
 1493: 		char *sql;
 1494: 		sql="ATTACH DATABASE '' AS aux1;"; /* Private temp DB, probably in-memory */
 1495: 		if (!run_sql(sql))
 1496: 			exit_cleanup(RERR_IPC);
 1497: 
 1498: 		sql="CREATE TABLE aux1.inode_present ("
 1499: 		    " disk_id integer NOT NULL,"
 1500: 		    " ino bigint NOT NULL,"
 1501: 		    " PRIMARY KEY (disk_id,ino)"
 1502: 		    ")";
 1503: 		if (!run_sql(sql))
 1504: 			exit_cleanup(RERR_IPC);
 1505: 
 1506: 		sql="INSERT OR IGNORE INTO aux1.inode_present"
 1507: 		    " (disk_id, ino)"
 1508: 		    " VALUES (?, ?)";
 1509: 		if (!prepare_sqlite(INS_PRESENT, sql))
 1510: 			exit_cleanup(RERR_IPC);
 1511: 
 1512: 		sql="DELETE FROM inode_map"
 1513: 		    " WHERE ROWID IN ("
 1514: 		    "  SELECT m.ROWID"
 1515: 		    "  FROM inode_map AS m"
 1516: 		    "  LEFT JOIN aux1.inode_present AS p USING(disk_id, ino)"
 1517: 		    "  JOIN disk AS d ON(m.disk_id = d.disk_id)"
 1518: 		    "  WHERE host = ? AND devno != 0 AND p.disk_id IS NULL AND ctime < ?"
 1519: 		    " )";
 1520: 		if (!prepare_sqlite(DEL_SUMS, sql))
 1521: 			exit_cleanup(RERR_IPC);
 1522: 
 1523: 		transaction_state = -1; /* bug work-around -- force transaction off when cleaning XXX */
 1524: 
 1525: 		return 1;
 1526: 	    }
 1527: #endif
 1528: 	}
 1529: 
 1530: 	return 0;
 1531: }
 1532: 
 1533: static int db_note_present(UNUSED(int disk_id), UNUSED(int64 ino))
 1534: {
 1535: 	switch (use_db) {
 1536: #ifdef USE_MYSQL
 1537: 	case DB_TYPE_MYSQL:
 1538: 		bind_disk_id = disk_id;
 1539: 		bind_ino = ino;
 1540: 		return exec_mysql(INS_PRESENT) != NULL;
 1541: #endif
 1542: #ifdef USE_SQLITE
 1543: 	case DB_TYPE_SQLITE: {
 1544: 		int rc;
 1545: 		sqlite3_stmt *stmt = statements[INS_PRESENT].sqlite;
 1546: 		sqlite3_bind_int(stmt, 1, disk_id);
 1547: 		sqlite3_bind_int64(stmt, 2, ino);
 1548: 		rc = sqlite3_step(stmt);
 1549: 		sqlite3_reset(stmt);
 1550: 		return rc == SQLITE_DONE;
 1551: 	    }
 1552: #endif
 1553: 	}
 1554: 
 1555: 	return 0;
 1556: }
 1557: 
 1558: /* This function requires the user to have populated all disk_id+inode pairs
 1559:  * into the inode_present table. */
 1560: static int db_clean_inodes(UNUSED(time_t start_time))
 1561: {
 1562: 	int del_cnt = 0;
 1563: 
 1564: 	/* The extra ctime < start_time check ensures that brand-new checksums that
 1565: 	 * were added after the start of our cleaning run are not removed. */
 1566: 	switch (use_db) {
 1567: #ifdef USE_MYSQL
 1568: 	case DB_TYPE_MYSQL: {
 1569: 		MYSQL_STMT *stmt;
 1570: 		bind_ctime = start_time;
 1571: 		stmt = exec_mysql(DEL_SUMS);
 1572: 		if (stmt != NULL)
 1573: 			del_cnt = mysql_affected_rows(dbh.mysql);
 1574: 		break;
 1575: 	    }
 1576: #endif
 1577: #ifdef USE_SQLITE
 1578: 	case DB_TYPE_SQLITE: {
 1579: 		int rc;
 1580: 		sqlite3_stmt *stmt = statements[DEL_SUMS].sqlite;
 1581: 		sqlite3_bind_text(stmt, 1, bind_thishost, bind_thishost_len, SQLITE_STATIC);
 1582: 		sqlite3_bind_int64(stmt, 2, start_time);
 1583: 		rc = sqlite3_step(stmt);
 1584: 		if (rc == SQLITE_DONE)
 1585: 			del_cnt = sqlite3_changes(dbh.sqlite);
 1586: 		sqlite3_reset(stmt);
 1587: 		break;
 1588: 	    }
 1589: #endif
 1590: 	}
 1591: 
 1592: 	return del_cnt;
 1593: }
 1594: 
 1595: static int abs_path(char *buf, int bufsiz, const char *curdir, const char *dir)
 1596: {
 1597: 	if (*dir == '/')
 1598: 		strlcpy(buf, dir, bufsiz);
 1599: 	else {
 1600: 		int len = snprintf(buf, bufsiz, "%s/%s", curdir, dir);
 1601: 		assert(len > 0); /* silence a compiler warning */
 1602: 	}
 1603: 
 1604: 	return clean_fname(buf, CFN_DROP_TRAILING_DOT_DIR | CFN_COLLAPSE_DOT_DOT_DIRS);
 1605: }
 1606: 
 1607: static struct name_list *new_name(const char *basename, const char *filename)
 1608: {
 1609: 	struct name_list *n;
 1610: 	int blen = strlen(basename);
 1611: 	int slen = filename ? (int)strlen(filename) : -1;
 1612: 	int len = blen + 1 + slen;
 1613: 
 1614: 	if (len >= MAXPATHLEN) {
 1615: 		if (filename)
 1616: 			rprintf(FERROR, "Filename too long: %s/%s\n", basename, filename);
 1617: 		else
 1618: 			rprintf(FERROR, "Filename too long: %s\n", basename);
 1619: 		return NULL;
 1620: 	}
 1621: 
 1622: 	n = (struct name_list *)new_array(char, sizeof (struct name_list) + len);
 1623: 
 1624: 	memcpy(n->name, basename, blen);
 1625: 	if (filename) {
 1626: 		n->name[blen] = '/';
 1627: 		memcpy(n->name + 1 + blen, filename, slen);
 1628: 	}
 1629: 	n->name[len] = '\0';
 1630: 	n->next = NULL;
 1631: 
 1632: 	return n;
 1633: }
 1634: 
 1635: static int name_compare(const void *n1, const void *n2)
 1636: {
 1637: 	struct name_list *p1 = *(struct name_list **)n1;
 1638: 	struct name_list *p2 = *(struct name_list **)n2;
 1639: 	return strcmp(p1->name, p2->name);
 1640: }
 1641: 
 1642: static struct name_list *get_sorted_names(const char *dir)
 1643: {
 1644: 	struct name_list *add, **sortbuf, *names = NULL, *prior_name = NULL;
 1645: 	struct dirent *di;
 1646: 	int cnt = 0;
 1647: 	DIR *d;
 1648: 
 1649: 	if (!(d = opendir("."))) {
 1650: 		rprintf(FERROR, "Unable to opendir %s: %s\n", dir, strerror(errno));
 1651: 		return NULL;
 1652: 	}
 1653: 	while ((di = readdir(d)) != NULL) {
 1654: 		char *dname = d_name(di);
 1655: 		if (dname[0] == '.' && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0')))
 1656: 			continue;
 1657: 		if (!(add = new_name(dname, NULL)))
 1658: 			continue;
 1659: 		if (prior_name)
 1660: 			prior_name->next = add;
 1661: 		else
 1662: 			names = add;
 1663: 		prior_name = add;
 1664: 		cnt++;
 1665: 	}
 1666: 	closedir(d);
 1667: 
 1668: 	if (cnt) {
 1669: 		int j;
 1670: 
 1671: 		sortbuf = new_array(struct name_list *, cnt);
 1672: 		for (j = 0; j < cnt; j++) {
 1673: 			sortbuf[j] = names;
 1674: 			names = names->next;
 1675: 		}
 1676: 
 1677: 		qsort(sortbuf, cnt, PTR_SIZE, name_compare);
 1678: 
 1679: 		names = prior_name = NULL;
 1680: 		for (j = 0; j < cnt; j++) {
 1681: 			add = sortbuf[j];
 1682: 			if (prior_name)
 1683: 				prior_name->next = add;
 1684: 			else
 1685: 				names = add;
 1686: 			prior_name = add;
 1687: 		}
 1688: 
 1689: 		if (prior_name)
 1690: 			prior_name->next = NULL;
 1691: 		free(sortbuf);
 1692: 	}
 1693: 
 1694: 	return names;
 1695: }
 1696: 
 1697: static inline int sums_ne(const char *sum1, const char *sum2)
 1698: {
 1699: 	return memcmp(sum1, sum2, MD5_DIGEST_LEN) != 0;
 1700: }
 1701: 
 1702: /* Returns 1 if there is a checksum change, else 0. */
 1703: static int mention_file(const char *dir, const char *name, int right_cnt, int wrong_cnt,
 1704: 			const char *dbsum4, const char *dbsum5, const char *sum4, const char *sum5)
 1705: {
 1706: 	char *info_str = wrong_cnt && !right_cnt ? "!i " : "   ";
 1707: 	char *md4_str = !db_do_md4 ? NULL : !dbsum4 ? "+4 " : !sum4 ? "?4 " : sums_ne(sum4, dbsum4) ? "!4 " : "   ";
 1708: 	char *md5_str = !db_do_md5 ? NULL : !dbsum5 ? "+5 " : !sum5 ? "?5 " : sums_ne(sum5, dbsum5) ? "!5 " : "   ";
 1709: 	int chg = *info_str != ' ' || (md4_str && *md4_str != ' ') || (md5_str && *md5_str != ' ');
 1710: 	if (chg || db_output_unchanged) {
 1711: 		if (db_output_info) {
 1712: 			fputs(info_str, stdout);
 1713: 			if (md4_str)
 1714: 				fputs(md4_str, stdout);
 1715: 			if (md5_str)
 1716: 				fputs(md5_str, stdout);
 1717: 		}
 1718: 		if (db_output_sum) {
 1719: 			if (db_do_md4)
 1720: 				printf("%s ", sum_as_hex(4, sum4, 0));
 1721: 			if (db_do_md5)
 1722: 				printf("%s ", sum_as_hex(5, sum5, 0));
 1723: 		}
 1724: 		if (db_output_name) {
 1725: 			if (db_output_sum)
 1726: 				putchar(' '); /* We want 2 spaces, like md5sum. */
 1727: 			if (*dir != '.' || dir[1]) {
 1728: 				fputs(dir, stdout);
 1729: 				putchar('/');
 1730: 			}
 1731: 			puts(name);
 1732: 		}
 1733: 	}
 1734: 
 1735: 	return chg;
 1736: }
 1737: 
 1738: NORETURN void run_dbonly(const char **args)
 1739: {
 1740: 	char start_dir[MAXPATHLEN], dirbuf[MAXPATHLEN];
 1741: 	int need_sum_cnt, start_dir_len;
 1742: 	struct name_list *prior_dir;
 1743: 	struct name_list *names;
 1744: 	time_t clean_start = 0;
 1745: 	int exit_code = 0;
 1746: 
 1747: 	checksum_type = 5;
 1748: 
 1749: 	need_sum_cnt = db_do_md4 + db_do_md5;
 1750: 
 1751: 	if (!db_read_config(FERROR, db_config) || !db_connect(1))
 1752: 		exit_cleanup(RERR_FILEIO);
 1753: 
 1754: 	if (db_clean) {
 1755: 		clean_start = time(NULL);
 1756: 		db_clean_init();
 1757: 	}
 1758: 
 1759: 	if (getcwd(start_dir, sizeof start_dir - 1) == NULL) {
 1760: 		rsyserr(FERROR, errno, "getcwd()");
 1761: 		exit_cleanup(RERR_FILESELECT);
 1762: 	}
 1763: 	start_dir_len = strlen(start_dir);
 1764: 
 1765: 	if (args) {
 1766: 		prior_dir = NULL;
 1767: 		while (*args) {
 1768: 			struct name_list *add;
 1769: 			if (abs_path(dirbuf, sizeof dirbuf, start_dir, *args++) <= 0)
 1770: 				continue;
 1771: 			if (!(add = new_name(dirbuf, NULL)))
 1772: 				continue;
 1773: 			if (prior_dir)
 1774: 				prior_dir->next = add;
 1775: 			else
 1776: 				dirs_list = add;
 1777: 			prior_dir = add;
 1778: 		}
 1779: 	} else
 1780: 		dirs_list = new_name(start_dir, NULL);
 1781: 
 1782: 	prior_dir = NULL;
 1783: 	while (dirs_list) {
 1784: 		struct name_list *subdirs, *prior_subdir, *prior_name;
 1785: 		const char *dir = dirs_list->name;
 1786: 		const char *reldir = dir;
 1787: 
 1788: 		if (prior_dir)
 1789: 			free((void*)prior_dir);
 1790: 		prior_dir = dirs_list;
 1791: 		dirs_list = dirs_list->next;
 1792: 
 1793: 		if (strncmp(reldir, start_dir, start_dir_len) == 0) {
 1794: 			if (reldir[start_dir_len] == '\0')
 1795: 				reldir = ".";
 1796: 			else if (reldir[start_dir_len] == '/')
 1797: 				reldir += start_dir_len + 1;
 1798: 		}
 1799: 		if (db_output_dirs)
 1800: 			printf("... %s/ ...\n", reldir);
 1801: 
 1802: 		if (chdir(dir) < 0) {
 1803: 			rprintf(FERROR, "Unable to chdir to %s: %s\n", dir, strerror(errno));
 1804: 			continue;
 1805: 		}
 1806: 		if (!(names = get_sorted_names(dir)))
 1807: 			continue;
 1808: 
 1809: 		subdirs = prior_subdir = prior_name = NULL;
 1810: 		while (names) {
 1811: 			STRUCT_STAT st;
 1812: 			char *dbsum4, *sum4, sumbuf4[MD5_DIGEST_LEN];
 1813: 			char *dbsum5, *sum5, sumbuf5[MD5_DIGEST_LEN];
 1814: 			int right_sum_cnt, wrong_sum_cnt;
 1815: 			const char *name = names->name;
 1816: 			unsigned int disk_id;
 1817: 
 1818: 			if (prior_name)
 1819: 				free((void*)prior_name);
 1820: 			prior_name = names;
 1821: 			names = names->next;
 1822: 
 1823: 			dbsum4 = dbsum5 = sum4 = sum5 = NULL;
 1824: 
 1825: 			if (lstat(name, &st) < 0) {
 1826: 				rprintf(FERROR, "Failed to lstat(%s): %s\n", name, strerror(errno));
 1827: 				continue;
 1828: 			}
 1829: 			if (S_ISLNK(st.st_mode))
 1830: 				continue;
 1831: 			if (S_ISDIR(st.st_mode)) {
 1832: 				/* add optional excluding of things like /^(CVS|\.svn|\.git|\.bzr)$/; */
 1833: 				if (recurse) {
 1834: 					struct name_list *add = new_name(dir, name);
 1835: 					if (add) {
 1836: 						if (prior_subdir)
 1837: 							prior_subdir->next = add;
 1838: 						else
 1839: 							subdirs = add;
 1840: 						prior_subdir = add;
 1841: 					}
 1842: 				}
 1843: 				continue;
 1844: 			}
 1845: 			if (!S_ISREG(st.st_mode))
 1846: 				continue;
 1847: 
 1848: 			if (!(disk_id = get_disk_id(st.st_dev)))
 1849: 				continue;
 1850: 			if (db_clean) {
 1851: 				db_note_present(disk_id, st.st_ino);
 1852: 				if (!db_update && !db_check)
 1853: 					continue;
 1854: 			}
 1855: 			db_get_both_checksums(&st, &right_sum_cnt, &wrong_sum_cnt,
 1856: 					      db_do_md4 ? &dbsum4 : NULL, db_do_md5 ? &dbsum5 : NULL);
 1857: 
 1858: 			if (!db_check && right_sum_cnt == need_sum_cnt) {
 1859: 				mention_file(reldir, name, right_sum_cnt, wrong_sum_cnt, dbsum4, dbsum5, dbsum4, dbsum5);
 1860: 				continue;
 1861: 			}
 1862: 
 1863: 			if (db_update || (db_check && right_sum_cnt) || db_output_sum) {
 1864: 				uchar *data;
 1865: 				int32 remainder;
 1866: 				md_context m4;
 1867: 				MD5_CTX m5;
 1868: 				struct map_struct *buf;
 1869: 				OFF_T off, len = st.st_size;
 1870: 				int fd = do_open(name, O_RDONLY, 0);
 1871: 
 1872: 				if (fd < 0) {
 1873: 					rprintf(FERROR, "ERROR: unable to read %s: %s\n", name, strerror(errno));
 1874: 					continue;
 1875: 				}
 1876: 
 1877: 				if (db_do_md4)
 1878: 					mdfour_begin(&m4);
 1879: 				if (db_do_md5)
 1880: 					MD5_Init(&m5);
 1881: 
 1882: 				buf = map_file(fd, len, MAX_MAP_SIZE, CSUM_CHUNK);
 1883: 
 1884: 				for (off = 0; off + CSUM_CHUNK <= len; off += CSUM_CHUNK) {
 1885: 					data = (uchar*)map_ptr(buf, off, CSUM_CHUNK);
 1886: 					if (db_do_md4)
 1887: 						mdfour_update(&m4, data, CSUM_CHUNK);
 1888: 					if (db_do_md5)
 1889: 						MD5_Update(&m5, data, CSUM_CHUNK);
 1890: 				}
 1891: 
 1892: 				remainder = (int32)(len - off);
 1893: 				data = (uchar*)map_ptr(buf, off, remainder);
 1894: 				if (db_do_md4) {
 1895: 					mdfour_update(&m4, data, remainder);
 1896: 					mdfour_result(&m4, (uchar*)(sum4 = sumbuf4));
 1897: 				}
 1898: 				if (db_do_md5) {
 1899: 					MD5_Update(&m5, data, remainder);
 1900: 					MD5_Final((uchar*)(sum5 = sumbuf5), &m5);
 1901: 				}
 1902: 
 1903: 				close(fd);
 1904: 				unmap_file(buf);
 1905: 			}
 1906: 
 1907: 			int chg = mention_file(reldir, name, right_sum_cnt, wrong_sum_cnt, dbsum4, dbsum5, sum4, sum5);
 1908: 			if (!chg) {
 1909: 				/* Only db_check should get here... */
 1910: 			} else if (!db_update) {
 1911: 				exit_code = 1;
 1912: 			} else {
 1913: 				int fail = 0;
 1914: 				if (db_do_md4 && !db_set_checksum(4, &st, sum4))
 1915: 					fail = 1;
 1916: 				if (db_do_md5 && !db_set_checksum(5, &st, sum5))
 1917: 					fail = 1;
 1918: 				if (fail) {
 1919: 					fprintf(stderr, "Failed to set checksum on %s/%s\n", reldir, name);
 1920: 					exit_cleanup(RERR_FILEIO);
 1921: 				}
 1922: 			}
 1923: 		}
 1924: 		if (prior_name)
 1925: 			free((void*)prior_name);
 1926: 
 1927: 		if (recurse && subdirs) {
 1928: 			prior_subdir->next = dirs_list;
 1929: 			dirs_list = subdirs;
 1930: 		}
 1931: 	}
 1932: 	if (prior_dir)
 1933: 		free((void*)prior_dir);
 1934: 
 1935: 	if (db_clean) {
 1936: 		int rows = db_clean_inodes(clean_start);
 1937: 		if (db_output_msgs)
 1938: 			printf("Cleaned out %d old inode%s.\n", rows, rows == 1 ? "" : "s");
 1939: 	}
 1940: 
 1941: 	db_disconnect(True);
 1942: 	exit(exit_code);
 1943: }

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