예제 #1
0
파일: sql.c 프로젝트: rowhit/Tagsistant
/**
 * return(last insert row inode)
 */
tagsistant_inode tagsistant_last_insert_id(dbi_conn conn)
{
	return(dbi_conn_sequence_last(conn, NULL));

#if 0
	// -------- alternative version -----------------------------------------------

	tagsistant_inode inode = 0;

	switch (tagsistant.sql_database_driver) {
		case TAGSISTANT_DBI_SQLITE_BACKEND:
			tagsistant_query("SELECT last_insert_rowid() ", conn, tagsistant_return_integer, &inode);
			break;

		case TAGSISTANT_DBI_MYSQL_BACKEND:
			tagsistant_query("SELECT last_insert_id() ", conn, tagsistant_return_integer, &inode);
			break;
	}

	return (inode);
#endif
}
예제 #2
0
파일: flush.c 프로젝트: Copperis/Tagsistant
/**
 * close() equivalent [first part, second is tagsistant_release()]
 *
 * @param path the path to be open()ed
 * @param fi struct fuse_file_info holding open() flags
 * @return(0 on success, -errno otherwise)
 */
int tagsistant_flush(const char *path, struct fuse_file_info *fi)
{
	(void) fi;

	int res = 0, tagsistant_errno = 0, do_deduplicate = 0;
    gchar *deduplicate = NULL;

	TAGSISTANT_START("FLUSH on %s", path);

	// build querytree
	tagsistant_querytree *qtree = tagsistant_querytree_new(path, 0, 0, 1, 1);

	// -- malformed --
	if (QTREE_IS_MALFORMED(qtree))
		TAGSISTANT_ABORT_OPERATION(ENOENT);

	if (qtree->full_archive_path) {
		tagsistant_query(
			"select 1 from objects where objectname = '%s' and checksum = ''",
			qtree->dbi,
			tagsistant_return_integer,
			&do_deduplicate,
			qtree->object_path);

		if (do_deduplicate) {
			dbg('2', LOG_INFO, "Deduplicating %s", path);
			deduplicate = g_strdup(path);
		} else {
			dbg('2', LOG_INFO, "Skipping deduplication for %s", path);
		}
	}

	if (fi->fh) {
		dbg('F', LOG_INFO, "Uncaching %" PRIu64 " = open(%s)", fi->fh, path);
		close(fi->fh);
		fi->fh = 0;
	}

TAGSISTANT_EXIT_OPERATION:
	if ( res == -1 ) {
		TAGSISTANT_STOP_ERROR("FLUSH on %s (%s) (%s): %d %d: %s", path, qtree->full_archive_path, tagsistant_querytree_type(qtree), res, tagsistant_errno, strerror(tagsistant_errno));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_ROLLBACK_TRANSACTION);
		if (do_deduplicate) tagsistant_deduplicate(deduplicate);
		return (-tagsistant_errno);
	} else {
		TAGSISTANT_STOP_OK("FLUSH on %s (%s): OK", path, tagsistant_querytree_type(qtree));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_COMMIT_TRANSACTION);
		if (do_deduplicate) tagsistant_deduplicate(deduplicate);
		return (0);
	}
}
예제 #3
0
/**
 * unlink equivalent
 *
 * @param path the path to be unlinked (deleted)
 * @return(0 on success, -errno otherwise)
 */
