예제 #1
0
/**
 * close() equivalent [second part, first is tagsistant_flush()]
 *
 * @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_release(const char *path, struct fuse_file_info *fi)
{
    int res = 0, tagsistant_errno = 0;
    (void) fi;

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

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

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

	tagsistant_querytree_check_tagging_consistency(qtree);

	// -- object --
	if (QTREE_IS_TAGGABLE(qtree) && 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("RELEASE 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);
		return (-tagsistant_errno);
	} else {
		TAGSISTANT_STOP_OK("RELEASE on %s (%s): OK", path, tagsistant_querytree_type(qtree));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_COMMIT_TRANSACTION);
		return (0);
	}
}
예제 #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
/**
 * utime equivalent
 *
 * @param path the path to utime()ed
 * @param buf struct utimbuf pointer holding new access and modification times
 * @return(0 on success, -errno otherwise)
 */
int tagsistant_utime(const char *path, struct utimbuf *buf)
{
    int res = 0, tagsistant_errno = 0;
	char *utime_path = NULL;

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

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

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

	// -- object on disk --
	if (QTREE_POINTS_TO_OBJECT(qtree)) utime_path = qtree->full_archive_path;

	// -- tags --
	// -- stats --
	// -- relations --
	else utime_path = tagsistant.archive;

	// do the real utime()
//	dbg(LOG_INFO, "utime(%s)", utime_path);

	res = utime(utime_path, buf);
	tagsistant_errno = errno;

TAGSISTANT_EXIT_OPERATION:
	if ( res is -1 ) {
		TAGSISTANT_STOP_ERROR(OPS_OUT "UTIME %s (%s): %d %d: %s", utime_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 "UTIME %s (%s): OK", path, tagsistant_querytree_type(qtree));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_COMMIT_TRANSACTION);
		return (0);
	}
}
예제 #4
0
/**
 * set extended attributes
 *
 * @param path the path
 * @param stbuf pointer to struct stat buffer holding data about file
 * @return(0 on success, -errno otherwise)
 */
int tagsistant_getxattr(const char *path, const char *name, char *value, size_t size)
{
    int res = 0, tagsistant_errno = 0;

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

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

	// -- malformed --
	if (QTREE_IS_MALFORMED(qtree))
		TAGSISTANT_ABORT_OPERATION(ENOENT);
	
	// -- error message --
	if (qtree->error_message && g_regex_match_simple("@/error$", path, G_REGEX_EXTENDED, 0)) {
		TAGSISTANT_ABORT_OPERATION(EFAULT);
	}

	// -- archive --
	else if (QTREE_IS_ARCHIVE(qtree)) {
		if (!g_regex_match_simple(TAGSISTANT_INODE_DELIMITER, qtree->object_path, 0, 0)) {
			res = lgetxattr(qtree->object_path, name, value, size);
			tagsistant_errno = errno;
		} else if (qtree->full_archive_path) {
			res = lgetxattr(qtree->full_archive_path, name, value, size);
			tagsistant_errno = errno;
		} else {
			TAGSISTANT_ABORT_OPERATION(ENOENT);
		}
	}

	// -- object on disk --
	else if (QTREE_POINTS_TO_OBJECT(qtree)) {
		if (qtree->full_archive_path) {
			res = lgetxattr(qtree->full_archive_path, name, value, size);
			tagsistant_errno = errno;
		} else {
			TAGSISTANT_ABORT_OPERATION(ENOENT);
		}
	}

	// -- alias --
	// -- relations --
	// -- stats --
	// -- store (incomplete) --
	// -- tags --
	// -- archive (the directory itself) --
	// -- root --
	else {
		res = lgetxattr(tagsistant.repository, name, value, size);
		tagsistant_errno = errno;
	}

TAGSISTANT_EXIT_OPERATION:

	if ( res == -1 ) {
		TAGSISTANT_STOP_ERROR("GETXATTR on %s {%s}: %d %d: %s", 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("GETXATTR on %s {%s}: OK", path, tagsistant_querytree_type(qtree));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_COMMIT_TRANSACTION);
		return (res);
	}
}
예제 #5
0
파일: write.c 프로젝트: chebee7i/Tagsistant
/**
 * write() equivalent
 *
 * @param path the path of the file to be written
 * @param buf buffer holding write() data
 * @param size how many bytes should be written (size of *buf)
 * @param offset starting of the write
 * @param fi struct fuse_file_info used for open() flags
 * @return(0 on success, -errno otherwise)
 */
