/** * 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); } }
/** * 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); } }
/** * 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); } }
/** * 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); } }
/** * 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); } }
/** * 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); } }
/** * 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); } }
/** * 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); } }
/** * 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); } }