diff --git a/docs/manual.rst b/docs/manual.rst index fe8594c20..d24582fd2 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -918,7 +918,7 @@ The ``torrent_info`` has the following synopsis:: typedef std::vector::const_reverse_iterator reverse_file_iterator; - bool remap_files(std::vector > const& map); + bool remap_files(std::vector const& map); file_iterator begin_files(bool storage = false) const; file_iterator end_files(bool storage = false) const; @@ -1052,18 +1052,20 @@ remap_files() :: - bool remap_files(std::vector > const& map); + bool remap_files(std::vector const& map); This call will create a new mapping of the data in this torrent to other files. The ``torrent_info`` maintains 2 views of the file storage. One that is true to the torrent file, and one that represents what is actually saved on disk. This call will change what the files on disk are called. -The each entry in the vector ``map`` is a pair of a (relative) file path and the file's size. +The each entry in the vector ``map`` is a ``file_entry``. The only fields in this struct +that are used in this case are ``path``, ``size`` and ``file_base``. The return value indicates if the remap was successful or not. True means success and false means failure. The sum of all the files passed in through ``map`` has to be exactly -the same as the total_size of the torrent. +the same as the total_size of the torrent. If the number of bytes that are mapped do not +match, false will be returned (this is the only case this function may fail). Changing this mapping for an existing torrent will not move or rename files. If some files should be renamed, this can be done before the torrent is added. @@ -1097,6 +1099,7 @@ remapped, they may differ. For more info, see `remap_files()`_. boost::filesystem::path path; size_type offset; size_type size; + size_type file_base; boost::shared_ptr orig_path; }; @@ -1108,6 +1111,13 @@ The filenames are encoded with UTF-8. of the file within the torrent. i.e. the sum of all the sizes of the files before it in the list. +``file_base`` is the offset in the file where the storage should start. The normal +case is to have this set to 0, so that the storage starts saving data at the start +if the file. In cases where multiple files are mapped into the same file though, +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, diff --git a/include/libtorrent/torrent_info.hpp b/include/libtorrent/torrent_info.hpp index 16eebf234..a064936ff 100755 --- a/include/libtorrent/torrent_info.hpp +++ b/include/libtorrent/torrent_info.hpp @@ -69,9 +69,15 @@ namespace libtorrent struct TORRENT_EXPORT file_entry { + file_entry(): offset(0), size(0), file_base(0) {} + fs::path path; size_type offset; // the offset of this file inside the torrent size_type size; // the size of this file + // the offset in the file where the storage starts. + // This is always 0 unless parts of the torrent is + // compressed into a single file, such as a so-called part file. + size_type file_base; // if the path was incorrectly encoded, this is // the original corrupt encoded string. It is // preserved in order to be able to reproduce @@ -117,7 +123,7 @@ namespace libtorrent void add_file(fs::path file, size_type size); void add_url_seed(std::string const& url); - bool remap_files(std::vector > const& map); + bool remap_files(std::vector const& map); std::vector map_block(int piece, size_type offset , int size, bool storage = false) const; @@ -271,7 +277,7 @@ namespace libtorrent // this vector is typically empty. If it is not // empty, it means the user has re-mapped the - // files in this torrent to diffefrent names + // files in this torrent to different names // on disk. This is only used when reading and // writing the disk. std::vector m_remapped_files; diff --git a/src/storage.cpp b/src/storage.cpp index cf0781e6a..07878e7ee 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -768,10 +768,10 @@ namespace libtorrent TORRENT_ASSERT(file_offset < file_iter->size); - TORRENT_ASSERT(slices[0].offset == file_offset); + TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base); - size_type new_pos = in->seek(file_offset); - if (new_pos != file_offset) + size_type new_pos = in->seek(file_offset + file_iter->file_base); + if (new_pos != file_offset + file_iter->file_base) { // the file was not big enough if (!fill_zero) @@ -782,7 +782,7 @@ namespace libtorrent #ifndef NDEBUG size_type in_tell = in->tell(); - TORRENT_ASSERT(in_tell == file_offset); + TORRENT_ASSERT(in_tell == file_offset + file_iter->file_base); #endif int left_to_read = size; @@ -846,7 +846,7 @@ namespace libtorrent file_offset = 0; in = m_files.open_file( this, path, file::in); - in->seek(0); + in->seek(file_iter->file_base); } } return result; @@ -892,11 +892,11 @@ namespace libtorrent this, p, file::out | file::in); TORRENT_ASSERT(file_offset < file_iter->size); - TORRENT_ASSERT(slices[0].offset == file_offset); + TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base); - size_type pos = out->seek(file_offset); + size_type pos = out->seek(file_offset + file_iter->file_base); - if (pos != file_offset) + if (pos != file_offset + file_iter->file_base) { std::stringstream s; s << "no storage for slot " << slot; @@ -962,7 +962,7 @@ namespace libtorrent out = m_files.open_file( this, p, file::out | file::in); - out->seek(0); + out->seek(file_iter->file_base); } } } diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index 3d30dadd5..b89510f9f 100755 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -165,7 +165,7 @@ namespace { target.size = dict["length"].integer(); target.path = root_dir; - + target.file_base = 0; // prefer the name.utf-8 // because if it exists, it is more @@ -824,20 +824,19 @@ namespace libtorrent m_nodes.push_back(node); } - bool torrent_info::remap_files(std::vector > const& map) + bool torrent_info::remap_files(std::vector const& map) { - typedef std::vector > files_t; - size_type offset = 0; m_remapped_files.resize(map.size()); for (int i = 0; i < int(map.size()); ++i) { file_entry& fe = m_remapped_files[i]; - fe.path = map[i].first; + fe.path = map[i].path; fe.offset = offset; - fe.size = map[i].second; + fe.size = map[i].size; + fe.file_base = map[i].file_base; + fe.orig_path.reset(); offset += fe.size; } if (offset != total_size()) @@ -846,6 +845,26 @@ namespace libtorrent return false; } +#ifndef NDEBUG + std::vector map2(m_remapped_files); + std::sort(map2.begin(), map2.end() + , bind(&file_entry::file_base, _1) < bind(&file_entry::file_base, _2)); + std::stable_sort(map2.begin(), map2.end() + , bind(&file_entry::path, _1) < bind(&file_entry::path, _2)); + fs::path last_path; + size_type last_end = 0; + for (std::vector::iterator i = map2.begin(), end(map2.end()); + i != end; ++i) + { + if (last_path == i->path) + { + assert(last_end <= i->file_base); + } + last_end = i->file_base + i->size; + last_path = i->path; + } +#endif + return true; } @@ -871,7 +890,7 @@ namespace libtorrent { file_slice f; f.file_index = counter; - f.offset = file_offset; + f.offset = file_offset + file_iter->file_base; f.size = (std::min)(file_iter->size - file_offset, (size_type)size); size -= f.size; file_offset += f.size; diff --git a/test/test_storage.cpp b/test/test_storage.cpp index cf7b33f98..982b62d74 100644 --- a/test/test_storage.cpp +++ b/test/test_storage.cpp @@ -192,8 +192,17 @@ void run_test(path const& test_path) // ============================================== // make sure remap_files works - std::vector > map; - map.push_back(std::make_pair(std::string("temp_storage/test.tmp"), 17 + 612 + 1)); + std::vector map; + file_entry fe; + fe.path = "temp_storage/test.tmp"; + fe.size = 17; + fe.file_base = 612 + 1; + map.push_back(fe); + fe.path = "temp_storage/test.tmp"; + fe.size = 612 + 1; + fe.file_base = 0; + map.push_back(fe); + bool ret = info->remap_files(map); TEST_CHECK(ret); @@ -202,7 +211,7 @@ void run_test(path const& test_path) run_storage_tests(info, test_path, storage_mode_compact); std::cerr << file_size(test_path / "temp_storage" / "test.tmp") << std::endl; - TEST_CHECK(file_size(test_path / "temp_storage" / "test.tmp") == 48); + TEST_CHECK(file_size(test_path / "temp_storage" / "test.tmp") == 17 + 612 + 1); remove_all(test_path / "temp_storage");