add support for creating symlinks, for torrents with symlinks in them

This commit is contained in:
arvidn 2019-02-26 14:44:16 +01:00 committed by Arvid Norberg
parent 57e56d5070
commit 381d5a3c5d
10 changed files with 103 additions and 41 deletions

View File

@ -1,3 +1,4 @@
* support creating symlinks, for torrents with symlinks in them
* fix error in seed_mode flag * fix error in seed_mode flag
* support magnet link parameters with number siffixes * support magnet link parameters with number siffixes
* consistently use "lt" namespace in examples and documentation * consistently use "lt" namespace in examples and documentation

View File

@ -345,6 +345,7 @@ void bind_alert()
.value("partfile_read", operation_t::partfile_read) .value("partfile_read", operation_t::partfile_read)
.value("partfile_write", operation_t::partfile_write) .value("partfile_write", operation_t::partfile_write)
.value("hostname_lookup", operation_t::hostname_lookup) .value("hostname_lookup", operation_t::hostname_lookup)
.value("symlink", operation_t::symlink)
; ;
def("operation_name", static_cast<char const*(*)(operation_t)>(&lt::operation_name)); def("operation_name", static_cast<char const*(*)(operation_t)>(&lt::operation_name));

View File

@ -166,6 +166,7 @@ POSSIBILITY OF SUCH DAMAGE.
#endif #endif
#endif // __APPLE__ #endif // __APPLE__
#define TORRENT_HAS_SYMLINK 1
#define TORRENT_USE_DEV_RANDOM 1 #define TORRENT_USE_DEV_RANDOM 1
#define TORRENT_HAVE_MMAP 1 #define TORRENT_HAVE_MMAP 1
@ -188,6 +189,7 @@ POSSIBILITY OF SUCH DAMAGE.
# define TORRENT_USE_PREAD 1 # define TORRENT_USE_PREAD 1
#endif #endif
#define TORRENT_HAS_SYMLINK 1
#define TORRENT_HAVE_MMAP 1 #define TORRENT_HAVE_MMAP 1
#define TORRENT_USE_NETLINK 1 #define TORRENT_USE_NETLINK 1
#define TORRENT_USE_IFADDRS 0 #define TORRENT_USE_IFADDRS 0
@ -308,6 +310,7 @@ POSSIBILITY OF SUCH DAMAGE.
#define TORRENT_HAS_SALEN 0 #define TORRENT_HAS_SALEN 0
#define TORRENT_HAS_SEM_RELTIMEDWAIT 1 #define TORRENT_HAS_SEM_RELTIMEDWAIT 1
#define TORRENT_HAVE_MMAP 1 #define TORRENT_HAVE_MMAP 1
#define TORRENT_HAS_SYMLINK 1
// ==== BEOS === // ==== BEOS ===
#elif defined __BEOS__ || defined __HAIKU__ #elif defined __BEOS__ || defined __HAIKU__
@ -323,6 +326,7 @@ POSSIBILITY OF SUCH DAMAGE.
#define TORRENT_HURD #define TORRENT_HURD
#define TORRENT_USE_IFADDRS 1 #define TORRENT_USE_IFADDRS 1
#define TORRENT_USE_IFCONF 1 #define TORRENT_USE_IFCONF 1
#define TORRENT_HAS_SYMLINK 1
// ==== eCS(OS/2) === // ==== eCS(OS/2) ===
#elif defined __OS2__ #elif defined __OS2__
@ -505,6 +509,10 @@ constexpr std::size_t TORRENT_WRITE_HANDLER_MAX_SIZE = 342;
# endif # endif
#endif #endif
#ifndef TORRENT_HAS_SYMLINK
#define TORRENT_HAS_SYMLINK 0
#endif
// debug builds have asserts enabled by default, release // debug builds have asserts enabled by default, release
// builds have asserts if they are explicitly enabled by // builds have asserts if they are explicitly enabled by
// the release_asserts macro. // the release_asserts macro.

View File

@ -465,6 +465,8 @@ namespace detail {
} }
#endif // TORRENT_ABI_VERSION #endif // TORRENT_ABI_VERSION
TORRENT_EXTRA_EXPORT file_flags_t get_file_attributes(std::string const& p);
TORRENT_EXTRA_EXPORT std::string get_symlink_path(std::string const& p);
} }
#endif #endif

View File

