mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-11 17:02:25 +00:00
Allow symlinks in file storage
This commit is contained in:
parent
de36038733
commit
059fc1de69
@ -118,7 +118,7 @@ bool pathStartsWith(const std::filesystem::path & path, const std::filesystem::p
|
|||||||
return absolute_path.starts_with(absolute_prefix_path);
|
return absolute_path.starts_with(absolute_prefix_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool symlinkStartsWith(const std::filesystem::path & path, const std::filesystem::path & prefix_path)
|
bool fileOrSymlinkPathStartsWith(const std::filesystem::path & path, const std::filesystem::path & prefix_path)
|
||||||
{
|
{
|
||||||
/// Differs from pathStartsWith in how `path` is normalized before comparison.
|
/// Differs from pathStartsWith in how `path` is normalized before comparison.
|
||||||
/// Make `path` absolute if it was relative and put it into normalized form: remove
|
/// Make `path` absolute if it was relative and put it into normalized form: remove
|
||||||
@ -140,13 +140,14 @@ bool pathStartsWith(const String & path, const String & prefix_path)
|
|||||||
return pathStartsWith(filesystem_path, filesystem_prefix_path);
|
return pathStartsWith(filesystem_path, filesystem_prefix_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool symlinkStartsWith(const String & path, const String & prefix_path)
|
bool fileOrSymlinkPathStartsWith(const String & path, const String & prefix_path)
|
||||||
{
|
{
|
||||||
auto filesystem_path = std::filesystem::path(path);
|
auto filesystem_path = std::filesystem::path(path);
|
||||||
auto filesystem_prefix_path = std::filesystem::path(prefix_path);
|
auto filesystem_prefix_path = std::filesystem::path(prefix_path);
|
||||||
|
|
||||||
return symlinkStartsWith(filesystem_path, filesystem_prefix_path);
|
return fileOrSymlinkPathStartsWith(filesystem_path, filesystem_prefix_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,8 +35,9 @@ bool pathStartsWith(const std::filesystem::path & path, const std::filesystem::p
|
|||||||
/// Returns true if path starts with prefix path
|
/// Returns true if path starts with prefix path
|
||||||
bool pathStartsWith(const String & path, const String & prefix_path);
|
bool pathStartsWith(const String & path, const String & prefix_path);
|
||||||
|
|
||||||
/// Returns true if symlink starts with prefix path
|
/// Same as pathStartsWith, but wihtout canonization, i.e. allowed to check symlinks.
|
||||||
bool symlinkStartsWith(const String & path, const String & prefix_path);
|
/// (Path is made absolute and normalized)
|
||||||
|
bool fileOrSymlinkPathStartsWith(const String & path, const String & prefix_path);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ FileDictionarySource::FileDictionarySource(
|
|||||||
, context(context_)
|
, context(context_)
|
||||||
{
|
{
|
||||||
auto user_files_path = context->getUserFilesPath();
|
auto user_files_path = context->getUserFilesPath();
|
||||||
if (created_from_ddl && !pathStartsWith(filepath, user_files_path))
|
if (created_from_ddl && !fileOrSymlinkPathStartsWith(filepath, user_files_path))
|
||||||
throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "File path {} is not inside {}", filepath, user_files_path);
|
throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "File path {} is not inside {}", filepath, user_files_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,13 +41,7 @@ LibraryDictionarySource::LibraryDictionarySource(
|
|||||||
, context(Context::createCopy(context_))
|
, context(Context::createCopy(context_))
|
||||||
{
|
{
|
||||||
auto dictionaries_lib_path = context->getDictionariesLibPath();
|
auto dictionaries_lib_path = context->getDictionariesLibPath();
|
||||||
bool path_checked = false;
|
if (created_from_ddl && !fileOrSymlinkPathStartsWith(path, dictionaries_lib_path))
|
||||||
if (fs::is_symlink(path))
|
|
||||||
path_checked = symlinkStartsWith(path, dictionaries_lib_path);
|
|
||||||
else
|
|
||||||
path_checked = pathStartsWith(path, dictionaries_lib_path);
|
|
||||||
|
|
||||||
if (created_from_ddl && !path_checked)
|
|
||||||
throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "File path {} is not inside {}", path, dictionaries_lib_path);
|
throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "File path {} is not inside {}", path, dictionaries_lib_path);
|
||||||
|
|
||||||
if (!fs::exists(path))
|
if (!fs::exists(path))
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <Common/escapeForFileName.h>
|
#include <Common/escapeForFileName.h>
|
||||||
#include <Common/typeid_cast.h>
|
#include <Common/typeid_cast.h>
|
||||||
#include <Common/parseGlobs.h>
|
#include <Common/parseGlobs.h>
|
||||||
|
#include <Common/filesystemHelpers.h>
|
||||||
#include <Storages/ColumnsDescription.h>
|
#include <Storages/ColumnsDescription.h>
|
||||||
#include <Storages/StorageInMemoryMetadata.h>
|
#include <Storages/StorageInMemoryMetadata.h>
|
||||||
|
|
||||||
@ -124,8 +125,8 @@ void checkCreationIsAllowed(ContextPtr context_global, const std::string & db_di
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/// "/dev/null" is allowed for perf testing
|
/// "/dev/null" is allowed for perf testing
|
||||||
if (!startsWith(table_path, db_dir_path) && table_path != "/dev/null")
|
if (!fileOrSymlinkPathStartsWith(table_path, db_dir_path) && table_path != "/dev/null")
|
||||||
throw Exception("File is not inside " + db_dir_path, ErrorCodes::DATABASE_ACCESS_DENIED);
|
throw Exception(ErrorCodes::DATABASE_ACCESS_DENIED, "File `{}` is not inside `{}`", table_path, db_dir_path);
|
||||||
|
|
||||||
if (fs::exists(table_path) && fs::is_directory(table_path))
|
if (fs::exists(table_path) && fs::is_directory(table_path))
|
||||||
throw Exception("File must not be a directory", ErrorCodes::INCORRECT_FILE_NAME);
|
throw Exception("File must not be a directory", ErrorCodes::INCORRECT_FILE_NAME);
|
||||||
@ -140,7 +141,10 @@ Strings StorageFile::getPathsList(const String & table_path, const String & user
|
|||||||
fs_table_path = user_files_absolute_path / fs_table_path;
|
fs_table_path = user_files_absolute_path / fs_table_path;
|
||||||
|
|
||||||
Strings paths;
|
Strings paths;
|
||||||
const String path = fs::weakly_canonical(fs_table_path);
|
/// Do not use fs::canonical or fs::weakly_canonical.
|
||||||
|
/// Otherwise it will not allow to work with symlinks in `user_files_path` directory.
|
||||||
|
String path = std::filesystem::absolute(fs_table_path);
|
||||||
|
path = fs::path(path).lexically_normal(); /// Normalize path.
|
||||||
if (path.find_first_of("*?{") == std::string::npos)
|
if (path.find_first_of("*?{") == std::string::npos)
|
||||||
{
|
{
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
OK
|
30
tests/queries/0_stateless/02051_symlinks_to_user_files.sh
Executable file
30
tests/queries/0_stateless/02051_symlinks_to_user_files.sh
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Tags: no-fasttest
|
||||||
|
|
||||||
|
CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
# shellcheck source=../shell_config.sh
|
||||||
|
. "$CUR_DIR"/../shell_config.sh
|
||||||
|
|
||||||
|
# See 01658_read_file_to_string_column.sh
|
||||||
|
user_files_path=$(clickhouse-client --query "select _path,_file from file('nonexist.txt', 'CSV', 'val1 char')" 2>&1 | grep Exception | awk '{gsub("/nonexist.txt","",$9); print $9}')
|
||||||
|
|
||||||
|
mkdir -p "${user_files_path}/"
|
||||||
|
chmod 777 "${user_files_path}"
|
||||||
|
|
||||||
|
export FILE="test_symlink_${CLICKHOUSE_DATABASE}"
|
||||||
|
|
||||||
|
symlink_path=${user_files_path}/${FILE}
|
||||||
|
file_path=$CUR_DIR/${FILE}
|
||||||
|
|
||||||
|
function cleanup()
|
||||||
|
{
|
||||||
|
rm ${symlink_path} ${file_path}
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
touch ${file_path}
|
||||||
|
ln -s ${file_path} ${symlink_path}
|
||||||
|
|
||||||
|
${CLICKHOUSE_CLIENT} --query="insert into table function file('${symlink_path}', 'Values', 'a String') select 'OK'";
|
||||||
|
${CLICKHOUSE_CLIENT} --query="select * from file('${symlink_path}', 'Values', 'a String')";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user