added extension for file attributes. Fixes problem when sharing Application bundles on OSX or hidden files on windows

This commit is contained in:
Arvid Norberg 2009-01-11 22:27:43 +00:00
parent de9286a760
commit 43e69cd316
11 changed files with 186 additions and 44 deletions

View File

@ -1,3 +1,4 @@
* added new extension for file attributes (executable and hidden)
* added support for unbuffered I/O for aligned files * added support for unbuffered I/O for aligned files
* added workaround for sparse file issue on Windows Vista * added workaround for sparse file issue on Windows Vista
* added new lt_trackers extension to exchange trackers between * added new lt_trackers extension to exchange trackers between

View File

@ -117,9 +117,16 @@ file structure. Its synopsis::
bool is_valid() const; 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(file_entry const& e);
void add_file(fs::path 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, bool pad_file = false); 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::string const& new_filename);
void rename_file(int index, std::wstring 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); 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 create_torrent
============== ==============

View File

@ -1378,7 +1378,9 @@ iterators with the type ``file_entry``.
size_type offset; size_type offset;
size_type size; size_type size;
size_type file_base; 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 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 not overlap. This is used when mapping "unselected" files into a so-called part
file. file.
``orig_path`` is set to 0 in case the path element is an exact copy of that ``pad_file`` is set to true for files that are not part of the data of the torrent.
found in the metadata. In case the path in the original metadata was They are just there to make sure the next file is aligned to a particular byte offset
incorrectly encoded, and had to be fixed in order to be acceptable utf-8, or piece boundry. These files should typically be hidden from an end user. They are
the original string is preserved in ``orig_path``. The reason to keep it not written to disk.
is to be able to reproduce the info-section exactly, with the correct
info-hash.
num_files() file_at() num_files() file_at()

View File

@ -116,7 +116,12 @@ int main(int argc, char* argv[])
int first = t.map_file(index, 0, 1).piece; int first = t.map_file(index, 0, 1).piece;
int last = t.map_file(index, i->size - 1, 1).piece; int last = t.map_file(index, i->size - 1, 1).piece;
std::cout << " " << std::setw(11) << i->size 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"; << last << " ]\n";
} }

View File

@ -150,6 +150,9 @@ namespace libtorrent
inline void nop(int i) {} 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> template <class Pred, class Str, class PathTraits>
void add_files_impl(file_storage& fs, boost::filesystem::basic_path<Str, PathTraits> const& p 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) , boost::filesystem::basic_path<Str, PathTraits> const& l, Pred pred)
@ -175,7 +178,8 @@ namespace libtorrent
} }
else else
{ {
fs.add_file(l, file_size(f)); int file_flags = get_file_attributes(f);
fs.add_file(l, file_size(f), file_flags);
} }
} }
} }

View File

@ -92,19 +92,23 @@ namespace libtorrent
read_only = GENERIC_READ, read_only = GENERIC_READ,
write_only = GENERIC_WRITE, write_only = GENERIC_WRITE,
read_write = GENERIC_READ | 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 #else
read_only = O_RDONLY, read_only = O_RDONLY,
write_only = O_WRONLY | O_CREAT, write_only = O_WRONLY | O_CREAT,
read_write = O_RDWR | O_CREAT, read_write = O_RDWR | O_CREAT,
rw_mask = O_RDONLY | O_WRONLY | O_RDWR | O_CREAT,
#if defined O_DIRECT #if defined O_DIRECT
no_buffer = O_DIRECT no_buffer = O_DIRECT,
#else #else
no_buffer = O_SYNC no_buffer = O_SYNC,
#endif #endif
attribute_hidden = 0,
attribute_executable = 0x100000,
#endif #endif
rw_mask = read_only | write_only | read_write,
attribute_mask = attribute_hidden | attribute_executable
}; };
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS

View File

@ -56,7 +56,9 @@ namespace libtorrent
struct TORRENT_EXPORT file_entry 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; fs::path path;
size_type offset; // the offset of this file inside the torrent 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. // compressed into a single file, such as a so-called part file.
size_type file_base; size_type file_base;
bool pad_file:1; bool pad_file:1;
bool hidden_attribute:1;
bool executable_attribute:1;
}; };
struct TORRENT_EXPORT file_slice struct TORRENT_EXPORT file_slice
@ -84,9 +88,16 @@ namespace libtorrent
bool is_valid() const { return m_piece_length > 0; } 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(file_entry const& e);
void add_file(fs::path 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, bool pad_file = false); 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::string const& new_filename);
void rename_file(int index, std::wstring const& new_filename); void rename_file(int index, std::wstring const& new_filename);

View File

