diff --git a/ChangeLog b/ChangeLog index 62c48a7d1..99dcd3bc5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/docs/make_torrent.rst b/docs/make_torrent.rst index 9fbdc98ba..63f16b446 100644 --- a/docs/make_torrent.rst +++ b/docs/make_torrent.rst @@ -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 ============== diff --git a/docs/manual.rst b/docs/manual.rst index 22ce93a47..9dc824df0 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1378,7 +1378,9 @@ iterators with the type ``file_entry``. size_type offset; size_type size; size_type file_base; - boost::shared_ptr 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() diff --git a/examples/dump_torrent.cpp b/examples/dump_torrent.cpp index 58644b2a9..a7be383e4 100644 --- a/examples/dump_torrent.cpp +++ b/examples/dump_torrent.cpp @@ -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"; } diff --git a/include/libtorrent/create_torrent.hpp b/include/libtorrent/create_torrent.hpp index 58a2155ce..722970e93 100644 --- a/include/libtorrent/create_torrent.hpp +++ b/include/libtorrent/create_torrent.hpp @@ -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 void add_files_impl(file_storage& fs, boost::filesystem::basic_path const& p , boost::filesystem::basic_path 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); } } } diff --git a/include/libtorrent/file.hpp b/include/libtorrent/file.hpp index 9f2a1dc6d..36e6529ce 100644 --- a/include/libtorrent/file.hpp +++ b/include/libtorrent/file.hpp @@ -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 diff --git a/include/libtorrent/file_storage.hpp b/include/libtorrent/file_storage.hpp index 1a4b79dca..6473535e3 100644 --- a/include/libtorrent/file_storage.hpp +++ b/include/libtorrent/file_storage.hpp @@ -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); diff --git a/src/create_torrent.cpp b/src/create_torrent.cpp index f6b1068a1..f74cb1ae7 100644 --- a/src/create_torrent.cpp +++ b/src/create_torrent.cpp @@ -38,10 +38,53 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#ifndef TORRENT_WINDOWS +#include +#include +#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'; + } } } } diff --git a/src/file.cpp b/src/file.cpp index 276625a2f..8fffd442d 100644 --- a/src/file.cpp +++ b/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) { diff --git a/src/file_storage.cpp b/src/file_storage.cpp index 30e7b0656..717800c18 100644 --- a/src/file_storage.cpp +++ b/src/file_storage.cpp @@ -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; diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index d3b4b7b1d..14189eaf3 100644 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -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) {