Annotation of embedaddon/rsync/db.c, revision 1.1
1.1 ! misho 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>