@ -38,10 +38,53 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/date_time/gregorian/gregorian.hpp> #include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#ifndef TORRENT_WINDOWS
#include <sys/types.h>
#include <sys/stat.h>
#endif
namespace gr = boost::gregorian; namespace gr = boost::gregorian;
namespace libtorrent 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) create_torrent::create_torrent(file_storage& fs, int piece_size, int pad_file_limit)
: m_files(fs) : m_files(fs)
, m_creation_date(pt::second_clock::universal_time()) , m_creation_date(pt::second_clock::universal_time())
@ -61,12 +104,13 @@ namespace libtorrent
const int target_size = 40 * 1024; const int target_size = 40 * 1024;
piece_size = fs.total_size() / (target_size / 20); 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; if (piece_size > i) continue;
piece_size = i;
break; break;
} }
piece_size = i;
} }
// make sure the size is an even power of 2 // make sure the size is an even power of 2
@ -237,6 +281,13 @@ namespace libtorrent
{ {
path_e.list().push_back(entry(*j)); 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';
}
} }
} }
} }

View File

@ -71,7 +71,11 @@ BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
#include "libtorrent/assert.hpp" #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::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 namespace
{ {
@ -146,8 +150,9 @@ namespace libtorrent
, FILE_SHARE_READ | ((mode & no_buffer) ? FILE_SHARE_WRITE : 0) , FILE_SHARE_READ | ((mode & no_buffer) ? FILE_SHARE_WRITE : 0)
, 0 , 0
, ((mode & rw_mask) == read_write || (mode & rw_mask) == write_only)?OPEN_ALWAYS:OPEN_EXISTING , ((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 & no_buffer)?FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING:0)
| ((mode & attribute_mask)?(mode & attribute_mask):FILE_ATTRIBUTE_NORMAL)
, 0); , 0);
if (m_file_handle == INVALID_HANDLE_VALUE) if (m_file_handle == INVALID_HANDLE_VALUE)
@ -170,8 +175,11 @@ namespace libtorrent
| S_IRGRP | S_IWGRP | S_IRGRP | S_IWGRP
| S_IROTH | S_IWOTH; | S_IROTH | S_IWOTH;
if (mode & attribute_executable)
permissions |= S_IXGRP | S_IXOTH | S_IXUSR;
m_fd = ::open(path.external_file_string().c_str() m_fd = ::open(path.external_file_string().c_str()
, mode, permissions); , mode & (rw_mask | no_buffer), permissions);
if (m_fd == -1) if (m_fd == -1)
{ {

View File

@ -145,14 +145,14 @@ namespace libtorrent
return ret; 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; std::string utf8;
wchar_utf8(file.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); TORRENT_ASSERT(size >= 0);
#if BOOST_VERSION < 103600 #if BOOST_VERSION < 103600
@ -174,18 +174,41 @@ namespace libtorrent
m_name = *file.begin(); m_name = *file.begin();
} }
TORRENT_ASSERT(m_name == *file.begin()); TORRENT_ASSERT(m_name == *file.begin());
file_entry e; m_files.push_back(file_entry());
e.pad_file = pad_file; file_entry& e = m_files.back();
m_files.push_back(e); e.size = size;
m_files.back().size = size; e.path = file;
m_files.back().path = file; e.offset = m_total_size;
m_files.back().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; 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) void file_storage::optimize(int pad_file_limit)
@ -214,11 +237,13 @@ namespace libtorrent
{ {
if (pad_file_limit >= 0 if (pad_file_limit >= 0
&& (off & (alignment-1)) != 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 // if we have pad files enabled, and this file is
// not piece-aligned and the file size exceeds the // 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)); int pad_size = alignment - (off & (alignment-1));
// find the largest file that fits in pad_size // find the largest file that fits in pad_size
@ -254,6 +279,7 @@ namespace libtorrent
i->path = *(i+1)->path.begin(); i->path = *(i+1)->path.begin();
i->path /= "_____padding_file_"; i->path /= "_____padding_file_";
i->path /= name; i->path /= name;
i->pad_file = true;
off += pad_size; off += pad_size;
++padding_file; ++padding_file;
++i; ++i;

View File

@ -218,10 +218,23 @@ namespace
return false; return false;
// bitcomet pad file // bitcomet pad file
if (target.path.string().find("_____padding_file_") != std::string::npos) if (target.path.string().find("_____padding_file_") != std::string::npos)
target.pad_file = true; 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; return true;
} }
@ -503,11 +516,7 @@ namespace libtorrent
e.offset = 0; e.offset = 0;
e.size = info.dict_find_int_value("length", -1); e.size = info.dict_find_int_value("length", -1);
// bitcomet pad file // bitcomet pad file
#if BOOST_VERSION < 103600 if (e.path.string().find("_____padding_file_") != std::string::npos)
if (e.path.leaf().substr(0, 18) == "_____padding_file_")
#else
if (e.path.filename().substr(0, 18) == "_____padding_file_")
#endif
e.pad_file = true; e.pad_file = true;
if (e.size < 0) if (e.size < 0)
{ {