From f5197cc8ffb0e2c96f18b6fcfee523bb3619070a Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sat, 17 Feb 2018 21:26:47 -0800 Subject: [PATCH] ircd::fs: Add magic interface. --- include/ircd/fs/fs.h | 1 + include/ircd/fs/magic.h | 25 ++++++ ircd/fs.cc | 180 +++++++++++++++++++++++++++++++++++++++- ircd/info.cc | 5 +- 4 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 include/ircd/fs/magic.h diff --git a/include/ircd/fs/fs.h b/include/ircd/fs/fs.h index 12ea7df8d..88459fd6f 100644 --- a/include/ircd/fs/fs.h +++ b/include/ircd/fs/fs.h @@ -87,6 +87,7 @@ namespace ircd::fs extern aio *aioctx; } +#include "magic.h" #include "read.h" #include "write.h" diff --git a/include/ircd/fs/magic.h b/include/ircd/fs/magic.h new file mode 100644 index 000000000..0b18dd2f6 --- /dev/null +++ b/include/ircd/fs/magic.h @@ -0,0 +1,25 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#pragma once +#define HAVE_IRCD_FS_MAGIC_H + +namespace ircd::fs::magic +{ + IRCD_EXCEPTION(fs::error, error) + + int version(); + + string_view mime(const mutable_buffer &out, const const_buffer &); + string_view mime_type(const mutable_buffer &out, const const_buffer &); + string_view mime_encoding(const mutable_buffer &out, const const_buffer &); + string_view extensions(const mutable_buffer &out, const const_buffer &); + string_view description(const mutable_buffer &out, const const_buffer &); +} diff --git a/ircd/fs.cc b/ircd/fs.cc index 1a8d8fb03..319857fe6 100644 --- a/ircd/fs.cc +++ b/ircd/fs.cc @@ -8,6 +8,7 @@ // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. +#include #include @@ -24,6 +25,12 @@ namespace ircd::fs extern const std::array()> paths; } +namespace ircd::fs::magic +{ + static void init(); + static void fini(); +} + /// Non-null when aio is available for use decltype(ircd::fs::aioctx) ircd::fs::aioctx @@ -45,6 +52,8 @@ ircd::fs::paths ircd::fs::init::init() { + magic::init(); + #ifdef IRCD_USE_AIO assert(!aioctx); aioctx = new aio{}; @@ -65,6 +74,7 @@ noexcept #endif assert(!aioctx); + magic::fini(); } /////////////////////////////////////////////////////////////////////////////// @@ -141,7 +151,6 @@ ircd::fs::read__std(const string_view &path, }; } - /////////////////////////////////////////////////////////////////////////////// // // fs/write.h @@ -346,3 +355,172 @@ catch(const std::out_of_range &e) { return nullptr; } + +/////////////////////////////////////////////////////////////////////////////// +// +// fs/magic.h +// + +namespace ircd::fs::magic +{ + magic_t cookie; + + // magic_getflags() may not be available; this will supplement for our + // tracking of their flags state. + int flags{MAGIC_NONE}; + + static void throw_on_error(const magic_t &); + static void set_flags(const magic_t &, const int &flags); + static string_view call(const magic_t &, const int &flags, const mutable_buffer &, const std::function &); + static string_view call(const magic_t &, const int &flags, const mutable_buffer &, const const_buffer &); +} + +void +ircd::fs::magic::init() +{ + if(unlikely(!(cookie = ::magic_open(flags)))) + throw error{"magic_open() failed"}; + + //TODO: conf; TODO: windows + static const char *const paths[] + { + "/usr/local/share/misc/magic.mgc", + "/usr/share/misc/magic.mgc", + nullptr + }; + + for(const char *const *path{paths}; *path; ++path) + if(magic_check(cookie, *path) == 0) + if(magic_load(cookie, *path) == 0) + return; + + throw error + { + "Failed to open any magic database" + }; +} + +void +ircd::fs::magic::fini() +{ + ::magic_close(cookie); +} + +ircd::string_view +ircd::fs::magic::description(const mutable_buffer &out, + const const_buffer &buffer) +{ + return call(cookie, MAGIC_NONE, out, buffer); +} + +ircd::string_view +ircd::fs::magic::extensions(const mutable_buffer &out, + const const_buffer &buffer) +{ + return call(cookie, MAGIC_EXTENSION, out, buffer); +} + +ircd::string_view +ircd::fs::magic::mime_encoding(const mutable_buffer &out, + const const_buffer &buffer) +{ + return call(cookie, MAGIC_MIME_ENCODING, out, buffer); +} + +ircd::string_view +ircd::fs::magic::mime_type(const mutable_buffer &out, + const const_buffer &buffer) +{ + return call(cookie, MAGIC_MIME_TYPE, out, buffer); +} + +ircd::string_view +ircd::fs::magic::mime(const mutable_buffer &out, + const const_buffer &buffer) +{ + return call(cookie, MAGIC_MIME, out, buffer); +} + +ircd::string_view +ircd::fs::magic::call(const magic_t &cookie, + const int &flags, + const mutable_buffer &out, + const const_buffer &buffer) +{ + return call(cookie, flags, out, [&cookie, &buffer] + { + return magic_buffer(cookie, data(buffer), size(buffer)); + }); +} + +ircd::string_view +ircd::fs::magic::call(const magic_t &cookie, + const int &ours, + const mutable_buffer &out, + const std::function &closure) +{ + const auto &theirs{magic::flags}; + const unwind reset{[&theirs, &cookie] + { + set_flags(cookie, theirs); + }}; + + set_flags(cookie, ours); + string_view str + { + closure() + }; + + if(!str) + { + throw_on_error(cookie); + str = "application/octet-stream"_sv; + } + + return { data(out), copy(out, str) }; +} + +void +ircd::fs::magic::set_flags(const magic_t &cookie, + const int &flags) +{ + if(magic_setflags(cookie, flags) == -1) + throw_on_error(cookie); +} + +void +ircd::fs::magic::throw_on_error(const magic_t &cookie) +{ + const char *const errstr + { + ::magic_error(cookie) + }; + + if(errstr) + throw error + { + "%s", errstr + }; + + assert(magic_errno(cookie) == 0); +} + +int +ircd::fs::magic::version() +{ + return ::magic_version(); +} + +__attribute__((constructor)) +static void +ircd_fs_magic_version_check() +{ + if(unlikely(MAGIC_VERSION != ircd::fs::magic::version())) + { + fprintf(stderr, "FATAL: linked libmagic version %d != compiled magic.h version %d.\n", + ircd::fs::magic::version(), + MAGIC_VERSION); + + std::terminate(); + } +} diff --git a/ircd/info.cc b/ircd/info.cc index babaaea64..f16fa13ab 100644 --- a/ircd/info.cc +++ b/ircd/info.cc @@ -30,14 +30,15 @@ ircd::info::init() // assumed for this execution. log::info { - "%s. boost %u.%u.%u. rocksdb %s. sodium %s. %s.", + "%s. boost %u.%u.%u. rocksdb %s. sodium %s. %s. libmagic %d.", PACKAGE_STRING, boost_version[0], boost_version[1], boost_version[2], db::version, nacl::version(), - openssl::version() + openssl::version(), + fs::magic::version() }; // This message flashes posix information about the system and platform IRCd