int tagsistant_unlink(const char *path)
{
    int res = 0, tagsistant_errno = 0;
    gboolean dispose = TRUE;
	gchar *unlink_path = NULL;

	TAGSISTANT_START(OPS_IN "UNLINK on %s", path);

	// build querytree
	tagsistant_querytree *qtree = tagsistant_querytree_new(path, 0, 1, 1, 0);

	// -- malformed --
	if (QTREE_IS_MALFORMED(qtree)) TAGSISTANT_ABORT_OPERATION(ENOENT);

	// -- objects on disk --
	if (QTREE_IS_STORE(qtree)) {
		tagsistant_querytree_check_tagging_consistency(qtree);

		if (QTREE_IS_TAGGABLE(qtree)) {
			if (is_all_path(qtree->full_path)) {

				tagsistant_query(
					"delete from objects where inode = %d",
					qtree->dbi, NULL, NULL, qtree->inode);

				tagsistant_query(
					"delete from tagging where inode = %d",
					qtree->dbi, NULL, NULL, qtree->inode);

			} else {

				/*
				 * if object is pointed by a tags/ query, then untag it
				 * from the tags included in the query path...
				 */
				tagsistant_querytree_traverse(qtree, tagsistant_sql_untag_object, qtree->inode);

				/*
				 * ...then check if it's tagged elsewhere...
				 * ...if still tagged, then avoid real unlink(): the object must survive!
				 * ...otherwise we can delete it from the objects table
				 */
				dispose = tagsistant_dispose_object_if_untagged(qtree);
			}

#if TAGSISTANT_ENABLE_AND_SET_CACHE
			/*
			 * invalidate the and_set cache
			 */
			tagsistant_invalidate_and_set_cache_entries(qtree);
#endif


			/*
			 * clean the RDS library
			 */
			tagsistant_delete_rds_involved(qtree);
		}

		// unlink the object on disk
		if (dispose) {
			unlink_path = qtree->full_archive_path;
			res = unlink(unlink_path);
			tagsistant_errno = errno;
		}
	} else

	// -- alias --
	if (QTREE_IS_ALIAS(qtree)) {
		tagsistant_sql_alias_delete(qtree->dbi, qtree->alias);
	}

	// -- tags --
	// -- stats --
	// -- relations --
	// -- archive --
	else TAGSISTANT_ABORT_OPERATION(EROFS);

TAGSISTANT_EXIT_OPERATION:
	if ( res is -1 ) {
		TAGSISTANT_STOP_ERROR(OPS_OUT "UNLINK on %s (%s) (%s): %d %d: %s", path, unlink_path, tagsistant_querytree_type(qtree), res, tagsistant_errno, strerror(tagsistant_errno));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_ROLLBACK_TRANSACTION);
		return (-tagsistant_errno);
	} else {
		TAGSISTANT_STOP_OK(OPS_OUT "UNLINK on %s (%s): OK", path, tagsistant_querytree_type(qtree));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_COMMIT_TRANSACTION);
		return (0);
	}
}
예제 #4
0
파일: sql.c 프로젝트: rowhit/Tagsistant
void tagsistant_wal_sync()
{
	/*
	 * compile the WAL pattern regex
	 */
	if (!wal_pattern) wal_pattern = g_regex_new("([^:]+): (.*)", G_REGEX_EXTENDED|G_REGEX_CASELESS, 0, NULL);

	/*
	 * compute the wal directory path
	 */
	gchar *wal_dir = g_strdup_printf("%s/wal", tagsistant.repository);
	if (!wal_dir) {
		dbg('s', LOG_ERR, "WAL: error restoring the WAL");
		exit (1);
	}

	/*
	 * open a DB connection
	 */
	dbi_conn dbi = tagsistant_db_connection(TAGSISTANT_START_TRANSACTION);
	gboolean commit = FALSE;

	/*
	 * load the last timestamp inserted
	 */
	gchar *last_tstamp = NULL;
	tagsistant_query(
		"select value from status where state = 'wal_timestamp'",
		dbi, tagsistant_return_string, &last_tstamp);

	if (!last_tstamp) {
		/*
		 * check if this is an empty filesystem
		 */
		int entries = 0;
		tagsistant_query(
			"select sum(entries) as entries from ("
				"select count(*) as entries from objects "
				"union all "
				"select count(*) as entries from tags "
			")",
			dbi, tagsistant_return_integer, &entries);

		if (entries) {
			dbg('s', LOG_ERR, "WAL: error loading last timestamp, can't proceed");
			exit (1);
		} else {
			dbg('s', LOG_INFO, "WAL: skipping sync on empty repository");
			tagsistant_rollback_transaction(dbi);
			tagsistant_db_connection_release(dbi, 1);
			g_free(wal_dir);
			return;
		}
	}

	/*
	 * open the WAL directory and scan its files
	 */
	GError *error = NULL;
	GDir *dir = g_dir_open(wal_dir, 0, &error);
	if (dir) {
		const gchar *entry;
		while (1) {
			entry = g_dir_read_name(dir);
			if (!entry) {
				commit = TRUE;
				break;
			}

			if (!tagsistant_wal_apply_log(dbi, entry, last_tstamp)) break;
		}
		g_dir_close(dir);
	} else {
		dbg('s', LOG_ERR, "WAL: error opening directory: %s", error->message);
		g_error_free(error);
	}

	/*
	 * on positive sync, commit the transaction, otherwise roll back
	 */
	if (commit)
		tagsistant_commit_transaction(dbi);
	else
		tagsistant_rollback_transaction(dbi);

	tagsistant_db_connection_release(dbi, 1);
	g_free(wal_dir);

	/*
	 * if unable to sync the WAL, exit to avoid mounting a compromised database
	 */
	if (!commit) {
		dbg('s', LOG_ERR, "WAL: error merging write-ahead logs into DB, can't mount a compromised repository");
		exit (1);
	}
}
예제 #5
0
파일: sql.c 프로젝트: rowhit/Tagsistant
/**
 * Create DB schema
 */