@ -134,6 +134,7 @@ namespace libtorrent {
partfile_read, partfile_read,
partfile_write, partfile_write,
hostname_lookup, hostname_lookup,
symlink,
}; };
// maps an operation id (from peer_error_alert and peer_disconnected_alert) // maps an operation id (from peer_error_alert and peer_disconnected_alert)

View File

@ -894,6 +894,7 @@ namespace {
case o::partfile_read: return -1; case o::partfile_read: return -1;
case o::partfile_write: return -1; case o::partfile_write: return -1;
case o::hostname_lookup: return -1; case o::hostname_lookup: return -1;
case o::symlink: return -1;
}; };
return -1; return -1;
} }
@ -1558,7 +1559,8 @@ namespace {
"partfile_move", "partfile_move",
"partfile_read", "partfile_read",
"partfile_write", "partfile_write",
"hostname_lookup" "hostname_lookup",
"symlink"
}; };
int const idx = static_cast<int>(op); int const idx = static_cast<int>(op);

View File

@ -66,28 +66,6 @@ namespace {
bool ignore_subdir(std::string const& leaf) bool ignore_subdir(std::string const& leaf)
{ return leaf == ".." || leaf == "."; } { return leaf == ".." || leaf == "."; }
file_flags_t get_file_attributes(std::string const& p)
{
auto const path = convert_to_native_path_string(p);
#ifdef TORRENT_WINDOWS
WIN32_FILE_ATTRIBUTE_DATA attr;
GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attr);
if (attr.dwFileAttributes == INVALID_FILE_ATTRIBUTES) return {};
if (attr.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) return file_storage::flag_hidden;
return {};
#else
struct ::stat s;
if (::lstat(path.c_str(), &s) < 0) return {};
file_flags_t file_attr = {};
if (s.st_mode & S_IXUSR)
file_attr |= file_storage::flag_executable;
if (S_ISLNK(s.st_mode))
file_attr |= file_storage::flag_symlink;
return file_attr;
#endif
}
#ifndef TORRENT_WINDOWS #ifndef TORRENT_WINDOWS
std::string get_symlink_path_impl(char const* path) std::string get_symlink_path_impl(char const* path)
{ {
@ -103,16 +81,6 @@ namespace {
} }
#endif #endif
std::string get_symlink_path(std::string const& p)
{
#if defined TORRENT_WINDOWS
TORRENT_UNUSED(p);
return "";
#else
return get_symlink_path_impl(p.c_str());
#endif
}
void add_files_impl(file_storage& fs, std::string const& p void add_files_impl(file_storage& fs, std::string const& p
, std::string const& l, std::function<bool(std::string)> const& pred , std::string const& l, std::function<bool(std::string)> const& pred
, create_flags_t const flags) , create_flags_t const flags)
@ -202,6 +170,39 @@ namespace {
} // anonymous namespace } // anonymous namespace
file_flags_t get_file_attributes(std::string const& p)
{
auto const path = convert_to_native_path_string(p);
#ifdef TORRENT_WINDOWS
WIN32_FILE_ATTRIBUTE_DATA attr;
GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attr);
if (attr.dwFileAttributes == INVALID_FILE_ATTRIBUTES) return {};
if (attr.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) return file_storage::flag_hidden;
return {};
#else
struct ::stat s;
if (::lstat(path.c_str(), &s) < 0) return {};
file_flags_t file_attr = {};
if (s.st_mode & S_IXUSR)
file_attr |= file_storage::flag_executable;
if (S_ISLNK(s.st_mode))
file_attr |= file_storage::flag_symlink;
return file_attr;
#endif
}
std::string get_symlink_path(std::string const& p)
{
#if defined TORRENT_WINDOWS
TORRENT_UNUSED(p);
return "";
#else
return get_symlink_path_impl(p.c_str());
#endif
}
#if TORRENT_ABI_VERSION == 1 #if TORRENT_ABI_VERSION == 1
void add_files(file_storage& fs, std::wstring const& wfile void add_files(file_storage& fs, std::wstring const& wfile

View File

@ -62,6 +62,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <sys/mount.h> #include <sys/mount.h>
#endif #endif
#if TORRENT_HAS_SYMLINK
#include <unistd.h> // for symlink()
#endif
#include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/aux_/disable_warnings_pop.hpp"
#include "libtorrent/storage.hpp" #include "libtorrent/storage.hpp"
@ -294,6 +298,7 @@ namespace libtorrent {
break; break;
} }
// if the file is empty and doesn't already exist, create it // if the file is empty and doesn't already exist, create it
// deliberately don't truncate files that already exist // deliberately don't truncate files that already exist
// if a file is supposed to have size 0, but already exists, we will // if a file is supposed to have size 0, but already exists, we will
@ -301,8 +306,7 @@ namespace libtorrent {
if (fs.file_size(file_index) == 0 if (fs.file_size(file_index) == 0
&& err == boost::system::errc::no_such_file_or_directory) && err == boost::system::errc::no_such_file_or_directory)
{ {
std::string file_path = fs.file_path(file_index, m_save_path); std::string dir = parent_path(fs.file_path(file_index, m_save_path));
std::string dir = parent_path(file_path);
if (dir != last_path) if (dir != last_path)
{ {
@ -317,12 +321,30 @@ namespace libtorrent {
} }
} }
ec.ec.clear(); ec.ec.clear();
// just creating the file is enough to make it zero-sized. If
// there's a race here and some other process truncates the file, #if TORRENT_HAS_SYMLINK
// it's not a problem, we won't access empty files ever again // create symlinks
file_handle f = open_file(file_index, open_mode::read_write if (fs.file_flags(file_index) & file_storage::flag_symlink)
| open_mode::random_access, ec); {
if (ec) return; if (::symlink(fs.symlink(file_index).c_str()
, fs.file_path(file_index, m_save_path).c_str()) != 0)
{
ec.ec = error_code(errno, generic_category());
ec.file(file_index);
ec.operation = operation_t::symlink;
break;
}
}
else
#endif
{
// just creating the file is enough to make it zero-sized. If
// there's a race here and some other process truncates the file,
// it's not a problem, we won't access empty files ever again
file_handle f = open_file(file_index, open_mode::read_write
| open_mode::random_access, ec);
if (ec) return;
}
} }
ec.ec.clear(); ec.ec.clear();
} }

