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>