added extension for file attributes. Fixes problem when sharing Application bundles on OSX or hidden files on windows
This commit is contained in:
parent
de9286a760
commit
43e69cd316
@ -1,3 +1,4 @@
|
||||
* added new extension for file attributes (executable and hidden)
|
||||
* added support for unbuffered I/O for aligned files
|
||||
* added workaround for sparse file issue on Windows Vista
|
||||
* added new lt_trackers extension to exchange trackers between
|
||||
|
@ -117,9 +117,16 @@ file structure. Its synopsis::
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
enum flags_t
|
||||
{
|
||||
pad_file = 1,
|
||||
attribute_hidden = 2,
|
||||
attribute_executable = 4
|
||||
};
|
||||
|
||||
void add_file(file_entry const& e);
|
||||
void add_file(fs::path const& p, size_type size, bool pad_file = false);
|
||||
void add_file(fs::wpath const& p, size_type size, bool pad_file = false);
|
||||
void add_file(fs::path const& p, size_type size, int flags = 0);
|
||||
void add_file(fs::wpath const& p, size_type size, int flags = 0);
|
||||
void rename_file(int index, std::string const& new_filename);
|
||||
void rename_file(int index, std::wstring const& new_filename);
|
||||
|
||||
@ -152,6 +159,23 @@ file structure. Its synopsis::
|
||||
void swap(file_storage& ti);
|
||||
}
|
||||
|
||||
add_file()
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
void add_file(file_entry const& e);
|
||||
void add_file(fs::path const& p, size_type size, int flags = 0);
|
||||
void add_file(fs::wpath const& p, size_type size, int flags = 0);
|
||||
|
||||
Adds a file to the file storage. The ``flags`` argument sets attributes on the file.
|
||||
The file attributes is an extension and may not work in all bittorrent clients.
|
||||
The possible arreibutes are::
|
||||
|
||||
pad_file
|
||||
attribute_hidden
|
||||
attribute_executable
|
||||
|
||||
|
||||
create_torrent
|
||||
==============
|
||||
|
@ -1378,7 +1378,9 @@ iterators with the type ``file_entry``.
|
||||
size_type offset;
|
||||
size_type size;
|
||||
size_type file_base;
|
||||
boost::shared_ptr<const boost::filesystem::path> orig_path;
|
||||
bool pad_file:1;
|
||||
bool hidden_attribute:1;
|
||||
bool executable_attribute:1;
|
||||
};
|
||||
|
||||
The ``path`` is the full (relative) path of each file. i.e. if it is a multi-file
|
||||
@ -1396,13 +1398,10 @@ the ``file_base`` should be set to an offset so that the different regions do
|
||||
not overlap. This is used when mapping "unselected" files into a so-called part
|
||||
file.
|
||||
|
||||
``orig_path`` is set to 0 in case the path element is an exact copy of that
|
||||
found in the metadata. In case the path in the original metadata was
|
||||
incorrectly encoded, and had to be fixed in order to be acceptable utf-8,
|
||||
the original string is preserved in ``orig_path``. The reason to keep it
|
||||
is to be able to reproduce the info-section exactly, with the correct
|
||||
info-hash.
|
||||
|
||||
``pad_file`` is set to true for files that are not part of the data of the torrent.
|
||||
They are just there to make sure the next file is aligned to a particular byte offset
|
||||
or piece boundry. These files should typically be hidden from an end user. They are
|
||||
not written to disk.
|
||||
|
||||
|
||||
num_files() file_at()
|
||||
|
@ -116,7 +116,12 @@ int main(int argc, char* argv[])
|
||||
int first = t.map_file(index, 0, 1).piece;
|
||||
int last = t.map_file(index, i->size - 1, 1).piece;
|
||||
std::cout << " " << std::setw(11) << i->size
|
||||
<< " " << i->path.string() << "[ " << first << ", "
|
||||
<< " "
|
||||
<< (i->pad_file?'p':'-')
|
||||
<< (i->executable_attribute?'x':'-')
|
||||
<< (i->hidden_attribute?'h':'-')
|
||||
<< " "
|
||||
<< i->path.string() << "[ " << first << ", "
|
||||
<< last << " ]\n";
|
||||
}
|
||||
|
||||
|
@ -150,6 +150,9 @@ namespace libtorrent
|
||||
|
||||
inline void nop(int i) {}
|
||||
|
||||
int get_file_attributes(boost::filesystem::path const& p);
|
||||
int get_file_attributes(boost::filesystem::wpath const& p);
|
||||
|
||||
template <class Pred, class Str, class PathTraits>
|
||||
void add_files_impl(file_storage& fs, boost::filesystem::basic_path<Str, PathTraits> const& p
|
||||
, boost::filesystem::basic_path<Str, PathTraits> const& l, Pred pred)
|
||||
@ -175,7 +178,8 @@ namespace libtorrent
|
||||
}
|
||||
else
|
||||
{
|
||||
fs.add_file(l, file_size(f));
|
||||
int file_flags = get_file_attributes(f);
|
||||
fs.add_file(l, file_size(f), file_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,19 +92,23 @@ namespace libtorrent
|
||||
read_only = GENERIC_READ,
|
||||
write_only = GENERIC_WRITE,
|
||||
read_write = GENERIC_READ | GENERIC_WRITE,
|
||||
rw_mask = GENERIC_READ | GENERIC_WRITE,
|
||||
no_buffer = 1
|
||||
no_buffer = 1,
|
||||
attribute_hidden = FILE_ATTRIBUTE_HIDDEN,
|
||||
attribute_executable = 0,
|
||||
#else
|
||||
read_only = O_RDONLY,
|
||||
write_only = O_WRONLY | O_CREAT,
|
||||
read_write = O_RDWR | O_CREAT,
|
||||
rw_mask = O_RDONLY | O_WRONLY | O_RDWR | O_CREAT,
|
||||
#if defined O_DIRECT
|
||||
no_buffer = O_DIRECT
|
||||
no_buffer = O_DIRECT,
|
||||
#else
|
||||
no_buffer = O_SYNC
|
||||
no_buffer = O_SYNC,
|
||||
#endif
|
||||
attribute_hidden = 0,
|
||||
attribute_executable = 0x100000,
|
||||
#endif
|
||||
rw_mask = read_only | write_only | read_write,
|
||||
attribute_mask = attribute_hidden | attribute_executable
|
||||
};
|
||||
|
||||
#ifdef TORRENT_WINDOWS
|
||||
|
@ -56,7 +56,9 @@ namespace libtorrent
|
||||
|
||||
struct TORRENT_EXPORT file_entry
|
||||
{
|
||||
file_entry(): offset(0), size(0), file_base(0), pad_file(false) {}
|
||||
file_entry(): offset(0), size(0), file_base(0)
|
||||
, pad_file(false), hidden_attribute(false)
|
||||
, executable_attribute(false) {}
|
||||
|
||||
fs::path path;
|
||||
size_type offset; // the offset of this file inside the torrent
|
||||
@ -66,6 +68,8 @@ namespace libtorrent
|
||||
// compressed into a single file, such as a so-called part file.
|
||||
size_type file_base;
|
||||
bool pad_file:1;
|
||||
bool hidden_attribute:1;
|
||||
bool executable_attribute:1;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT file_slice
|
||||
@ -84,9 +88,16 @@ namespace libtorrent
|
||||
|
||||
bool is_valid() const { return m_piece_length > 0; }
|
||||
|
||||
enum flags_t
|
||||
{
|
||||
pad_file = 1,
|
||||
attribute_hidden = 2,
|
||||
attribute_executable = 4
|
||||
};
|
||||
|
||||
void add_file(file_entry const& e);
|
||||
void add_file(fs::path const& p, size_type size, bool pad_file = false);
|
||||
void add_file(fs::wpath const& p, size_type size, bool pad_file = false);
|
||||
void add_file(fs::path const& p, size_type size, int flags = 0);
|
||||
void add_file(fs::wpath const& p, size_type size, int flags = 0);
|
||||
void rename_file(int index, std::string const& new_filename);
|
||||
void rename_file(int index, std::wstring const& new_filename);
|
||||
|
||||
|
@ -38,10 +38,53 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <boost/date_time/gregorian/gregorian.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#ifndef TORRENT_WINDOWS
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
namespace gr = boost::gregorian;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
int TORRENT_EXPORT get_file_attributes(boost::filesystem::path const& p)
|
||||
{
|
||||
#ifdef TORRENT_WINDOWS
|
||||
|
||||
#ifdef TORRENT_USE_WPATH
|
||||
std::wstring path = safe_convert(p.external_file_string());
|
||||
#else
|
||||
std::string path = utf8_native(p.external_file_string());
|
||||
#endif
|
||||
DWORD attr = GetFileAttributes(path.c_str());
|
||||
if (attr & FILE_ATTRIBUTE_HIDDEN) return file_storage::attribute_hidden;
|
||||
return 0;
|
||||
#else
|
||||
struct stat s;
|
||||
if (stat(p.external_file_string().c_str(), &s) < 0) return 0;
|
||||
return (s.st_mode & S_IXUSR) ? file_storage::attribute_executable : 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int TORRENT_EXPORT get_file_attributes(boost::filesystem::wpath const& p)
|
||||
{
|
||||
#ifdef TORRENT_WINDOWS
|
||||
std::wstring const& path = p.external_file_string();
|
||||
DWORD attr = GetFileAttributes(path.c_str());
|
||||
if (attr & FILE_ATTRIBUTE_HIDDEN) return file_storage::attribute_hidden;
|
||||
return 0;
|
||||
#else
|
||||
std::string utf8;
|
||||
wchar_utf8(p.string(), utf8);
|
||||
struct stat s;
|
||||
if (stat(utf8.c_str(), &s) < 0) return 0;
|
||||
return (s.st_mode & S_IXUSR) ? file_storage::attribute_executable : 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
create_torrent::create_torrent(file_storage& fs, int piece_size, int pad_file_limit)
|
||||
: m_files(fs)
|
||||
, m_creation_date(pt::second_clock::universal_time())
|
||||
@ -61,12 +104,13 @@ namespace libtorrent
|
||||
const int target_size = 40 * 1024;
|
||||
piece_size = fs.total_size() / (target_size / 20);
|
||||
|
||||
for (int i = 2*1024*1024; i >= 16*1024; i /= 2)
|
||||
int i = 16*1024;
|
||||
for (; i < 2*1024*1024; i *= 2)
|
||||
{
|
||||
if (piece_size < i) continue;
|
||||
piece_size = i;
|
||||
if (piece_size > i) continue;
|
||||
break;
|
||||
}
|
||||
piece_size = i;
|
||||
}
|
||||
|
||||
// make sure the size is an even power of 2
|
||||
@ -237,6 +281,13 @@ namespace libtorrent
|
||||
{
|
||||
path_e.list().push_back(entry(*j));
|
||||
}
|
||||
if (i->pad_file || i->hidden_attribute || i->executable_attribute)
|
||||
{
|
||||
std::string& attr = file_e["attr"].string();
|
||||
if (i->pad_file) attr += 'p';
|
||||
if (i->hidden_attribute) attr += 'h';
|
||||
if (i->executable_attribute) attr += 'x';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
src/file.cpp
12
src/file.cpp
@ -71,7 +71,11 @@ BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
|
||||
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::no_buffer) == 0);
|
||||
BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::attribute_mask) == 0);
|
||||
BOOST_STATIC_ASSERT((libtorrent::file::no_buffer & libtorrent::file::attribute_mask) == 0);
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -146,8 +150,9 @@ namespace libtorrent
|
||||
, FILE_SHARE_READ | ((mode & no_buffer) ? FILE_SHARE_WRITE : 0)
|
||||
, 0
|
||||
, ((mode & rw_mask) == read_write || (mode & rw_mask) == write_only)?OPEN_ALWAYS:OPEN_EXISTING
|
||||
, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS
|
||||
, FILE_FLAG_RANDOM_ACCESS
|
||||
| ((mode & no_buffer)?FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING:0)
|
||||
| ((mode & attribute_mask)?(mode & attribute_mask):FILE_ATTRIBUTE_NORMAL)
|
||||
, 0);
|
||||
|
||||
if (m_file_handle == INVALID_HANDLE_VALUE)
|
||||
@ -170,8 +175,11 @@ namespace libtorrent
|
||||
| S_IRGRP | S_IWGRP
|
||||
| S_IROTH | S_IWOTH;
|
||||
|
||||
if (mode & attribute_executable)
|
||||
permissions |= S_IXGRP | S_IXOTH | S_IXUSR;
|
||||
|
||||
m_fd = ::open(path.external_file_string().c_str()
|
||||
, mode, permissions);
|
||||
, mode & (rw_mask | no_buffer), permissions);
|
||||
|
||||
if (m_fd == -1)
|
||||
{
|
||||
|
@ -145,14 +145,14 @@ namespace libtorrent
|
||||
return ret;
|
||||
}
|
||||
|
||||
void file_storage::add_file(fs::wpath const& file, size_type size, bool pad_file)
|
||||
void file_storage::add_file(fs::wpath const& file, size_type size, int flags)
|
||||
{
|
||||
std::string utf8;
|
||||
wchar_utf8(file.string(), utf8);
|
||||
add_file(utf8, size, pad_file);
|
||||
add_file(utf8, size, flags);
|
||||
}
|
||||
|
||||
void file_storage::add_file(fs::path const& file, size_type size, bool pad_file)
|
||||
void file_storage::add_file(fs::path const& file, size_type size, int flags)
|
||||
{
|
||||
TORRENT_ASSERT(size >= 0);
|
||||
#if BOOST_VERSION < 103600
|
||||
@ -174,18 +174,41 @@ namespace libtorrent
|
||||
m_name = *file.begin();
|
||||
}
|
||||
TORRENT_ASSERT(m_name == *file.begin());
|
||||
file_entry e;
|
||||
e.pad_file = pad_file;
|
||||
m_files.push_back(e);
|
||||
m_files.back().size = size;
|
||||
m_files.back().path = file;
|
||||
m_files.back().offset = m_total_size;
|
||||
m_files.push_back(file_entry());
|
||||
file_entry& e = m_files.back();
|
||||
e.size = size;
|
||||
e.path = file;
|
||||
e.offset = m_total_size;
|
||||
e.pad_file = bool(flags & pad_file);
|
||||
e.hidden_attribute = bool(flags & attribute_hidden);
|
||||
e.executable_attribute = bool(flags & attribute_executable);
|
||||
m_total_size += size;
|
||||
}
|
||||
|
||||
void file_storage::add_file(file_entry const& e)
|
||||
void file_storage::add_file(file_entry const& ent)
|
||||
{
|
||||
add_file(e.path, e.size, e.pad_file);
|
||||
#if BOOST_VERSION < 103600
|
||||
if (!ent.path.has_branch_path())
|
||||
#else
|
||||
if (!ent.path.has_parent_path())
|
||||
#endif
|
||||
{
|
||||
// you have already added at least one file with a
|
||||
// path to the file (branch_path), which means that
|
||||
// all the other files need to be in the same top
|
||||
// directory as the first file.
|
||||
TORRENT_ASSERT(m_files.empty());
|
||||
m_name = ent.path.string();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_files.empty())
|
||||
m_name = *ent.path.begin();
|
||||
}
|
||||
m_files.push_back(ent);
|
||||
file_entry& e = m_files.back();
|
||||
e.offset = m_total_size;
|
||||
m_total_size += ent.size;
|
||||
}
|
||||
|
||||
void file_storage::optimize(int pad_file_limit)
|
||||
@ -214,11 +237,13 @@ namespace libtorrent
|
||||
{
|
||||
if (pad_file_limit >= 0
|
||||
&& (off & (alignment-1)) != 0
|
||||
&& i->size > pad_file_limit)
|
||||
&& i->size > pad_file_limit
|
||||
&& i->pad_file == false)
|
||||
{
|
||||
// if we have pad files enabled, and this file is
|
||||
// not piece-aligned and the file size exceeds the
|
||||
// limit, so add a padding file in front of it
|
||||
// limit, and it's not a padding file itself.
|
||||
// so add a padding file in front of it
|
||||
int pad_size = alignment - (off & (alignment-1));
|
||||
|
||||
// find the largest file that fits in pad_size
|
||||
@ -254,6 +279,7 @@ namespace libtorrent
|
||||
i->path = *(i+1)->path.begin();
|
||||
i->path /= "_____padding_file_";
|
||||
i->path /= name;
|
||||
i->pad_file = true;
|
||||
off += pad_size;
|
||||
++padding_file;
|
||||
++i;
|
||||
|
@ -218,10 +218,23 @@ namespace
|
||||
return false;
|
||||
|
||||
// bitcomet pad file
|
||||
|
||||
if (target.path.string().find("_____padding_file_") != std::string::npos)
|
||||
target.pad_file = true;
|
||||
|
||||
lazy_entry const* attr = dict.dict_find_string("attr");
|
||||
if (attr)
|
||||
{
|
||||
for (int i = 0; i < attr->string_length(); ++i)
|
||||
{
|
||||
switch (attr->string_ptr()[i])
|
||||
{
|
||||
case 'x': target.executable_attribute = true; break;
|
||||
case 'h': target.hidden_attribute = true; break;
|
||||
case 'p': target.pad_file = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -503,11 +516,7 @@ namespace libtorrent
|
||||
e.offset = 0;
|
||||
e.size = info.dict_find_int_value("length", -1);
|
||||
// bitcomet pad file
|
||||
#if BOOST_VERSION < 103600
|
||||
if (e.path.leaf().substr(0, 18) == "_____padding_file_")
|
||||
#else
|
||||
if (e.path.filename().substr(0, 18) == "_____padding_file_")
|
||||
#endif
|
||||
if (e.path.string().find("_____padding_file_") != std::string::npos)
|
||||
e.pad_file = true;
|
||||
if (e.size < 0)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user