void tagsistant_create_schema()
{
	dbi_conn dbi = tagsistant_db_connection(TAGSISTANT_START_TRANSACTION);
	gchar *current_schema_version = NULL;

	// create database schema
	switch (tagsistant.sql_database_driver) {
		case TAGSISTANT_DBI_SQLITE_BACKEND:
			/*
			 * Create schema table and check current schema compatiblity
			 */
			tagsistant_query(
				"create table if not exists schema_version (version varchar(32))",
				dbi, NULL, NULL);

			tagsistant_query(
				"select version from schema_version",
				dbi, tagsistant_return_string, &current_schema_version);

			if (current_schema_version && g_strcmp0(TAGSISTANT_SCHEMA_VERSION, current_schema_version) != 0) {
				dbg('s', LOG_ERR,
					"Required schema version %s differs from current schema version %s",
					TAGSISTANT_SCHEMA_VERSION, current_schema_version);
				exit(1);
			}

			/*
			 * Tags table
			 */
			tagsistant_query(
				"create table if not exists tags ("
					"tag_id integer primary key autoincrement not null, "
					"tagname varchar(65) not null, "
					"key varchar(65) not null default '', "
					"value varchar(65) not null default '', "
					"constraint Tag_key unique (tagname, key, value))",
				dbi, NULL, NULL);

			/*
			 * Objects table
			 */
			tagsistant_query(
				"create table if not exists objects ("
					"inode integer not null primary key autoincrement, "
					"objectname text(255) not null, "
					"last_autotag timestamp not null default 0, "
					"checksum text(40) not null default '', "
					"symlink text(1024) not null default '')",
				dbi, NULL, NULL);

			/*
			 * Tagging table
			 */
			tagsistant_query(
				"create table if not exists tagging ("
					"inode integer not null, "
					"tag_id integer not null, "
					"constraint Tagging_key unique (inode, tag_id))",
				dbi, NULL, NULL);

			/*
			 * Relations table
			 */
			tagsistant_query(
				"create table if not exists relations ("
					"relation_id integer primary key autoincrement not null, "
					"tag1_id integer not null, "
					"relation varchar not null, "
					"tag2_id integer not null)",
				dbi, NULL, NULL);

			/*
			 * Aliases table
			 */
			tagsistant_query(
				"create table if not exists aliases ("
					"alias varchar(65) primary key not null, "
					"query varchar(%d) not null)",
				dbi, NULL, NULL, TAGSISTANT_ALIAS_MAX_LENGTH);

			/*
			 * RDS table
			 */
			tagsistant_query(
				"create temporary table if not exists rds ("
					"id varchar(32) not null, "
					"reasoned integer not null, "
					"inode integer not null, "
					"objectname text(255) not null, "
					"tagset text not null, "
					"creation datetime not null default CURRENT_DATE)",
				dbi, NULL, NULL);

			tagsistant_query(
				"create table if not exists status ("
					"state varchar(16) primary key not null, "
					"value varchar(256) not null)",
				dbi, NULL, NULL);

			/*
			 * Index declarations
			 */
			tagsistant_query("create index if not exists relations_index on relations (tag1_id, tag2_id)", dbi, NULL, NULL);
			tagsistant_query("create index if not exists objectname_index on objects (objectname)", dbi, NULL, NULL);
			tagsistant_query("create index if not exists symlink_index on objects (symlink, inode)", dbi, NULL, NULL);
			tagsistant_query("create index if not exists checksum_index on objects (checksum, inode)", dbi, NULL, NULL);
			tagsistant_query("create index if not exists relations_type_index on relations (relation)", dbi, NULL, NULL);
			tagsistant_query("create index if not exists aliases_index on aliases (alias)", dbi, NULL, NULL);
			tagsistant_query("create index if not exists rds_index1 on rds (id, reasoned, objectname, inode)", dbi, NULL, NULL);
			tagsistant_query("create index if not exists rds_index2 on rds (id, reasoned, inode, objectname)", dbi, NULL, NULL);

			tagsistant_query("delete from schema_version", dbi, NULL, NULL);
			tagsistant_query("insert into schema_version (version) values (\"%s\")",
				dbi, NULL, NULL, TAGSISTANT_SCHEMA_VERSION);

			break;

		case TAGSISTANT_DBI_MYSQL_BACKEND:
			/*
			 * Create schema table and check current schema compatiblity
			 */
			tagsistant_query(
				"create table if not exists schema_version (version varchar(32))",
				dbi, NULL, NULL);

			tagsistant_query(
				"select version from schema_version",
				dbi, tagsistant_return_string, &current_schema_version);

			if (current_schema_version && g_strcmp0(TAGSISTANT_SCHEMA_VERSION, current_schema_version) != 0) {
				dbg('s', LOG_ERR,
					"Required schema version %s differs from current schema version %s",
					TAGSISTANT_SCHEMA_VERSION, current_schema_version);
				exit(1);
			}

			/*
			 * Tags table
			 */
			tagsistant_query(
				"create table if not exists tags ("
					"tag_id integer primary key auto_increment not null, "
					"tagname varchar(65) not null, "
					"`key` varchar(65) not null, "
					"value varchar(65) not null, "
					"constraint Tag_key unique `key` (tagname, `key`, value))",
				dbi, NULL, NULL);

			/*
			 * Objects table
			 */
			tagsistant_query(
				"create table if not exists objects ("
					"inode integer not null primary key auto_increment, "
					"objectname varchar(255) not null, "
					"last_autotag timestamp not null default 0, "
					"checksum varchar(40) not null default '', "
					"symlink varchar(1024) not null default '')",
				dbi, NULL, NULL);

			/*
			 * Tagging table
			 */
			tagsistant_query(
				"create table if not exists tagging ("
					"inode integer not null, "
					"tag_id integer not null, "
					"constraint Tagging_key unique key (inode, tag_id))",
				dbi, NULL, NULL);

			/*
			 * Relations table
			 */
			tagsistant_query(
				"create table if not exists relations ("
					"relation_id integer primary key auto_increment not null, "
					"tag1_id integer not null, "
					"relation varchar(32) not null, "
					"tag2_id integer not null)",
				dbi, NULL, NULL);

			/*
			 * Aliases table
			 */
			tagsistant_query(
				"create table if not exists aliases ("
					"alias varchar(65) primary key not null, "
					"query varchar(%d) not null)",
				dbi, NULL, NULL, TAGSISTANT_ALIAS_MAX_LENGTH);

			/*
			 * RDS table
			 */
			tagsistant_query(
				"create temporary table if not exists rds ("
					"id varchar(32) not null, "
					"reasoned integer not null, "
					"inode integer not null, "
					"objectname text(255) not null, "
					"tagset text not null, "
					"creation datetime not null) ENGINE = MEMORY",
				dbi, NULL, NULL);

			tagsistant_query(
				"create table if not exists status ("
					"state varchar(16) primary key not null, "
					"value varchar(256) not null)",
				dbi, NULL, NULL);

			/*
			 * Index declarations
			 */
			tagsistant_query("create index relations_index on relations (tag1_id, tag2_id)", dbi, NULL, NULL);
			tagsistant_query("create index objectname_index on objects (objectname)", dbi, NULL, NULL);
			tagsistant_query("create index symlink_index on objects (symlink, inode)", dbi, NULL, NULL);
			tagsistant_query("create index checksum_index on objects (checksum, inode)", dbi, NULL, NULL);
			tagsistant_query("create index relations_type_index on relations (relation)", dbi, NULL, NULL);
			tagsistant_query("create index aliases_index on aliases (alias)", dbi, NULL, NULL);
			tagsistant_query("create index rds_index1 on rds (id, reasoned, objectname, inode)", dbi, NULL, NULL);
			tagsistant_query("create index rds_index2 on rds (id, reasoned, inode, objectname)", dbi, NULL, NULL);

			/*
			 * Schema version update
			 */
			tagsistant_query("delete from schema_version", dbi, NULL, NULL);
			tagsistant_query("insert into schema_version (version) values (\"%s\")",
				dbi, NULL, NULL, TAGSISTANT_SCHEMA_VERSION);

			break;

		default:
			break;
	}

	tagsistant_commit_transaction(dbi);
	tagsistant_db_connection_release(dbi, 1);
}
예제 #6
0
파일: sql.c 프로젝트: rowhit/Tagsistant
/**
 * Parse command line options, create connection object,
 * start the connection and finally create database schema
 *
 * @return DBI connection handle
 */
