diff --git a/README.md b/README.md index 9b61156d49ac4a7469addd271bcd7926c1b7cd8c..e5a24c6fdfedec9c297918c1472897967c4a9a80 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ If you want to disable libvlc, you can do so by passing `-Dlibvlc=disabled` to m * libvlc * libvlcpp (available as a git submodule) * libsqlite3 (>= 3.33.0) +* libzfs (optional, for ZFS support) * libjpeg (only when using libvlc 3) * rapidjson when building the functional tests diff --git a/config.h.in b/config.h.in index 47224d7b891cf37870c5d744ef28eb2c2c9c7224..b46f6fb6ce848378e050a2101ae9a30f8cc54ac5 100644 --- a/config.h.in +++ b/config.h.in @@ -19,6 +19,7 @@ #mesondefine CXX11_THREAD_LOCAL #mesondefine CXX11_CONDITION_VARIABLE +#mesondefine HAVE_LIBZFS #mesondefine HAVE_LIBVLC #mesondefine HAVE_JPEG #mesondefine FORCE_ATTACHMENTS_API diff --git a/meson.build b/meson.build index 095e1ef1a804246734c575bdb14aaad4e4ca7ab8..6753ee7738c5ebdd9109ba1ce4c4652383649d03 100644 --- a/meson.build +++ b/meson.build @@ -110,6 +110,11 @@ endif sqlite_dep = dependency('sqlite3', version: '>= 3.33.0', required: true) +libzfs_dep = dependency('libzfs', version: '>= 2.0.0', required: get_option('libzfs')) +if libzfs_dep.found() + cdata.set('HAVE_LIBZFS', 1) +endif + libvlc_dep = dependency('libvlc', version: '>= 3.0', required: get_option('libvlc')) if libvlc_dep.found() cdata.set('HAVE_LIBVLC', 1) diff --git a/meson_options.txt b/meson_options.txt index a3539c59b04171fc7917b398c0648b3be0ec69dd..e87e91caaa668036bb826dd4882c5be954fc91d3 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,4 @@ +option('libzfs', type: 'feature', value: 'auto') option('libvlc', type: 'feature', value: 'auto') option('libjpeg_prefix', type: 'string') option('tests', type: 'feature', value: 'auto') diff --git a/src/filesystem/unix/DeviceLister.cpp b/src/filesystem/unix/DeviceLister.cpp index ca5e08db774cc9fa0700f6ca8dcdda02019d92be..d4d8e0d75a5db8c5022d05a99cfca27930c6f86a 100644 --- a/src/filesystem/unix/DeviceLister.cpp +++ b/src/filesystem/unix/DeviceLister.cpp @@ -30,6 +30,10 @@ #include "utils/Directory.h" #include "medialibrary/filesystem/Errors.h" +#ifdef HAVE_LIBZFS +# include "utils/ZFS.h" +#endif + #include #include #include @@ -44,6 +48,7 @@ #include #include #include +#include namespace medialibrary { @@ -271,7 +276,13 @@ std::vector DeviceLister::getAllowedFsTypes() const std::string fsType; iss >> fsType; if ( fsType == "nodev" ) - continue; + { +#ifdef HAVE_LIBZFS + iss >> fsType; + if ( fsType != "zfs" ) +#endif + continue; + } res.push_back( std::move( fsType ) ); } return res; @@ -289,55 +300,82 @@ std::vector DeviceLister::devices() const return res; } auto devices = listDevices(); - if ( devices.empty() == true ) - { - LOG_WARN( "Failed to detect any device" ); - return res; - } + +#ifdef HAVE_LIBZFS + LibZFS libzfs; + if ( libzfs.init() < 0 ) + LOG_WARN( "Failed to init libzfs: ", LibZFS::error_init( errno ) ); +#endif + for ( auto& p : mountpoints ) { assert( p.second.empty() == false ); const auto& partitionPath = p.first; + auto deviceName = utils::file::fileName( partitionPath ); auto it = devices.find( deviceName ); std::string uuid; if ( it != end( devices ) ) + { uuid = it->second; - else + goto success; + } + + LOG_INFO( "Failed to find known device with name ", partitionPath, + ". Attempting to resolve using device mapper" ); + + try { - std::pair dmPair; - LOG_INFO( "Failed to find known device with name ", deviceName, - ". Attempting to resolve using device mapper" ); - try - { - // Fetch a pair containing the device-mapper device name and - // the block device it points to - dmPair = deviceFromDeviceMapper( partitionPath ); - } - catch( fs::errors::DeviceMapper& ex ) - { - LOG_WARN( ex.what() ); - continue; - } + // Fetch a pair containing the device-mapper device name and + // the block device it points to + auto dmPair = deviceFromDeviceMapper( partitionPath ); + // First try with the block device it = devices.find( dmPair.second ); if ( it != end( devices ) ) + { uuid = it->second; + goto success; + } else { // Otherwise try with the device mapper name. it = devices.find( dmPair.first ); if( it != end( devices ) ) - uuid = it->second; - else { - LOG_ERROR( "Failed to resolve device ", deviceName, - " to any known device" ); - continue; + uuid = it->second; + goto success; } } } + catch( fs::errors::DeviceMapper& ex ) + { + LOG_WARN( ex.what() ); + } + +#ifdef HAVE_LIBZFS + LOG_INFO( "Failed to resolve device using device mapper" ); + if ( libzfs.active() ) + { + LOG_INFO( "Attempting to resolve using ZFS" ); + try + { + auto zfs = libzfs.openFs( partitionPath.c_str() ); + uuid = std::to_string( zfs.get_guid() ); + goto success; + } + catch ( ... ) + { + } + } +#endif + + LOG_ERROR( "Failed to resolve device ", partitionPath, + " to any known device" ); + continue; + +success: auto removable = isRemovable( partitionPath ); res.emplace_back( uuid, std::move( p.second ), removable ); diff --git a/src/meson.build b/src/meson.build index 0bdb51480297c1cd95dedda72543dd594c3f010d..7c0a465c28408aad729a5dc8724f22af768f04f7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -81,6 +81,15 @@ if not libxxhash_dep.found() ) endif +zfswrapper_lib = [] +if libzfs_dep.found() + zfswrapper_lib = static_library('zfswrapper', + ['utils/ZFS.cpp'], + dependencies : [libzfs_dep] + ) +endif +libzfswrapper_dep = declare_dependency(link_with: zfswrapper_lib) + if libvlc_dep.found() if libvlc_dep.version().version_compare('>=4.0') medialib_src += [ @@ -129,6 +138,7 @@ endif medialib = library('medialibrary', medialib_src, dependencies: [ sqlite_dep, + libzfswrapper_dep, libjpeg_dep, libvlc_dep, libvlcpp_dep, diff --git a/src/utils/ZFS.cpp b/src/utils/ZFS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5503d9d389c2e6b0cb25c3d6d7bdca2c1b9ec3a --- /dev/null +++ b/src/utils/ZFS.cpp @@ -0,0 +1,50 @@ +#include +#include + +#include "ZFS.h" + +namespace medialibrary +{ + +#define LIBZFS_HANDLE static_cast( handle ) + +LibZFS::~LibZFS() +{ + ::libzfs_fini( LIBZFS_HANDLE ); +} + +int LibZFS::init() +{ + handle = ::libzfs_init(); + if ( !handle ) + return -1; + return 0; +} + +const char* LibZFS::error_init( int err ) +{ + return ::libzfs_error_init( err ); +} + +ZFS LibZFS::openFs( const char* p ) +{ + auto h = ::zfs_open( LIBZFS_HANDLE, p, ZFS_TYPE_FILESYSTEM ); + if (!h) + throw std::runtime_error(""); + return h; +} + +#define ZFS_HANDLE static_cast( handle ) + +ZFS::~ZFS() +{ + if ( handle ) + ::zfs_close( ZFS_HANDLE ); +} + +uint64_t ZFS::get_guid() +{ + return ::zfs_prop_get_int( ZFS_HANDLE, ZFS_PROP_GUID ); +} + +} diff --git a/src/utils/ZFS.h b/src/utils/ZFS.h new file mode 100644 index 0000000000000000000000000000000000000000..df1b8be7d2fcb840c735ddc20921ddafa84d7d0c --- /dev/null +++ b/src/utils/ZFS.h @@ -0,0 +1,30 @@ +#pragma once + +namespace medialibrary +{ + +class ZFS +{ + void* handle; + +public: + ZFS( void* handle ) : handle( handle ) {}; + ~ZFS(); + uint64_t get_guid(); +}; + +class LibZFS +{ + void* handle; + +public: + LibZFS() : handle( nullptr ) {}; + ~LibZFS(); + + int init(); + static const char* error_init( int err ); + bool active() const { return handle != nullptr; } + ZFS openFs( const char* p ); +}; + +}