int tagsistant_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
    int res = 0, tagsistant_errno = 0, fh = 0;

	TAGSISTANT_START("WRITE on %s [size: %lu offset: %lu]", path, (unsigned long) size, (long unsigned int) offset);

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

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

	// -- alias --
	if (QTREE_IS_ALIAS(qtree) && qtree->alias) {
		res = size;

		gchar *_buf = g_strndup(buf, size);

		// end the string at the first carriage return or line feed character
		gchar *path_ptr = rindex(_buf, '\n');
		if (path_ptr) *path_ptr = '/';

		path_ptr = rindex(_buf, '\r');
		if (path_ptr) *path_ptr = '/';

		// remove double slashes
		GRegex *rx = g_regex_new("//", 0, 0, NULL);
		gchar *_buf2 = g_regex_replace(rx, _buf, -1, 0, "/", 0, NULL);
		g_regex_unref(rx);
		g_free(_buf);
		_buf = _buf2;

		// get the size of the buffer
		size_t max_size = MIN(size, TAGSISTANT_ALIAS_MAX_LENGTH - 1);
		size_t real_size = MIN(max_size, strlen(_buf));

		// copy the buffer to a temporary variable
		gchar *value = g_strndup(_buf, real_size);

		// save the buffer on disk
		tagsistant_sql_alias_set(qtree->dbi, qtree->alias, value);

		g_free(value);
		g_free(_buf);

	} else

	// -- object on disk --
	if (QTREE_POINTS_TO_OBJECT(qtree)) {
		if (!qtree->full_archive_path) {
			dbg('F', LOG_ERR, "Null qtree->full_archive_path");
			TAGSISTANT_ABORT_OPERATION(EFAULT);
		}

#if TAGSISTANT_ENABLE_FILE_HANDLE_CACHING
		if (fi->fh) {
			tagsistant_get_file_handle(fi, fh);
			res = pwrite(fh, buf, size, offset);
			tagsistant_errno = errno;
		}

		if ((-1 == res) || (0 == fh)) {
			if (fh) close(fh);
			fh = open(qtree->full_archive_path, fi->flags|O_WRONLY);
			if (fh)	res = pwrite(fh, buf, size, offset);
			else res = -1;
			tagsistant_errno = errno;
		}

		tagsistant_set_file_handle(fi, fh);
#else
		fh = open(qtree->full_archive_path, fi->flags|O_WRONLY);
		if (fh) {
			res = pwrite(fh, buf, size, offset);
			tagsistant_errno = errno;
			close(fh);
		} else {
			TAGSISTANT_ABORT_OPERATION(errno);
		}
#endif
	}

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

	// dbg('F', LOG_ERR, "Yeah!");

TAGSISTANT_EXIT_OPERATION:
	if ( res == -1 ) {
		TAGSISTANT_STOP_ERROR("WRITE %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);
		return (-tagsistant_errno);
	} else {
		TAGSISTANT_STOP_OK("WRITE %s (%s): OK", path, tagsistant_querytree_type(qtree));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_COMMIT_TRANSACTION);
		return (res);
	}
}
예제 #6
0
파일: link.c 프로젝트: Copperis/Tagsistant
/**
 * link equivalent
 *
 * @param from existing file name
 * @param to new file name
 * @return(0 on success, -errno otherwise)
 */
