add support for creating symlinks, for torrents with symlinks in them
This commit is contained in:
parent
57e56d5070
commit
381d5a3c5d
|
@ -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
|
||||||
|
|
|
@ -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)>(<::operation_name));
|
def("operation_name", static_cast<char const*(*)(operation_t)>(<::operation_name));
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue