mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
Merge pull request #27815 from kssenii/fix-symlinks0in-bridge
Allow symlinks to library dictionary path
This commit is contained in:
commit
87f607faf9
@ -122,6 +122,24 @@ bool pathStartsWith(const std::filesystem::path & path, const std::filesystem::p
|
||||
return path_starts_with_prefix_path;
|
||||
}
|
||||
|
||||
bool symlinkStartsWith(const std::filesystem::path & path, const std::filesystem::path & prefix_path)
|
||||
{
|
||||
/// Differs from pathStartsWith in how `path` is normalized before comparison.
|
||||
/// Make `path` absolute if it was relative and put it into normalized form: remove
|
||||
/// `.` and `..` and extra `/`. Path is not canonized because otherwise path will
|
||||
/// not be a path of a symlink itself.
|
||||
|
||||
auto absolute_path = std::filesystem::absolute(path);
|
||||
absolute_path = absolute_path.lexically_normal(); /// Normalize path.
|
||||
auto absolute_prefix_path = std::filesystem::absolute(prefix_path);
|
||||
absolute_prefix_path = absolute_prefix_path.lexically_normal(); /// Normalize path.
|
||||
|
||||
auto [_, prefix_path_mismatch_it] = std::mismatch(absolute_path.begin(), absolute_path.end(), absolute_prefix_path.begin(), absolute_prefix_path.end());
|
||||
|
||||
bool path_starts_with_prefix_path = (prefix_path_mismatch_it == absolute_prefix_path.end());
|
||||
return path_starts_with_prefix_path;
|
||||
}
|
||||
|
||||
bool pathStartsWith(const String & path, const String & prefix_path)
|
||||
{
|
||||
auto filesystem_path = std::filesystem::path(path);
|
||||
@ -130,6 +148,13 @@ bool pathStartsWith(const String & path, const String & prefix_path)
|
||||
return pathStartsWith(filesystem_path, filesystem_prefix_path);
|
||||
}
|
||||
|
||||
bool symlinkStartsWith(const String & path, const String & prefix_path)
|
||||
{
|
||||
auto filesystem_path = std::filesystem::path(path);
|
||||
auto filesystem_prefix_path = std::filesystem::path(prefix_path);
|
||||
|
||||
return symlinkStartsWith(filesystem_path, filesystem_prefix_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,6 +35,8 @@ bool pathStartsWith(const std::filesystem::path & path, const std::filesystem::p
|
||||
/// Returns true if path starts with prefix path
|
||||
bool pathStartsWith(const String & path, const String & prefix_path);
|
||||
|
||||
bool symlinkStartsWith(const String & path, const String & prefix_path);
|
||||
|
||||
}
|
||||
|
||||
namespace FS
|
||||
|
@ -41,10 +41,13 @@ LibraryDictionarySource::LibraryDictionarySource(
|
||||
, sample_block{sample_block_}
|
||||
, context(Context::createCopy(context_))
|
||||
{
|
||||
if (fs::path(path).is_relative())
|
||||
path = fs::canonical(path);
|
||||
bool path_checked = false;
|
||||
if (fs::is_symlink(path))
|
||||
path_checked = symlinkStartsWith(path, context->getDictionariesLibPath());
|
||||
else
|
||||
path_checked = pathStartsWith(path, context->getDictionariesLibPath());
|
||||
|
||||
if (created_from_ddl && !pathStartsWith(path, context->getDictionariesLibPath()))
|
||||
if (created_from_ddl && !path_checked)
|
||||
throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "File path {} is not inside {}", path, context->getDictionariesLibPath());
|
||||
|
||||
if (!fs::exists(path))
|
||||
|
@ -44,6 +44,11 @@ def ch_cluster():
|
||||
'/usr/bin/g++ -shared -o /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.so -fPIC /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.cpp'],
|
||||
user='root')
|
||||
|
||||
instance.exec_in_container(
|
||||
['bash', '-c',
|
||||
'/usr/bin/g++ -shared -o /dict_lib_copy.so -fPIC /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.cpp'], user='root')
|
||||
instance.exec_in_container(['bash', '-c', 'ln -s /dict_lib_copy.so /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib_symlink.so'])
|
||||
|
||||
yield cluster
|
||||
|
||||
finally:
|
||||
@ -59,6 +64,7 @@ def test_load_all(ch_cluster):
|
||||
if instance.is_built_with_memory_sanitizer():
|
||||
pytest.skip("Memory Sanitizer cannot work with third-party shared libraries")
|
||||
|
||||
instance.query('DROP DICTIONARY IF EXISTS lib_dict')
|
||||
instance.query('''
|
||||
CREATE DICTIONARY lib_dict (key UInt64, value1 UInt64, value2 UInt64, value3 UInt64)
|
||||
PRIMARY KEY key
|
||||
@ -128,6 +134,7 @@ def test_load_keys(ch_cluster):
|
||||
if instance.is_built_with_memory_sanitizer():
|
||||
pytest.skip("Memory Sanitizer cannot work with third-party shared libraries")
|
||||
|
||||
instance.query('DROP DICTIONARY IF EXISTS lib_dict_ckc')
|
||||
instance.query('''
|
||||
CREATE DICTIONARY lib_dict_ckc (key UInt64, value1 UInt64, value2 UInt64, value3 UInt64)
|
||||
PRIMARY KEY key
|
||||
@ -148,6 +155,7 @@ def test_load_all_many_rows(ch_cluster):
|
||||
pytest.skip("Memory Sanitizer cannot work with third-party shared libraries")
|
||||
|
||||
num_rows = [1000, 10000, 100000, 1000000]
|
||||
instance.query('DROP DICTIONARY IF EXISTS lib_dict')
|
||||
for num in num_rows:
|
||||
instance.query('''
|
||||
CREATE DICTIONARY lib_dict (key UInt64, value1 UInt64, value2 UInt64, value3 UInt64)
|
||||
@ -267,6 +275,42 @@ def test_bridge_dies_with_parent(ch_cluster):
|
||||
instance.query('DROP DICTIONARY lib_dict_c')
|
||||
|
||||
|
||||
def test_path_validation(ch_cluster):
|
||||
if instance.is_built_with_memory_sanitizer():
|
||||
pytest.skip("Memory Sanitizer cannot work with third-party shared libraries")
|
||||
|
||||
instance.query('DROP DICTIONARY IF EXISTS lib_dict_c')
|
||||
instance.query('''
|
||||
CREATE DICTIONARY lib_dict_c (key UInt64, value1 UInt64, value2 UInt64, value3 UInt64)
|
||||
PRIMARY KEY key SOURCE(library(PATH '/etc/clickhouse-server/config.d/dictionaries_lib/dict_lib_symlink.so'))
|
||||
LAYOUT(CACHE(
|
||||
SIZE_IN_CELLS 10000000
|
||||
BLOCK_SIZE 4096
|
||||
FILE_SIZE 16777216
|
||||
READ_BUFFER_SIZE 1048576
|
||||
MAX_STORED_KEYS 1048576))
|
||||
LIFETIME(2) ;
|
||||
''')
|
||||
|
||||
result = instance.query('''select dictGet(lib_dict_c, 'value1', toUInt64(1));''')
|
||||
assert(result.strip() == '101')
|
||||
|
||||
instance.query('DROP DICTIONARY IF EXISTS lib_dict_c')
|
||||
instance.query('''
|
||||
CREATE DICTIONARY lib_dict_c (key UInt64, value1 UInt64, value2 UInt64, value3 UInt64)
|
||||
PRIMARY KEY key SOURCE(library(PATH '/etc/clickhouse-server/config.d/dictionaries_lib/../../../../dict_lib_copy.so'))
|
||||
LAYOUT(CACHE(
|
||||
SIZE_IN_CELLS 10000000
|
||||
BLOCK_SIZE 4096
|
||||
FILE_SIZE 16777216
|
||||
READ_BUFFER_SIZE 1048576
|
||||
MAX_STORED_KEYS 1048576))
|
||||
LIFETIME(2) ;
|
||||
''')
|
||||
result = instance.query_and_get_error('''select dictGet(lib_dict_c, 'value1', toUInt64(1));''')
|
||||
assert('DB::Exception: File path /etc/clickhouse-server/config.d/dictionaries_lib/../../../../dict_lib_copy.so is not inside /etc/clickhouse-server/config.d/dictionaries_lib' in result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cluster.start()
|
||||
input("Cluster created, press any key to destroy...")
|
||||
|
Loading…
Reference in New Issue
Block a user