int tagsistant_link(const char *from, const char *to)
{
	int tagsistant_errno = 0, res = 0;

	TAGSISTANT_START("LINK %s to %s", from, to);

	/*
	 * guess if query points to an external or internal object
	 */
	char *_from = (char *) from;
	if (!TAGSISTANT_PATH_IS_EXTERNAL(from)) {
		_from = (char * ) from + strlen(tagsistant.mountpoint);
		// dbg(LOG_INFO, "%s is internal to %s, trimmed to %s", from, tagsistant.mountpoint, _from);
	}

	tagsistant_querytree *from_qtree = tagsistant_querytree_new(_from, 0, 1, 0, 0);
	tagsistant_querytree *to_qtree = tagsistant_querytree_new(to, 0, 0, 1, 0);

	from_qtree->is_external = (from == _from) ? 1 : 0;

	// -- malformed --
	if (QTREE_IS_MALFORMED(from_qtree)) TAGSISTANT_ABORT_OPERATION(ENOENT);
	if (QTREE_IS_MALFORMED(to_qtree)) TAGSISTANT_ABORT_OPERATION(ENOENT);

	// -- object on disk --
	if (QTREE_POINTS_TO_OBJECT(to_qtree) || (QTREE_IS_STORE(to_qtree) && QTREE_IS_COMPLETE(to_qtree))) {

		// if object_path is null, borrow it from original path
		if (strlen(to_qtree->object_path) == 0) {
			dbg('F', LOG_INFO, "Getting object path from %s", from);
			tagsistant_querytree_set_object_path(to_qtree, g_path_get_basename(from));
		}

		tagsistant_querytree_check_tagging_consistency(to_qtree);

		// if qtree is taggable, do it
		if (QTREE_IS_TAGGABLE(to_qtree)) {
			dbg('F', LOG_INFO, "LINK : Creating %s", to_qtree->object_path);
			res = tagsistant_force_create_and_tag_object(to_qtree, &tagsistant_errno);
			if (-1 == res) goto TAGSISTANT_EXIT_OPERATION;
		} else

		// nothing to do about tags
		{
			dbg('F', LOG_ERR, "%s is not taggable!", to_qtree->full_path); // ??? why ??? should be taggable!!
		}

		// do the real link on disk
		dbg('F', LOG_INFO, "Hard-linking %s to %s", from_qtree->full_archive_path, to_qtree->object_path);
		res = link(from_qtree->full_archive_path, to_qtree->full_archive_path);
		tagsistant_errno = errno;
	}

	// -- store (not complete) --
	// -- tags --
	// -- stats --
	// -- relations --
	// -- alias --
	else TAGSISTANT_ABORT_OPERATION(EINVAL);

TAGSISTANT_EXIT_OPERATION:
	if ( res == -1 ) {
		TAGSISTANT_STOP_ERROR("LINK from %s to %s (%s) (%s): %d %d: %s", from, to, to_qtree->full_archive_path, tagsistant_querytree_type(to_qtree), res, tagsistant_errno, strerror(tagsistant_errno));
		tagsistant_querytree_destroy(from_qtree, TAGSISTANT_ROLLBACK_TRANSACTION);
		tagsistant_querytree_destroy(to_qtree, TAGSISTANT_ROLLBACK_TRANSACTION);
		return (-tagsistant_errno);
	} else {
		TAGSISTANT_STOP_OK("LINK from %s to %s (%s): OK", from, to, tagsistant_querytree_type(to_qtree));
		tagsistant_querytree_destroy(from_qtree, TAGSISTANT_COMMIT_TRANSACTION);
		tagsistant_querytree_destroy(to_qtree, TAGSISTANT_COMMIT_TRANSACTION);
		return (0);
	}
}
예제 #7
0
파일: open.c 프로젝트: Copperis/Tagsistant
/**
 * open() equivalent
 *
 * @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_open(const char *path, struct fuse_file_info *fi)
{
    int res = -1, tagsistant_errno = ENOENT;

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

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

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

	// -- error message --
	if (qtree->error_message && g_regex_match_simple("@/error$", path, G_REGEX_EXTENDED, 0)) {
		res = 1;
		tagsistant_errno = 0;
		goto TAGSISTANT_EXIT_OPERATION;
	}

	// -- object --
	else if (QTREE_POINTS_TO_OBJECT(qtree)) {
		if (tagsistant_is_tags_list_file(qtree)) {
			res = open(tagsistant.tags, fi->flags|O_RDONLY);
			tagsistant_errno = errno;
			goto TAGSISTANT_EXIT_OPERATION;
		}

		if (!qtree->full_archive_path) {
			dbg('F', LOG_ERR, "Null qtree->full_archive_path");
			TAGSISTANT_ABORT_OPERATION(EFAULT);
		}

		res = open(qtree->full_archive_path, fi->flags /*|O_RDONLY */);
		tagsistant_errno = errno;

		if (-1 != res) {
#if TAGSISTANT_ENABLE_FILE_HANDLE_CACHING
			tagsistant_set_file_handle(fi, res);
			dbg('F', LOG_INFO, "Caching %" PRIu64 " = open(%s)", fi->fh, path);
//			fprintf(stderr, "Opened FD %lu\n", fi->fh);

#else
			close(res);
#endif

			tagsistant_querytree_check_tagging_consistency(qtree);

			if (QTREE_IS_TAGGABLE(qtree)) {
				if ((fi->flags & O_WRONLY) || (fi->flags & O_RDWR)) {
					// invalidate the checksum
					dbg('2', LOG_INFO, "Invalidating checksum on %s", path);
					tagsistant_invalidate_object_checksum(qtree->inode, qtree->dbi);
				} else {
					fi->keep_cache = 1;
				}
			}
		} else {
			tagsistant_set_file_handle(fi, 0);
		}
	}

	// -- stats --
	else if (QTREE_IS_STATS(qtree)) {
		res = open(tagsistant.tags, fi->flags|O_RDONLY);
		tagsistant_set_file_handle(fi, res);
		tagsistant_errno = errno;
		fi->keep_cache = 0;
	}

	// -- alias --
	else if (QTREE_IS_ALIAS(qtree) && qtree->alias) {
		if (tagsistant_sql_alias_exists(qtree->dbi, qtree->alias)) {
			res = 0;
			tagsistant_errno = 0;
		} else {
			TAGSISTANT_ABORT_OPERATION(ENOENT);
		}
	}

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

