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
* support magnet link parameters with number siffixes
* 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_write", operation_t::partfile_write)
.value("hostname_lookup", operation_t::hostname_lookup)
.value("symlink", operation_t::symlink)
;
def("operation_name", static_cast<char const*(*)(operation_t)>(&lt::operation_name));

View File

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

View File

@ -465,6 +465,8 @@ namespace detail {
}
#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

View File

@ -134,6 +134,7 @@ namespace libtorrent {
partfile_read,
partfile_write,
hostname_lookup,
symlink,
};
// 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_write: return -1;
case o::hostname_lookup: return -1;
case o::symlink: return -1;
};
return -1;
}
@ -1558,7 +1559,8 @@ namespace {
"partfile_move",
"partfile_read",
"partfile_write",
"hostname_lookup"
"hostname_lookup",
"symlink"
};
int const idx = static_cast<int>(op);

View File

@ -66,28 +66,6 @@ namespace {
bool ignore_subdir(std::string const& 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
std::string get_symlink_path_impl(char const* path)
{
@ -103,16 +81,6 @@ namespace {
}
#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
, std::string const& l, std::function<bool(std::string)> const& pred
, create_flags_t const flags)
@ -202,6 +170,39 @@ 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
void add_files(file_storage& fs, std::wstring const& wfile

View File

@ -62,6 +62,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <sys/mount.h>
#endif
#if TORRENT_HAS_SYMLINK
#include <unistd.h> // for symlink()
#endif
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#include "libtorrent/storage.hpp"
@ -294,6 +298,7 @@ namespace libtorrent {
break;
}
// if the file is empty and doesn't already exist, create it
// deliberately don't truncate files that already exist
// 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
&& 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(file_path);
std::string dir = parent_path(fs.file_path(file_index, m_save_path));
if (dir != last_path)
{
@ -317,12 +321,30 @@ namespace libtorrent {
}
}
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,
// 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;
#if TORRENT_HAS_SYMLINK
// create symlinks
if (fs.file_flags(file_index) & file_storage::flag_symlink)
{
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();
}

View File

@ -739,3 +739,26 @@ TORRENT_TEST(test_calc_bytes_all_pieces_two_pad)
auto const fs = test_fs();
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