2 * Copyright (C) Ralph Boehme 2013
3 * All Rights Reserved. See COPYING.
8 #endif /* HAVE_CONFIG_H */
10 #undef _FORTIFY_SOURCE
17 #include <sys/socket.h>
18 #include <sys/param.h>
20 #include <netinet/in.h>
22 #include <netinet/tcp.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
28 #include <arpa/inet.h>
31 #include <mysqld_error.h>
34 #include <atalk/logger.h>
35 #include <atalk/adouble.h>
36 #include <atalk/util.h>
37 #include <atalk/cnid_mysql_private.h>
38 #include <atalk/cnid_bdb_private.h>
39 #include <atalk/errchk.h>
40 #include <atalk/globals.h>
41 #include <atalk/volume.h>
44 static MYSQL_BIND lookup_param[4], lookup_result[5];
45 static MYSQL_BIND add_param[4], put_param[5];
48 * Prepared statement parameters
50 static char stmt_param_name[MAXPATHLEN];
51 static unsigned long stmt_param_name_len;
52 static unsigned long long stmt_param_id;
53 static unsigned long long stmt_param_did;
54 static unsigned long long stmt_param_dev;
55 static unsigned long long stmt_param_ino;
58 * lookup result parameters
60 static unsigned long long lookup_result_id;
61 static unsigned long long lookup_result_did;
62 static char lookup_result_name[MAXPATHLEN];
63 static unsigned long lookup_result_name_len;
64 static unsigned long long lookup_result_dev;
65 static unsigned long long lookup_result_ino;
67 static int init_prepared_stmt_lookup(CNID_mysql_private *db)
72 lookup_param[0].buffer_type = MYSQL_TYPE_STRING;
73 lookup_param[0].buffer = &stmt_param_name;
74 lookup_param[0].buffer_length = sizeof(stmt_param_name);
75 lookup_param[0].length = &stmt_param_name_len;
77 lookup_param[1].buffer_type = MYSQL_TYPE_LONGLONG;
78 lookup_param[1].buffer = &stmt_param_did;
79 lookup_param[1].is_unsigned = true;
81 lookup_param[2].buffer_type = MYSQL_TYPE_LONGLONG;
82 lookup_param[2].buffer = &stmt_param_dev;
83 lookup_param[2].is_unsigned = true;
85 lookup_param[3].buffer_type = MYSQL_TYPE_LONGLONG;
86 lookup_param[3].buffer = &stmt_param_ino;
87 lookup_param[3].is_unsigned = true;
89 lookup_result[0].buffer_type = MYSQL_TYPE_LONGLONG;
90 lookup_result[0].buffer = &lookup_result_id;
91 lookup_result[0].is_unsigned = true;
93 lookup_result[1].buffer_type = MYSQL_TYPE_LONGLONG;
94 lookup_result[1].buffer = &lookup_result_did;
95 lookup_result[1].is_unsigned = true;
97 lookup_result[2].buffer_type = MYSQL_TYPE_STRING;
98 lookup_result[2].buffer = &lookup_result_name;
99 lookup_result[2].buffer_length = sizeof(lookup_result_name);
100 lookup_result[2].length = &lookup_result_name_len;
102 lookup_result[3].buffer_type = MYSQL_TYPE_LONGLONG;
103 lookup_result[3].buffer = &lookup_result_dev;
104 lookup_result[3].is_unsigned = true;
106 lookup_result[4].buffer_type = MYSQL_TYPE_LONGLONG;
107 lookup_result[4].buffer = &lookup_result_ino;
108 lookup_result[4].is_unsigned = true;
110 EC_NULL( db->cnid_lookup_stmt = mysql_stmt_init(db->cnid_mysql_con) );
111 EC_NEG1( asprintf(&sql,
112 "SELECT Id,Did,Name,DevNo,InodeNo FROM %s "
113 "WHERE (Name=? AND Did=?) OR (DevNo=? AND InodeNo=?)",
114 db->cnid_mysql_voluuid_str) );
115 EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_lookup_stmt, sql, strlen(sql)) );
116 EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_lookup_stmt, lookup_param) );
124 static int init_prepared_stmt_add(CNID_mysql_private *db)
129 EC_NULL( db->cnid_add_stmt = mysql_stmt_init(db->cnid_mysql_con) );
130 EC_NEG1( asprintf(&sql,
131 "INSERT INTO %s (Name,Did,DevNo,InodeNo) VALUES(?,?,?,?)",
132 db->cnid_mysql_voluuid_str) );
134 add_param[0].buffer_type = MYSQL_TYPE_STRING;
135 add_param[0].buffer = &stmt_param_name;
136 add_param[0].buffer_length = sizeof(stmt_param_name);
137 add_param[0].length = &stmt_param_name_len;
139 add_param[1].buffer_type = MYSQL_TYPE_LONGLONG;
140 add_param[1].buffer = &stmt_param_did;
141 add_param[1].is_unsigned = true;
143 add_param[2].buffer_type = MYSQL_TYPE_LONGLONG;
144 add_param[2].buffer = &stmt_param_dev;
145 add_param[2].is_unsigned = true;
147 add_param[3].buffer_type = MYSQL_TYPE_LONGLONG;
148 add_param[3].buffer = &stmt_param_ino;
149 add_param[3].is_unsigned = true;
151 EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_add_stmt, sql, strlen(sql)) );
152 EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_add_stmt, add_param) );
160 static int init_prepared_stmt_put(CNID_mysql_private *db)
165 EC_NULL( db->cnid_put_stmt = mysql_stmt_init(db->cnid_mysql_con) );
166 EC_NEG1( asprintf(&sql,
167 "INSERT INTO %s (Id,Name,Did,DevNo,InodeNo) VALUES(?,?,?,?,?)",
168 db->cnid_mysql_voluuid_str) );
170 put_param[0].buffer_type = MYSQL_TYPE_LONGLONG;
171 put_param[0].buffer = &stmt_param_id;
172 put_param[0].is_unsigned = true;
174 put_param[1].buffer_type = MYSQL_TYPE_STRING;
175 put_param[1].buffer = &stmt_param_name;
176 put_param[1].buffer_length = sizeof(stmt_param_name);
177 put_param[1].length = &stmt_param_name_len;
179 put_param[2].buffer_type = MYSQL_TYPE_LONGLONG;
180 put_param[2].buffer = &stmt_param_did;
181 put_param[2].is_unsigned = true;
183 put_param[3].buffer_type = MYSQL_TYPE_LONGLONG;
184 put_param[3].buffer = &stmt_param_dev;
185 put_param[3].is_unsigned = true;
187 put_param[4].buffer_type = MYSQL_TYPE_LONGLONG;
188 put_param[4].buffer = &stmt_param_ino;
189 put_param[4].is_unsigned = true;
191 EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_put_stmt, sql, strlen(sql)) );
192 EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_put_stmt, put_param) );
200 static int init_prepared_stmt(CNID_mysql_private *db)
204 EC_ZERO( init_prepared_stmt_lookup(db) );
205 EC_ZERO( init_prepared_stmt_add(db) );
206 EC_ZERO( init_prepared_stmt_put(db) );
212 static void close_prepared_stmt(CNID_mysql_private *db)
214 mysql_stmt_close(db->cnid_lookup_stmt);
215 mysql_stmt_close(db->cnid_add_stmt);
216 mysql_stmt_close(db->cnid_put_stmt);
219 static int cnid_mysql_execute(MYSQL *con, char *fmt, ...)
226 if (vasprintf(&sql, fmt, ap) == -1)
230 LOG(log_maxdebug, logtype_cnid, "SQL: %s", sql);
232 rv = mysql_query(con, sql);
235 LOG(log_info, logtype_cnid, "MySQL query \"%s\", error: %s", sql, mysql_error(con));
242 int cnid_mysql_delete(struct _cnid_db *cdb, const cnid_t id)
245 CNID_mysql_private *db;
247 if (!cdb || !(db = cdb->cnid_db_private) || !id) {
248 LOG(log_error, logtype_cnid, "cnid_mysql_delete: Parameter error");
249 errno = CNID_ERR_PARAM;
253 LOG(log_debug, logtype_cnid, "cnid_mysql_delete(%" PRIu32 "): BEGIN", ntohl(id));
255 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
256 "DELETE FROM %s WHERE Id=%" PRIu32,
257 db->cnid_mysql_voluuid_str,
260 LOG(log_debug, logtype_cnid, "cnid_mysql_delete(%" PRIu32 "): END", ntohl(id));
266 void cnid_mysql_close(struct _cnid_db *cdb)
268 CNID_mysql_private *db;
271 LOG(log_error, logtype_cnid, "cnid_close called with NULL argument !");
275 if ((db = cdb->cnid_db_private) != NULL) {
276 LOG(log_debug, logtype_cnid, "closing database connection for volume '%s'",
277 cdb->cnid_db_vol->v_localname);
279 free(db->cnid_mysql_voluuid_str);
281 close_prepared_stmt(db);
283 if (db->cnid_mysql_con)
284 mysql_close(db->cnid_mysql_con);
293 int cnid_mysql_update(struct _cnid_db *cdb,
295 const struct stat *st,
301 CNID_mysql_private *db;
304 if (!cdb || !(db = cdb->cnid_db_private) || !id || !st || !name) {
305 LOG(log_error, logtype_cnid, "cnid_update: Parameter error");
306 errno = CNID_ERR_PARAM;
310 if (len > MAXPATHLEN) {
311 LOG(log_error, logtype_cnid, "cnid_update: Path name is too long");
312 errno = CNID_ERR_PATH;
316 uint64_t dev = st->st_dev;
317 uint64_t ino = st->st_ino;
320 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
321 "DELETE FROM %s WHERE Id=%" PRIu32,
322 db->cnid_mysql_voluuid_str,
324 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
325 "DELETE FROM %s WHERE Did=%" PRIu32 " AND Name='%s'",
326 db->cnid_mysql_voluuid_str, ntohl(did), name) );
327 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
328 "DELETE FROM %s WHERE DevNo=%" PRIu64 " AND InodeNo=%" PRIu64,
329 db->cnid_mysql_voluuid_str, dev, ino) );
331 stmt_param_id = ntohl(id);
332 strncpy(stmt_param_name, name, sizeof(stmt_param_name));
333 stmt_param_name_len = len;
334 stmt_param_did = ntohl(did);
335 stmt_param_dev = dev;
336 stmt_param_ino = ino;
338 if (mysql_stmt_execute(db->cnid_put_stmt)) {
339 switch (mysql_stmt_errno(db->cnid_put_stmt)) {
343 * between deletion and insert another process may have inserted
348 close_prepared_stmt(db);
349 EC_ZERO( init_prepared_stmt(db) );
355 update_id = mysql_stmt_insert_id(db->cnid_put_stmt);
356 } while (update_id != ntohl(id));
362 cnid_t cnid_mysql_lookup(struct _cnid_db *cdb,
363 const struct stat *st,
369 CNID_mysql_private *db;
370 cnid_t id = CNID_INVALID;
371 bool have_result = false;
373 if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
374 LOG(log_error, logtype_cnid, "cnid_mysql_lookup: Parameter error");
375 errno = CNID_ERR_PARAM;
379 if (len > MAXPATHLEN) {
380 LOG(log_error, logtype_cnid, "cnid_mysql_lookup: Path name is too long");
381 errno = CNID_ERR_PATH;
385 uint64_t dev = st->st_dev;
386 uint64_t ino = st->st_ino;
387 cnid_t hint = db->cnid_mysql_hint;
389 LOG(log_maxdebug, logtype_cnid, "cnid_mysql_lookup(did: %" PRIu32 ", name: \"%s\", hint: %" PRIu32 "): START",
390 ntohl(did), name, ntohl(hint));
392 strncpy(stmt_param_name, name, sizeof(stmt_param_name));
393 stmt_param_name_len = len;
394 stmt_param_did = ntohl(did);
395 stmt_param_dev = dev;
396 stmt_param_ino = ino;
399 if (mysql_stmt_execute(db->cnid_lookup_stmt)) {
400 switch (mysql_stmt_errno(db->cnid_lookup_stmt)) {
402 close_prepared_stmt(db);
403 EC_ZERO( init_prepared_stmt(db) );
409 EC_ZERO_LOG( mysql_stmt_store_result(db->cnid_lookup_stmt) );
411 EC_ZERO_LOG( mysql_stmt_bind_result(db->cnid_lookup_stmt, lookup_result) );
413 uint64_t retdev, retino;
414 cnid_t retid, retdid;
417 switch (mysql_stmt_num_rows(db->cnid_lookup_stmt)) {
421 LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: name: '%s', did: %u is not in the CNID database",
423 errno = CNID_DBD_RES_NOTFOUND;
427 /* either both OR clauses matched the same id or only one matched, handled below */
428 EC_ZERO( mysql_stmt_fetch(db->cnid_lookup_stmt) );
432 /* a mismatch, delete both and return not found */
433 while (mysql_stmt_fetch(db->cnid_lookup_stmt) == 0) {
434 if (cnid_mysql_delete(cdb, htonl((cnid_t)lookup_result_id))) {
435 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
440 errno = CNID_DBD_RES_NOTFOUND;
448 retid = htonl(lookup_result_id);
449 retdid = htonl(lookup_result_did);
450 retname = lookup_result_name;
451 retdev = lookup_result_dev;
452 retino = lookup_result_ino;
454 if (retdid != did || STRCMP(retname, !=, name)) {
455 LOG(log_debug, logtype_cnid,
456 "cnid_mysql_lookup(CNID hint: %" PRIu32 ", DID: %" PRIu32 ", name: \"%s\"): server side mv oder reused inode",
457 ntohl(hint), ntohl(did), name);
459 if (cnid_mysql_delete(cdb, retid) != 0) {
460 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
464 errno = CNID_DBD_RES_NOTFOUND;
467 LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: server side mv, got hint, updating");
468 if (cnid_mysql_update(cdb, retid, st, did, name, len) != 0) {
469 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
474 } else if (retdev != dev || retino != ino) {
475 LOG(log_debug, logtype_cnid, "cnid_mysql_lookup(DID:%u, name: \"%s\"): changed dev/ino",
477 if (cnid_mysql_delete(cdb, retid) != 0) {
478 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
482 errno = CNID_DBD_RES_NOTFOUND;
485 /* everythings good */
490 LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: id: %" PRIu32, ntohl(id));
492 mysql_stmt_free_result(db->cnid_lookup_stmt);
498 cnid_t cnid_mysql_add(struct _cnid_db *cdb,
499 const struct stat *st,
506 CNID_mysql_private *db;
507 cnid_t id = CNID_INVALID;
508 MYSQL_RES *result = NULL;
512 if (!cdb || !(db = cdb->cnid_db_private) || !st || !name) {
513 LOG(log_error, logtype_cnid, "cnid_mysql_add: Parameter error");
514 errno = CNID_ERR_PARAM;
518 if (len > MAXPATHLEN) {
519 LOG(log_error, logtype_cnid, "cnid_mysql_add: Path name is too long");
520 errno = CNID_ERR_PATH;
524 uint64_t dev = st->st_dev;
525 uint64_t ino = st->st_ino;
526 db->cnid_mysql_hint = hint;
528 LOG(log_maxdebug, logtype_cnid, "cnid_mysql_add(did: %" PRIu32 ", name: \"%s\", hint: %" PRIu32 "): START",
529 ntohl(did), name, ntohl(hint));
532 if ((id = cnid_mysql_lookup(cdb, st, did, name, len)) == CNID_INVALID) {
533 if (errno == CNID_ERR_DB)
536 * If the CNID set overflowed before (CNID_MYSQL_FLAG_DEPLETED)
537 * ignore the CNID "hint" taken from the AppleDouble file
539 if (!db->cnid_mysql_hint || (db->cnid_mysql_flags & CNID_MYSQL_FLAG_DEPLETED)) {
540 stmt = db->cnid_add_stmt;
542 stmt = db->cnid_put_stmt;
543 stmt_param_id = ntohl(db->cnid_mysql_hint);
545 strncpy(stmt_param_name, name, sizeof(stmt_param_name));
546 stmt_param_name_len = len;
547 stmt_param_did = ntohl(did);
548 stmt_param_dev = dev;
549 stmt_param_ino = ino;
551 if (mysql_stmt_execute(stmt)) {
552 switch (mysql_stmt_errno(stmt)) {
556 close_prepared_stmt(db);
557 EC_ZERO( init_prepared_stmt(db) );
564 * between lookup and insert another process may have inserted
567 if (db->cnid_mysql_hint)
568 db->cnid_mysql_hint = CNID_INVALID;
572 lastid = mysql_stmt_insert_id(stmt);
574 if (lastid > 0xffffffff) {
575 /* CNID set ist depleted, restart from scratch */
576 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
578 "UPDATE volumes SET Depleted=1 WHERE VolUUID='%s';"
580 "ALTER TABLE %s AUTO_INCREMENT = 17;"
582 db->cnid_mysql_voluuid_str,
583 db->cnid_mysql_voluuid_str,
584 db->cnid_mysql_voluuid_str) );
585 db->cnid_mysql_flags |= CNID_MYSQL_FLAG_DEPLETED;
588 result = mysql_store_result(db->cnid_mysql_con);
590 mysql_free_result(result);
591 } while (mysql_next_result(db->cnid_mysql_con) == 0);
595 /* Finally assign our result */
596 id = htonl((uint32_t)lastid);
598 } while (id == CNID_INVALID);
601 LOG(log_debug, logtype_cnid, "cnid_mysql_add: id: %" PRIu32, ntohl(id));
604 mysql_free_result(result);
608 cnid_t cnid_mysql_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t len)
611 CNID_mysql_private *db;
612 cnid_t id = CNID_INVALID;
613 MYSQL_RES *result = NULL;
616 if (!cdb || !(db = cdb->cnid_db_private) || !name) {
617 LOG(log_error, logtype_cnid, "cnid_mysql_get: Parameter error");
618 errno = CNID_ERR_PARAM;
622 if (len > MAXPATHLEN) {
623 LOG(log_error, logtype_cnid, "cnid_mysql_get: name is too long");
624 errno = CNID_ERR_PATH;
628 LOG(log_debug, logtype_cnid, "cnid_mysql_get(did: %" PRIu32 ", name: \"%s\"): START",
631 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
633 "WHERE Name='%s' AND Did=%" PRIu32,
634 db->cnid_mysql_voluuid_str,
638 if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
639 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
644 if (mysql_num_rows(result)) {
645 row = mysql_fetch_row(result);
646 id = htonl(atoi(row[0]));
650 LOG(log_debug, logtype_cnid, "cnid_mysql_get: id: %" PRIu32, ntohl(id));
653 mysql_free_result(result);
658 char *cnid_mysql_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
661 CNID_mysql_private *db;
662 MYSQL_RES *result = NULL;
665 if (!cdb || !(db = cdb->cnid_db_private)) {
666 LOG(log_error, logtype_cnid, "cnid_mysql_get: Parameter error");
667 errno = CNID_ERR_PARAM;
671 EC_NEG1( cnid_mysql_execute(
673 "SELECT Did,Name FROM %s WHERE Id=%" PRIu32,
674 db->cnid_mysql_voluuid_str, ntohl(*id)) );
676 EC_NULL( result = mysql_store_result(db->cnid_mysql_con) );
678 if (mysql_num_rows(result) != 1)
681 row = mysql_fetch_row(result);
683 *id = htonl(atoi(row[0]));
684 strncpy(buffer, row[1], len);
688 mysql_free_result(result);
698 * Caller passes buffer where we will store the db stamp
700 int cnid_mysql_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
703 CNID_mysql_private *db;
704 MYSQL_RES *result = NULL;
707 if (!cdb || !(db = cdb->cnid_db_private)) {
708 LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
709 errno = CNID_ERR_PARAM;
716 if (cnid_mysql_execute(db->cnid_mysql_con,
717 "SELECT Stamp FROM volumes WHERE VolPath='%s'",
718 cdb->cnid_db_vol->v_path)) {
719 if (mysql_errno(db->cnid_mysql_con) != ER_DUP_ENTRY) {
720 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
725 if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
726 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
730 if (!mysql_num_rows(result)) {
731 LOG(log_error, logtype_cnid, "Can't get DB stamp for volumes \"%s\"", cdb->cnid_db_vol->v_path);
734 row = mysql_fetch_row(result);
735 memcpy(buffer, row[0], len);
739 mysql_free_result(result);
744 int cnid_mysql_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
746 LOG(log_error, logtype_cnid,
747 "cnid_mysql_find(\"%s\"): not supported with MySQL CNID backend", name);
751 cnid_t cnid_mysql_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
752 cnid_t did, const char *name, size_t len, cnid_t hint)
754 LOG(log_error, logtype_cnid,
755 "cnid_mysql_rebuild_add(\"%s\"): not supported with MySQL CNID backend", name);
759 int cnid_mysql_wipe(struct _cnid_db *cdb)
762 CNID_mysql_private *db;
763 MYSQL_RES *result = NULL;
765 if (!cdb || !(db = cdb->cnid_db_private)) {
766 LOG(log_error, logtype_cnid, "cnid_wipe: Parameter error");
767 errno = CNID_ERR_PARAM;
771 LOG(log_debug, logtype_cnid, "cnid_dbd_wipe");
773 EC_NEG1( cnid_mysql_execute(
776 "UPDATE volumes SET Depleted=0 WHERE VolUUID='%s';"
778 "ALTER TABLE %s AUTO_INCREMENT = 17;"
780 db->cnid_mysql_voluuid_str,
781 db->cnid_mysql_voluuid_str,
782 db->cnid_mysql_voluuid_str) );
785 result = mysql_store_result(db->cnid_mysql_con);
787 mysql_free_result(result);
788 } while (mysql_next_result(db->cnid_mysql_con) == 0);
794 static struct _cnid_db *cnid_mysql_new(struct vol *vol)
796 struct _cnid_db *cdb;
798 if ((cdb = (struct _cnid_db *)calloc(1, sizeof(struct _cnid_db))) == NULL)
801 cdb->cnid_db_vol = vol;
802 cdb->cnid_db_flags = CNID_FLAG_PERSISTENT | CNID_FLAG_LAZY_INIT;
803 cdb->cnid_add = cnid_mysql_add;
804 cdb->cnid_delete = cnid_mysql_delete;
805 cdb->cnid_get = cnid_mysql_get;
806 cdb->cnid_lookup = cnid_mysql_lookup;
807 cdb->cnid_find = cnid_mysql_find;
808 cdb->cnid_nextid = NULL;
809 cdb->cnid_resolve = cnid_mysql_resolve;
810 cdb->cnid_getstamp = cnid_mysql_getstamp;
811 cdb->cnid_update = cnid_mysql_update;
812 cdb->cnid_rebuild_add = cnid_mysql_rebuild_add;
813 cdb->cnid_close = cnid_mysql_close;
814 cdb->cnid_wipe = cnid_mysql_wipe;
818 static const char *printuuid(const unsigned char *uuid) {
819 static char uuidstring[64];
823 while ((c = *uuid)) {
825 sprintf(uuidstring + i, "%02X", c);
832 /* ---------------------- */
833 struct _cnid_db *cnid_mysql_open(struct cnid_open_args *args)
836 CNID_mysql_private *db = NULL;
837 struct _cnid_db *cdb = NULL;
838 MYSQL_RES *result = NULL;
840 struct vol *vol = args->cnid_args_vol;
842 EC_NULL( cdb = cnid_mysql_new(vol) );
843 EC_NULL( db = (CNID_mysql_private *)calloc(1, sizeof(CNID_mysql_private)) );
844 cdb->cnid_db_private = db;
846 db->cnid_mysql_voluuid_str = strdup(printuuid(vol->v_uuid));
848 /* Initialize and connect to MySQL server */
849 EC_NULL( db->cnid_mysql_con = mysql_init(NULL) );
850 my_bool my_recon = true;
851 EC_ZERO( mysql_options(db->cnid_mysql_con, MYSQL_OPT_RECONNECT, &my_recon) );
852 int my_timeout = 600;
853 EC_ZERO( mysql_options(db->cnid_mysql_con, MYSQL_OPT_CONNECT_TIMEOUT, &my_timeout) );
855 const AFPObj *obj = vol->v_obj;
857 EC_NULL( mysql_real_connect(db->cnid_mysql_con,
858 obj->options.cnid_mysql_host,
859 obj->options.cnid_mysql_user,
860 obj->options.cnid_mysql_pw,
861 obj->options.cnid_mysql_db,
862 0, NULL, CLIENT_MULTI_STATEMENTS));
864 /* Add volume to volume table */
865 if (cnid_mysql_execute(db->cnid_mysql_con,
866 "CREATE TABLE IF NOT EXISTS volumes"
867 "(VolUUID CHAR(32) PRIMARY KEY,VolPath TEXT(4096),Stamp BINARY(8),Depleted INT, INDEX(VolPath(64)))")) {
868 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
871 time_t now = time(NULL);
874 memcpy(stamp, &now, sizeof(time_t));
876 mysql_real_escape_string(db->cnid_mysql_con, blob, stamp, 8);
878 if (cnid_mysql_execute(db->cnid_mysql_con,
879 "INSERT INTO volumes (VolUUID,Volpath,Stamp,Depleted) "
880 "VALUES('%s','%s','%s',0)",
881 db->cnid_mysql_voluuid_str,
884 if (mysql_errno(db->cnid_mysql_con) != ER_DUP_ENTRY) {
885 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
891 * Check whether CNID set overflowed before.
892 * If that's the case, in cnid_mysql_add() we'll ignore the CNID "hint" taken from the
895 if (cnid_mysql_execute(db->cnid_mysql_con,
896 "SELECT Depleted FROM volumes WHERE VolUUID='%s'",
897 db->cnid_mysql_voluuid_str)) {
898 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
901 if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
902 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
906 if (mysql_num_rows(result)) {
907 row = mysql_fetch_row(result);
908 int depleted = atoi(row[0]);
910 db->cnid_mysql_flags |= CNID_MYSQL_FLAG_DEPLETED;
912 mysql_free_result(result);
915 /* Create volume table */
916 if (cnid_mysql_execute(db->cnid_mysql_con,
917 "CREATE TABLE IF NOT EXISTS %s"
918 "(Id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,"
919 "Name VARCHAR(255) NOT NULL,"
920 "Did INT UNSIGNED NOT NULL,"
921 "DevNo BIGINT UNSIGNED NOT NULL,"
922 "InodeNo BIGINT UNSIGNED NOT NULL,"
923 "UNIQUE DidName(Did, Name), UNIQUE DevIno(DevNo, InodeNo)) "
925 db->cnid_mysql_voluuid_str)) {
926 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
930 EC_ZERO( init_prepared_stmt(db) );
932 LOG(log_debug, logtype_cnid, "Finished initializing MySQL CNID module for volume '%s'",
937 mysql_free_result(result);
948 struct _cnid_module cnid_mysql_module = {