TAGSISTANT_EXIT_OPERATION:
	if ( res == -1 ) {
		TAGSISTANT_STOP_ERROR("OPEN 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);
		return (-tagsistant_errno);
	} else {
		TAGSISTANT_STOP_OK("OPEN on %s (%s): OK", path, tagsistant_querytree_type(qtree));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_COMMIT_TRANSACTION);
		return (0);
	}
}
예제 #8
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);
	}
}
예제 #9
0
/**
 * mknod equivalent (used to create even regular files)
 *
 * @param path the path of the file (block, char, fifo) to be created
 * @param mode file type and permissions
 * @param rdev major and minor numbers, if applies
 * @return(0 on success, -errno otherwise)
 */
int tagsistant_mknod(const char *path, mode_t mode, dev_t rdev)
{
	int res = 0, tagsistant_errno = 0;

	TAGSISTANT_START(OPS_IN "MKNOD on %s [mode: %u rdev: %u]", path, mode, (unsigned int) rdev);

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

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

	// -- archive --
	if (QTREE_IS_ARCHIVE(qtree))
		TAGSISTANT_ABORT_OPERATION(EROFS);

	// -- tags --
	if (QTREE_POINTS_TO_OBJECT(qtree)) {
		if (is_all_path(qtree->full_path)) TAGSISTANT_ABORT_OPERATION(EFAULT);

		if (tagsistant_is_tags_list_file(qtree)) goto TAGSISTANT_EXIT_OPERATION;

		tagsistant_querytree_check_tagging_consistency(qtree);

		if (QTREE_IS_TAGGABLE(qtree)) {
			res = tagsistant_force_create_and_tag_object(qtree, &tagsistant_errno);
		}

		if (qtree->inode) {
			dbg('F', LOG_INFO, "NEW object on disk: mknod(%s) [inode: %d]", qtree->full_archive_path, qtree->inode);

			res = mknod(qtree->full_archive_path, mode|S_IWUSR, rdev);
			tagsistant_errno = errno;

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

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

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

TAGSISTANT_EXIT_OPERATION:
	if ( res is -1 ) {
		TAGSISTANT_STOP_ERROR(OPS_OUT "MKNOD 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);
		return (-tagsistant_errno);
	} else {
		TAGSISTANT_STOP_OK(OPS_OUT "MKNOD on %s (%s): OK", path, tagsistant_querytree_type(qtree));
		tagsistant_querytree_destroy(qtree, TAGSISTANT_COMMIT_TRANSACTION);
		return (0);
	}
}