View File

@ -739,3 +739,26 @@ TORRENT_TEST(test_calc_bytes_all_pieces_two_pad)
auto const fs = test_fs(); auto const fs = test_fs();
TEST_EQUAL(calc_bytes(fs, piece_count{fs.num_pieces(), 2, true}), fs.total_size() - 2 * 0x4000); TEST_EQUAL(calc_bytes(fs, piece_count{fs.num_pieces(), 2, true}), fs.total_size() - 2 * 0x4000);
} }
#if TORRENT_HAS_SYMLINK
TORRENT_TEST(symlinks_restore)
{
// downloading test torrent with symlinks
std::string const work_dir = current_working_directory();
lt::add_torrent_params p;
p.ti = std::make_shared<lt::torrent_info>(combine_path(
combine_path(parent_path(work_dir), "test_torrents"), "symlink2.torrent"));
p.flags &= ~lt::torrent_flags::paused;
p.save_path = work_dir;
settings_pack pack = settings();
pack.set_int(libtorrent::settings_pack::alert_mask, libtorrent::alert::status_notification | libtorrent::alert::error_notification);
lt::session ses(std::move(pack));
ses.add_torrent(p);
wait_for_alert(ses, torrent_checked_alert::alert_type, "torrent_checked_alert");
std::string const f = combine_path(combine_path(work_dir, "Some.framework"), "SDL2");
TEST_CHECK(get_file_attributes(f) & file_storage::flag_symlink);
TEST_CHECK(get_symlink_path(f) == "Versions/A/SDL2");
}
#endif

View File

@ -0,0 +1 @@
d7:comment13:Symlinks test4:infod5:filesld4:attr2:xl6:lengthi0e4:pathl4:SDL2e12:symlink pathl8:Versions1:A4:SDL2eed6:lengthi2514e4:pathl8:Versions1:A14:_CodeSignature13:CodeResourceseed6:lengthi1310e4:pathl8:Versions1:A9:Resources10:Info.plisteed6:lengthi0e4:pathl8:Versions1:A4:SDL2eed4:attr2:xl6:lengthi0e4:pathl8:Versions7:Currente12:symlink pathl1:Aeee4:name14:Some.framework12:piece lengthi16384e6:pieces20:¤ÎIÝ¡Ú‹<eõîÌ[דÍee