2 * Copyright (C) Ralph Boehme 2013
3 * All Rights Reserved. See COPYING.
8 #endif /* HAVE_CONFIG_H */
15 #include <sys/socket.h>
16 #include <sys/param.h>
18 #include <netinet/in.h>
20 #include <netinet/tcp.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
26 #include <arpa/inet.h>
29 #include <mysqld_error.h>
32 #include <atalk/logger.h>
33 #include <atalk/adouble.h>
34 #include <atalk/util.h>
35 #include <atalk/cnid_mysql_private.h>
36 #include <atalk/cnid_bdb_private.h>
37 #include <atalk/errchk.h>
38 #include <atalk/globals.h>
40 static MYSQL_BIND lookup_param[4], lookup_result[5];
41 static MYSQL_BIND add_param[4], put_param[5];
44 * Prepared statement parameters
46 static char stmt_param_name[MAXPATHLEN];
47 static unsigned long stmt_param_name_len;
48 static unsigned long long stmt_param_id;
49 static unsigned long long stmt_param_did;
50 static unsigned long long stmt_param_dev;
51 static unsigned long long stmt_param_ino;
54 * lookup result parameters
56 static unsigned long long lookup_result_id;
57 static unsigned long long lookup_result_did;
58 static char lookup_result_name[MAXPATHLEN];
59 static unsigned long lookup_result_name_len;
60 static unsigned long long lookup_result_dev;
61 static unsigned long long lookup_result_ino;
63 static int init_prepared_stmt_lookup(CNID_mysql_private *db)
68 lookup_param[0].buffer_type = MYSQL_TYPE_STRING;
69 lookup_param[0].buffer = &stmt_param_name;
70 lookup_param[0].buffer_length = sizeof(stmt_param_name);
71 lookup_param[0].length = &stmt_param_name_len;
73 lookup_param[1].buffer_type = MYSQL_TYPE_LONGLONG;
74 lookup_param[1].buffer = &stmt_param_did;
75 lookup_param[1].is_unsigned = true;
77 lookup_param[2].buffer_type = MYSQL_TYPE_LONGLONG;
78 lookup_param[2].buffer = &stmt_param_dev;
79 lookup_param[2].is_unsigned = true;
81 lookup_param[3].buffer_type = MYSQL_TYPE_LONGLONG;
82 lookup_param[3].buffer = &stmt_param_ino;
83 lookup_param[3].is_unsigned = true;
85 lookup_result[0].buffer_type = MYSQL_TYPE_LONGLONG;
86 lookup_result[0].buffer = &lookup_result_id;
87 lookup_result[0].is_unsigned = true;
89 lookup_result[1].buffer_type = MYSQL_TYPE_LONGLONG;
90 lookup_result[1].buffer = &lookup_result_did;
91 lookup_result[1].is_unsigned = true;
93 lookup_result[2].buffer_type = MYSQL_TYPE_STRING;
94 lookup_result[2].buffer = &lookup_result_name;
95 lookup_result[2].buffer_length = sizeof(lookup_result_name);
96 lookup_result[2].length = &lookup_result_name_len;
98 lookup_result[3].buffer_type = MYSQL_TYPE_LONGLONG;
99 lookup_result[3].buffer = &lookup_result_dev;
100 lookup_result[3].is_unsigned = true;
102 lookup_result[4].buffer_type = MYSQL_TYPE_LONGLONG;
103 lookup_result[4].buffer = &lookup_result_ino;
104 lookup_result[4].is_unsigned = true;
106 EC_NULL( db->cnid_lookup_stmt = mysql_stmt_init(db->cnid_mysql_con) );
107 EC_NEG1( asprintf(&sql,
108 "SELECT Id,Did,Name,DevNo,InodeNo FROM %s "
109 "WHERE (Name=? AND Did=?) OR (DevNo=? AND InodeNo=?)",
110 db->cnid_mysql_voluuid_str) );
111 EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_lookup_stmt, sql, strlen(sql)) );
112 EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_lookup_stmt, lookup_param) );
120 static int init_prepared_stmt_add(CNID_mysql_private *db)
125 EC_NULL( db->cnid_add_stmt = mysql_stmt_init(db->cnid_mysql_con) );
126 EC_NEG1( asprintf(&sql,
127 "INSERT INTO %s (Name,Did,DevNo,InodeNo) VALUES(?,?,?,?)",
128 db->cnid_mysql_voluuid_str) );
130 add_param[0].buffer_type = MYSQL_TYPE_STRING;
131 add_param[0].buffer = &stmt_param_name;
132 add_param[0].buffer_length = sizeof(stmt_param_name);
133 add_param[0].length = &stmt_param_name_len;
135 add_param[1].buffer_type = MYSQL_TYPE_LONGLONG;
136 add_param[1].buffer = &stmt_param_did;
137 add_param[1].is_unsigned = true;
139 add_param[2].buffer_type = MYSQL_TYPE_LONGLONG;
140 add_param[2].buffer = &stmt_param_dev;
141 add_param[2].is_unsigned = true;
143 add_param[3].buffer_type = MYSQL_TYPE_LONGLONG;
144 add_param[3].buffer = &stmt_param_ino;
145 add_param[3].is_unsigned = true;
147 EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_add_stmt, sql, strlen(sql)) );
148 EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_add_stmt, add_param) );
156 static int init_prepared_stmt_put(CNID_mysql_private *db)
161 EC_NULL( db->cnid_put_stmt = mysql_stmt_init(db->cnid_mysql_con) );
162 EC_NEG1( asprintf(&sql,
163 "INSERT INTO %s (Id,Name,Did,DevNo,InodeNo) VALUES(?,?,?,?,?)",
164 db->cnid_mysql_voluuid_str) );
166 put_param[0].buffer_type = MYSQL_TYPE_LONGLONG;
167 put_param[0].buffer = &stmt_param_id;
168 put_param[0].is_unsigned = true;
170 put_param[1].buffer_type = MYSQL_TYPE_STRING;
171 put_param[1].buffer = &stmt_param_name;
172 put_param[1].buffer_length = sizeof(stmt_param_name);
173 put_param[1].length = &stmt_param_name_len;
175 put_param[2].buffer_type = MYSQL_TYPE_LONGLONG;
176 put_param[2].buffer = &stmt_param_did;
177 put_param[2].is_unsigned = true;
179 put_param[3].buffer_type = MYSQL_TYPE_LONGLONG;
180 put_param[3].buffer = &stmt_param_dev;
181 put_param[3].is_unsigned = true;
183 put_param[4].buffer_type = MYSQL_TYPE_LONGLONG;
184 put_param[4].buffer = &stmt_param_ino;
185 put_param[4].is_unsigned = true;
187 EC_ZERO_LOG( mysql_stmt_prepare(db->cnid_put_stmt, sql, strlen(sql)) );
188 EC_ZERO_LOG( mysql_stmt_bind_param(db->cnid_put_stmt, put_param) );
196 static int init_prepared_stmt(CNID_mysql_private *db)
200 EC_ZERO( init_prepared_stmt_lookup(db) );
201 EC_ZERO( init_prepared_stmt_add(db) );
202 EC_ZERO( init_prepared_stmt_put(db) );
208 static void close_prepared_stmt(CNID_mysql_private *db)
210 mysql_stmt_close(db->cnid_lookup_stmt);
211 mysql_stmt_close(db->cnid_add_stmt);
212 mysql_stmt_close(db->cnid_put_stmt);
215 static int cnid_mysql_execute(MYSQL *con, char *fmt, ...)
222 if (vasprintf(&sql, fmt, ap) == -1)
226 LOG(log_maxdebug, logtype_cnid, "SQL: %s", sql);
228 rv = mysql_query(con, sql);
231 LOG(log_info, logtype_cnid, "MySQL query \"%s\", error: %s", sql, mysql_error(con));
238 int cnid_mysql_delete(struct _cnid_db *cdb, const cnid_t id)
241 CNID_mysql_private *db;
243 if (!cdb || !(db = cdb->_private) || !id) {
244 LOG(log_error, logtype_cnid, "cnid_mysql_delete: Parameter error");
245 errno = CNID_ERR_PARAM;
249 LOG(log_debug, logtype_cnid, "cnid_mysql_delete(%" PRIu32 "): BEGIN", ntohl(id));
251 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
252 "DELETE FROM %s WHERE Id=%" PRIu32,
253 db->cnid_mysql_voluuid_str,
256 LOG(log_debug, logtype_cnid, "cnid_mysql_delete(%" PRIu32 "): END", ntohl(id));
262 void cnid_mysql_close(struct _cnid_db *cdb)
264 CNID_mysql_private *db;
267 LOG(log_error, logtype_cnid, "cnid_close called with NULL argument !");
271 if ((db = cdb->_private) != NULL) {
272 LOG(log_debug, logtype_cnid, "closing database connection for volume '%s'", db->cnid_mysql_volname);
274 free(db->cnid_mysql_voluuid_str);
276 close_prepared_stmt(db);
278 if (db->cnid_mysql_con)
279 mysql_close(db->cnid_mysql_con);
289 int cnid_mysql_update(struct _cnid_db *cdb,
291 const struct stat *st,
297 CNID_mysql_private *db;
300 if (!cdb || !(db = cdb->_private) || !id || !st || !name) {
301 LOG(log_error, logtype_cnid, "cnid_update: Parameter error");
302 errno = CNID_ERR_PARAM;
306 if (len > MAXPATHLEN) {
307 LOG(log_error, logtype_cnid, "cnid_update: Path name is too long");
308 errno = CNID_ERR_PATH;
312 uint64_t dev = st->st_dev;
313 uint64_t ino = st->st_ino;
316 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
317 "DELETE FROM %s WHERE Id=%" PRIu32,
318 db->cnid_mysql_voluuid_str,
320 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
321 "DELETE FROM %s WHERE Did=%" PRIu32 " AND Name='%s'",
322 db->cnid_mysql_voluuid_str, ntohl(did), name) );
323 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
324 "DELETE FROM %s WHERE DevNo=%" PRIu64 " AND InodeNo=%" PRIu64,
325 db->cnid_mysql_voluuid_str, dev, ino) );
327 stmt_param_id = ntohl(id);
328 strncpy(stmt_param_name, name, sizeof(stmt_param_name));
329 stmt_param_name_len = len;
330 stmt_param_did = ntohl(did);
331 stmt_param_dev = dev;
332 stmt_param_ino = ino;
334 if (mysql_stmt_execute(db->cnid_put_stmt)) {
335 switch (mysql_stmt_errno(db->cnid_put_stmt)) {
339 * between deletion and insert another process may have inserted
344 close_prepared_stmt(db);
345 EC_ZERO( init_prepared_stmt(db) );
351 update_id = mysql_stmt_insert_id(db->cnid_put_stmt);
352 } while (update_id != ntohl(id));
358 cnid_t cnid_mysql_lookup(struct _cnid_db *cdb,
359 const struct stat *st,
365 CNID_mysql_private *db;
366 cnid_t id = CNID_INVALID;
367 bool have_result = false;
369 if (!cdb || !(db = cdb->_private) || !st || !name) {
370 LOG(log_error, logtype_cnid, "cnid_mysql_lookup: Parameter error");
371 errno = CNID_ERR_PARAM;
375 if (len > MAXPATHLEN) {
376 LOG(log_error, logtype_cnid, "cnid_mysql_lookup: Path name is too long");
377 errno = CNID_ERR_PATH;
381 uint64_t dev = st->st_dev;
382 uint64_t ino = st->st_ino;
383 cnid_t hint = db->cnid_mysql_hint;
385 LOG(log_maxdebug, logtype_cnid, "cnid_mysql_lookup(did: %" PRIu32 ", name: \"%s\", hint: %" PRIu32 "): START",
386 ntohl(did), name, ntohl(hint));
388 strncpy(stmt_param_name, name, sizeof(stmt_param_name));
389 stmt_param_name_len = len;
390 stmt_param_did = ntohl(did);
391 stmt_param_dev = dev;
392 stmt_param_ino = ino;
395 if (mysql_stmt_execute(db->cnid_lookup_stmt)) {
396 switch (mysql_stmt_errno(db->cnid_lookup_stmt)) {
398 close_prepared_stmt(db);
399 EC_ZERO( init_prepared_stmt(db) );
405 EC_ZERO_LOG( mysql_stmt_store_result(db->cnid_lookup_stmt) );
407 EC_ZERO_LOG( mysql_stmt_bind_result(db->cnid_lookup_stmt, lookup_result) );
409 uint64_t retdev, retino;
410 cnid_t retid, retdid;
413 switch (mysql_stmt_num_rows(db->cnid_lookup_stmt)) {
417 LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: name: '%s', did: %u is not in the CNID database",
419 errno = CNID_DBD_RES_NOTFOUND;
423 /* either both OR clauses matched the same id or only one matched, handled below */
424 EC_ZERO( mysql_stmt_fetch(db->cnid_lookup_stmt) );
428 /* a mismatch, delete both and return not found */
429 while (mysql_stmt_fetch(db->cnid_lookup_stmt) == 0) {
430 if (cnid_mysql_delete(cdb, htonl((cnid_t)lookup_result_id))) {
431 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
436 errno = CNID_DBD_RES_NOTFOUND;
444 retid = htonl(lookup_result_id);
445 retdid = htonl(lookup_result_did);
446 retname = lookup_result_name;
447 retdev = lookup_result_dev;
448 retino = lookup_result_ino;
450 if (retdid != did || STRCMP(retname, !=, name)) {
451 LOG(log_debug, logtype_cnid,
452 "cnid_mysql_lookup(CNID hint: %" PRIu32 ", DID: %" PRIu32 ", name: \"%s\"): server side mv oder reused inode",
453 ntohl(hint), ntohl(did), name);
455 if (cnid_mysql_delete(cdb, retid) != 0) {
456 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
460 errno = CNID_DBD_RES_NOTFOUND;
463 LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: server side mv, got hint, updating");
464 if (cnid_mysql_update(cdb, retid, st, did, name, len) != 0) {
465 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
470 } else if (retdev != dev || retino != ino) {
471 LOG(log_debug, logtype_cnid, "cnid_mysql_lookup(DID:%u, name: \"%s\"): changed dev/ino",
473 if (cnid_mysql_delete(cdb, retid) != 0) {
474 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
478 errno = CNID_DBD_RES_NOTFOUND;
481 /* everythings good */
486 LOG(log_debug, logtype_cnid, "cnid_mysql_lookup: id: %" PRIu32, ntohl(id));
488 mysql_stmt_free_result(db->cnid_lookup_stmt);
494 cnid_t cnid_mysql_add(struct _cnid_db *cdb,
495 const struct stat *st,
502 CNID_mysql_private *db;
503 cnid_t id = CNID_INVALID;
504 MYSQL_RES *result = NULL;
508 if (!cdb || !(db = cdb->_private) || !st || !name) {
509 LOG(log_error, logtype_cnid, "cnid_mysql_add: Parameter error");
510 errno = CNID_ERR_PARAM;
514 if (len > MAXPATHLEN) {
515 LOG(log_error, logtype_cnid, "cnid_mysql_add: Path name is too long");
516 errno = CNID_ERR_PATH;
520 uint64_t dev = st->st_dev;
521 uint64_t ino = st->st_ino;
522 db->cnid_mysql_hint = hint;
524 LOG(log_maxdebug, logtype_cnid, "cnid_mysql_add(did: %" PRIu32 ", name: \"%s\", hint: %" PRIu32 "): START",
525 ntohl(did), name, ntohl(hint));
528 if ((id = cnid_mysql_lookup(cdb, st, did, name, len)) == CNID_INVALID) {
529 if (errno == CNID_ERR_DB)
532 * If the CNID set overflowed before (CNID_MYSQL_FLAG_DEPLETED)
533 * ignore the CNID "hint" taken from the AppleDouble file
535 if (!db->cnid_mysql_hint || (db->cnid_mysql_flags & CNID_MYSQL_FLAG_DEPLETED)) {
536 stmt = db->cnid_add_stmt;
538 stmt = db->cnid_put_stmt;
539 stmt_param_id = ntohl(db->cnid_mysql_hint);
541 strncpy(stmt_param_name, name, sizeof(stmt_param_name));
542 stmt_param_name_len = len;
543 stmt_param_did = ntohl(did);
544 stmt_param_dev = dev;
545 stmt_param_ino = ino;
547 if (mysql_stmt_execute(stmt)) {
548 switch (mysql_stmt_errno(stmt)) {
552 close_prepared_stmt(db);
553 EC_ZERO( init_prepared_stmt(db) );
560 * between lookup and insert another process may have inserted
563 if (db->cnid_mysql_hint)
564 db->cnid_mysql_hint = CNID_INVALID;
568 lastid = mysql_stmt_insert_id(stmt);
570 if (lastid > 0xffffffff) {
571 /* CNID set ist depleted, restart from scratch */
572 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
574 "UPDATE volumes SET Depleted=1 WHERE VolUUID='%s';"
576 "ALTER TABLE %s AUTO_INCREMENT = 17;"
578 db->cnid_mysql_voluuid_str,
579 db->cnid_mysql_voluuid_str,
580 db->cnid_mysql_voluuid_str) );
581 db->cnid_mysql_flags |= CNID_MYSQL_FLAG_DEPLETED;
584 result = mysql_store_result(db->cnid_mysql_con);
586 mysql_free_result(result);
587 } while (mysql_next_result(db->cnid_mysql_con) == 0);
591 /* Finally assign our result */
592 id = htonl((uint32_t)lastid);
594 } while (id == CNID_INVALID);
597 LOG(log_debug, logtype_cnid, "cnid_mysql_add: id: %" PRIu32, ntohl(id));
600 mysql_free_result(result);
604 cnid_t cnid_mysql_get(struct _cnid_db *cdb, cnid_t did, const char *name, size_t len)
607 CNID_mysql_private *db;
608 cnid_t id = CNID_INVALID;
609 MYSQL_RES *result = NULL;
612 if (!cdb || !(db = cdb->_private) || !name) {
613 LOG(log_error, logtype_cnid, "cnid_mysql_get: Parameter error");
614 errno = CNID_ERR_PARAM;
618 if (len > MAXPATHLEN) {
619 LOG(log_error, logtype_cnid, "cnid_mysql_get: name is too long");
620 errno = CNID_ERR_PATH;
624 LOG(log_debug, logtype_cnid, "cnid_mysql_get(did: %" PRIu32 ", name: \"%s\"): START",
627 EC_NEG1( cnid_mysql_execute(db->cnid_mysql_con,
629 "WHERE Name='%s' AND Did=%" PRIu32,
630 db->cnid_mysql_voluuid_str,
634 if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
635 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
640 if (mysql_num_rows(result)) {
641 row = mysql_fetch_row(result);
642 id = htonl(atoi(row[0]));
646 LOG(log_debug, logtype_cnid, "cnid_mysql_get: id: %" PRIu32, ntohl(id));
649 mysql_free_result(result);
654 char *cnid_mysql_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
657 CNID_mysql_private *db;
658 MYSQL_RES *result = NULL;
661 if (!cdb || !(db = cdb->_private)) {
662 LOG(log_error, logtype_cnid, "cnid_mysql_get: Parameter error");
663 errno = CNID_ERR_PARAM;
667 EC_NEG1( cnid_mysql_execute(
669 "SELECT Did,Name FROM %s WHERE Id=%" PRIu32,
670 db->cnid_mysql_voluuid_str, ntohl(*id)) );
672 EC_NULL( result = mysql_store_result(db->cnid_mysql_con) );
674 if (mysql_num_rows(result) != 1)
677 row = mysql_fetch_row(result);
679 *id = htonl(atoi(row[0]));
680 strncpy(buffer, row[1], len);
684 mysql_free_result(result);
694 * Caller passes buffer where we will store the db stamp
696 int cnid_mysql_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
699 CNID_mysql_private *db;
700 MYSQL_RES *result = NULL;
703 if (!cdb || !(db = cdb->_private)) {
704 LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
705 errno = CNID_ERR_PARAM;
712 if (cnid_mysql_execute(db->cnid_mysql_con,
713 "SELECT Stamp FROM volumes WHERE VolPath='%s'",
714 db->cnid_mysql_volname)) {
715 if (mysql_errno(db->cnid_mysql_con) != ER_DUP_ENTRY) {
716 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
721 if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
722 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
726 if (!mysql_num_rows(result)) {
727 LOG(log_error, logtype_cnid, "Can't get DB stamp for volumes \"%s\"", db->cnid_mysql_volname);
730 row = mysql_fetch_row(result);
731 memcpy(buffer, row[0], len);
735 mysql_free_result(result);
740 int cnid_mysql_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
742 LOG(log_error, logtype_cnid,
743 "cnid_mysql_find(\"%s\"): not supported with MySQL CNID backend", name);
747 cnid_t cnid_mysql_rebuild_add(struct _cnid_db *cdb, const struct stat *st,
748 cnid_t did, const char *name, size_t len, cnid_t hint)
750 LOG(log_error, logtype_cnid,
751 "cnid_mysql_rebuild_add(\"%s\"): not supported with MySQL CNID backend", name);
755 int cnid_mysql_wipe(struct _cnid_db *cdb)
758 CNID_mysql_private *db;
759 MYSQL_RES *result = NULL;
761 if (!cdb || !(db = cdb->_private)) {
762 LOG(log_error, logtype_cnid, "cnid_wipe: Parameter error");
763 errno = CNID_ERR_PARAM;
767 LOG(log_debug, logtype_cnid, "cnid_dbd_wipe");
769 EC_NEG1( cnid_mysql_execute(
772 "UPDATE volumes SET Depleted=0 WHERE VolUUID='%s';"
774 "ALTER TABLE %s AUTO_INCREMENT = 17;"
776 db->cnid_mysql_voluuid_str,
777 db->cnid_mysql_voluuid_str,
778 db->cnid_mysql_voluuid_str) );
781 result = mysql_store_result(db->cnid_mysql_con);
783 mysql_free_result(result);
784 } while (mysql_next_result(db->cnid_mysql_con) == 0);
790 static struct _cnid_db *cnid_mysql_new(const char *volpath)
792 struct _cnid_db *cdb;
794 if ((cdb = (struct _cnid_db *)calloc(1, sizeof(struct _cnid_db))) == NULL)
797 if ((cdb->volpath = strdup(volpath)) == NULL) {
802 cdb->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];
820 const char *uuidmask;
824 while ((c = *uuid)) {
826 sprintf(uuidstring + i, "%02X", c);
833 /* ---------------------- */
834 struct _cnid_db *cnid_mysql_open(struct cnid_open_args *args)
837 CNID_mysql_private *db = NULL;
838 struct _cnid_db *cdb = NULL;
839 MYSQL_RES *result = NULL;
842 EC_NULL( cdb = cnid_mysql_new(args->dir) );
843 EC_NULL( db = (CNID_mysql_private *)calloc(1, sizeof(CNID_mysql_private)) );
846 db->cnid_mysql_volname = strdup(args->dir); /* db_dir contains the volume name */
847 db->cnid_mysql_magic = CNID_DB_MAGIC;
848 db->cnid_mysql_obj = args->obj;
849 memcpy(db->cnid_mysql_voluuid, args->voluuid, sizeof(atalk_uuid_t));
850 db->cnid_mysql_voluuid_str = strdup(printuuid(db->cnid_mysql_voluuid));
852 /* Initialize and connect to MySQL server */
853 EC_NULL( db->cnid_mysql_con = mysql_init(NULL) );
854 my_bool my_recon = true;
855 EC_ZERO( mysql_options(db->cnid_mysql_con, MYSQL_OPT_RECONNECT, &my_recon) );
856 int my_timeout = 600;
857 EC_ZERO( mysql_options(db->cnid_mysql_con, MYSQL_OPT_CONNECT_TIMEOUT, &my_timeout) );
859 const AFPObj *obj = db->cnid_mysql_obj;
861 EC_NULL( mysql_real_connect(db->cnid_mysql_con,
862 obj->options.cnid_mysql_host,
863 obj->options.cnid_mysql_user,
864 obj->options.cnid_mysql_pw,
865 obj->options.cnid_mysql_db,
866 0, NULL, CLIENT_MULTI_STATEMENTS));
868 /* Add volume to volume table */
869 if (cnid_mysql_execute(db->cnid_mysql_con,
870 "CREATE TABLE IF NOT EXISTS volumes"
871 "(VolUUID CHAR(32) PRIMARY KEY,VolPath TEXT(4096),Stamp BINARY(8),Depleted INT, INDEX(VolPath(64)))")) {
872 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
875 time_t now = time(NULL);
878 memcpy(stamp, &now, sizeof(time_t));
880 mysql_real_escape_string(db->cnid_mysql_con, blob, stamp, 8);
882 if (cnid_mysql_execute(db->cnid_mysql_con,
883 "INSERT INTO volumes (VolUUID,Volpath,Stamp,Depleted) "
884 "VALUES('%s','%s','%s',0)",
885 db->cnid_mysql_voluuid_str,
886 db->cnid_mysql_volname,
888 if (mysql_errno(db->cnid_mysql_con) != ER_DUP_ENTRY) {
889 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
895 * Check whether CNID set overflowed before.
896 * If that's the case, in cnid_mysql_add() we'll ignore the CNID "hint" taken from the
899 if (cnid_mysql_execute(db->cnid_mysql_con,
900 "SELECT Depleted FROM volumes WHERE VolUUID='%s'",
901 db->cnid_mysql_voluuid_str)) {
902 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
905 if ((result = mysql_store_result(db->cnid_mysql_con)) == NULL) {
906 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
910 if (mysql_num_rows(result)) {
911 row = mysql_fetch_row(result);
912 int depleted = atoi(row[0]);
914 db->cnid_mysql_flags |= CNID_MYSQL_FLAG_DEPLETED;
916 mysql_free_result(result);
919 /* Create volume table */
920 if (cnid_mysql_execute(db->cnid_mysql_con,
921 "CREATE TABLE IF NOT EXISTS %s"
922 "(Id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,"
923 "Name VARCHAR(255) NOT NULL,"
924 "Did INT UNSIGNED NOT NULL,"
925 "DevNo BIGINT UNSIGNED NOT NULL,"
926 "InodeNo BIGINT UNSIGNED NOT NULL,"
927 "UNIQUE DidName(Did, Name), UNIQUE DevIno(DevNo, InodeNo)) "
929 db->cnid_mysql_voluuid_str)) {
930 LOG(log_error, logtype_cnid, "MySQL query error: %s", mysql_error(db->cnid_mysql_con));
934 EC_ZERO( init_prepared_stmt(db) );
936 LOG(log_debug, logtype_cnid, "Finished initializing MySQL CNID module for volume '%s'",
937 db->cnid_mysql_volname);
941 mysql_free_result(result);
944 if (cdb->volpath != NULL) {
956 struct _cnid_module cnid_mysql_module = {