dbi_conn *tagsistant_db_connection(int start_transaction)
{
	/* DBI connection handler used by subsequent calls to dbi_* functions */
	dbi_conn dbi = NULL;

	if (start_transaction) {
		g_rw_lock_writer_lock(&(tagsistant_query_rwlock));
	} else {
		g_rw_lock_reader_lock(&(tagsistant_query_rwlock));
	}

	/* lock the pool */
	g_mutex_lock(&tagsistant_connection_pool_lock);

	GList *pool = tagsistant_connection_pool;
	while (pool) {
		dbi = (dbi_conn) pool->data;

		/* check if the connection is still alive */
		if (!dbi_conn_ping(dbi) && dbi_conn_connect(dbi) < 0) {
			dbi_conn_close(dbi);
			tagsistant_connection_pool = g_list_delete_link(tagsistant_connection_pool, pool);
			connections--;
		} else {
			tagsistant_connection_pool = g_list_remove_link(tagsistant_connection_pool, pool);
			g_list_free_1(pool);
			break;
		}

		pool = pool->next;
	}

	/*
	 * unlock the pool mutex only if the backend is not SQLite
	 */
	g_mutex_unlock(&tagsistant_connection_pool_lock);

	if (!dbi) {
		// initialize DBI drivers
		if (TAGSISTANT_DBI_MYSQL_BACKEND == dboptions.backend) {
			if (!tagsistant_driver_is_available("mysql")) {
				fprintf(stderr, "MySQL driver not installed\n");
				dbg('s', LOG_ERR, "MySQL driver not installed");
				exit (1);
			}

			// unlucky, MySQL does not provide INTERSECT operator
			tagsistant.sql_backend_have_intersect = 0;

			// create connection
#if TAGSISTANT_REENTRANT_DBI
			dbi = dbi_conn_new_r("mysql", tagsistant.dbi_instance);
#else
			dbi = dbi_conn_new("mysql");
#endif
			if (NULL == dbi) {
				dbg('s', LOG_ERR, "Error creating MySQL connection");
				exit (1);
			}

			// set connection options
			dbi_conn_set_option(dbi, "host",     dboptions.host);
			dbi_conn_set_option(dbi, "dbname",   dboptions.db);
			dbi_conn_set_option(dbi, "username", dboptions.username);
			dbi_conn_set_option(dbi, "password", dboptions.password);
			dbi_conn_set_option(dbi, "encoding", "UTF-8");

		} else if (TAGSISTANT_DBI_SQLITE_BACKEND == dboptions.backend) {
			if (!tagsistant_driver_is_available("sqlite3")) {
				fprintf(stderr, "SQLite3 driver not installed\n");
				dbg('s', LOG_ERR, "SQLite3 driver not installed");
				exit(1);
			}

			// create connection
#if TAGSISTANT_REENTRANT_DBI
			dbi = dbi_conn_new_r("sqlite3", tagsistant.dbi_instance);
#else
			dbi = dbi_conn_new("sqlite3");
#endif
			if (NULL == dbi) {
				dbg('s', LOG_ERR, "Error connecting to SQLite3");
				exit (1);
			}

			// set connection options
			dbi_conn_set_option(dbi, "dbname", "tags.sql");
			dbi_conn_set_option(dbi, "sqlite3_dbdir", tagsistant.repository);

		} else {

			dbg('s', LOG_ERR, "No or wrong database family specified!");
			exit (1);
		}

		// try to connect
		if (dbi_conn_connect(dbi) < 0) {
			int error = dbi_conn_error(dbi, NULL);
			dbg('s', LOG_ERR, "Could not connect to DB (error %d). Please check the --db settings", error);
			exit(1);
		}

		connections++;

		dbg('s', LOG_INFO, "SQL connection established");
	}

	/* start a transaction */
	if (start_transaction) {
#if TAGSISTANT_USE_INTERNAL_TRANSACTIONS
		switch (tagsistant.sql_database_driver) {
			case TAGSISTANT_DBI_SQLITE_BACKEND:
				tagsistant_query("begin transaction", dbi, NULL, NULL);
				break;

			case TAGSISTANT_DBI_MYSQL_BACKEND:
				tagsistant_query("start transaction", dbi, NULL, NULL);
				break;
		}
#else
		dbi_conn_transaction_begin(dbi);
#endif
	}

	return(dbi);
}