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