From 8f3723cdef83bd9105dcb3401a9d7efd695ebc39 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 9 Jun 2013 22:30:02 +0000 Subject: [PATCH] support renaming files to absolute paths --- ChangeLog | 1 + docs/manual.rst | 11 +++++++ include/libtorrent/file_storage.hpp | 8 +++-- src/file_storage.cpp | 34 +++++++++++++++------ src/storage.cpp | 46 ++++++++++++++++++----------- test/test_file_storage.cpp | 46 +++++++++++++++++++++++++++-- test/test_storage.cpp | 46 ++++++++++++----------------- 7 files changed, 135 insertions(+), 57 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7aa3771d4..7131ba8c7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * allow moving files to absolute paths, out of the download directory * make move_storage more generic to allow both overwriting files as well as taking existing ones * fix choking issue at high upload rates * optimized rate limiter diff --git a/docs/manual.rst b/docs/manual.rst index f53c9cfe6..28b8bdb74 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1914,6 +1914,12 @@ If you want to rename the base name of the torrent (for a multifile torrent), yo can copy the ``file_storage`` (see `files() orig_files()`_), change the name, and then use `remap_files()`_. +The ``new_filename`` can both be a relative path, in which case the file name +is relative to the ``save_path`` of the torrent. If the ``new_filename`` is +an absolute path (i.e. ``is_complete(new_filename) == true``), then the file +is detached from the ``save_path`` of the torrent. In this case the file is +not moved when move_storage_ is invoked. + begin_files() end_files() rbegin_files() rend_files() ----------------------------------------------------- @@ -2660,6 +2666,11 @@ of the other modes. ``dont_replace`` always takes the existing file in the target directory, if there is one. The source files will still be removed in that case. +Files that have been renamed to have absolute pahts are not moved by this function. +Keep in mind that files that don't belong to the torrent but are stored in the torrent's +directory may be moved as well. This goes for files that have been renamed to +absolute paths that still end up inside the save path. + rename_file() ------------- diff --git a/include/libtorrent/file_storage.hpp b/include/libtorrent/file_storage.hpp index 7288b13d0..2e3d6568a 100644 --- a/include/libtorrent/file_storage.hpp +++ b/include/libtorrent/file_storage.hpp @@ -149,6 +149,10 @@ namespace libtorrent // the full path to this file, concatenate the path // from that array with the 'name' field in // this struct + // if path_index == -2, it means the filename + // in this field contains the full, absolute path + // to the file + // -1 means no path (i.e. single file torrent) int path_index; }; @@ -251,7 +255,7 @@ namespace libtorrent time_t mtime(int index) const; size_type file_base(int index) const; void set_file_base(int index, size_type off); - std::string file_path(int index) const; + std::string file_path(int index, std::string const& save_path = "") const; std::string file_name(int index) const; size_type file_size(int index) const; bool pad_file_at(int index) const; @@ -263,7 +267,7 @@ namespace libtorrent int file_index(internal_file_entry const& fe) const; size_type file_base(internal_file_entry const& fe) const; void set_file_base(internal_file_entry const& fe, size_type off); - std::string file_path(internal_file_entry const& fe) const; + std::string file_path(internal_file_entry const& fe, std::string const& save_path = "") const; std::string file_name(internal_file_entry const& fe) const; size_type file_size(internal_file_entry const& fe) const; bool pad_file_at(internal_file_entry const& fe) const; diff --git a/src/file_storage.cpp b/src/file_storage.cpp index c3622ac94..a2cf924b7 100644 --- a/src/file_storage.cpp +++ b/src/file_storage.cpp @@ -71,7 +71,13 @@ namespace libtorrent void file_storage::update_path_index(internal_file_entry& e) { - std::string parent = parent_path(e.filename()); + std::string fname = e.filename(); + if (is_complete(fname)) + { + e.path_index = -2; + return; + } + std::string parent = parent_path(fname); if (parent.empty()) { e.path_index = -1; @@ -431,13 +437,18 @@ namespace libtorrent return m_file_base[index]; } - std::string file_storage::file_path(int index) const + std::string file_storage::file_path(int index, std::string const& save_path) const { TORRENT_ASSERT(index >= 0 && index < int(m_files.size())); internal_file_entry const& fe = m_files[index]; - TORRENT_ASSERT(fe.path_index >= -1 && fe.path_index < int(m_paths.size())); - if (fe.path_index == -1) return fe.filename(); - return combine_path(m_paths[fe.path_index], fe.filename()); + TORRENT_ASSERT(fe.path_index >= -2 && fe.path_index < int(m_paths.size())); + // -2 means this is an absolute path filename + if (fe.path_index == -2) return fe.filename(); + + // -1 means no path + if (fe.path_index == -1) return combine_path(save_path, fe.filename()); + + return combine_path(save_path, combine_path(m_paths[fe.path_index], fe.filename())); } std::string file_storage::file_name(int index) const @@ -507,11 +518,16 @@ namespace libtorrent return m_file_base[index]; } - std::string file_storage::file_path(internal_file_entry const& fe) const + std::string file_storage::file_path(internal_file_entry const& fe, std::string const& save_path) const { - TORRENT_ASSERT(fe.path_index >= -1 && fe.path_index < int(m_paths.size())); - if (fe.path_index == -1) return fe.filename(); - return combine_path(m_paths[fe.path_index], fe.filename()); + TORRENT_ASSERT(fe.path_index >= -2 && fe.path_index < int(m_paths.size())); + // -2 means this is an absolute path filename + if (fe.path_index == -2) return fe.filename(); + + // -1 means no path + if (fe.path_index == -1) return combine_path(save_path, fe.filename()); + + return combine_path(save_path, combine_path(m_paths[fe.path_index], fe.filename())); } std::string file_storage::file_name(internal_file_entry const& fe) const diff --git a/src/storage.cpp b/src/storage.cpp index 03ae6ee68..12556ac64 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -110,7 +110,7 @@ namespace libtorrent { file_status s; error_code ec; - stat_file(combine_path(save_path, storage.file_path(*i)), &s, ec); + stat_file(storage.file_path(*i, save_path), &s, ec); if (!ec) { @@ -160,7 +160,7 @@ namespace libtorrent file_status s; error_code ec; - stat_file(combine_path(p, fs.file_path(*i)), &s, ec); + stat_file(fs.file_path(*i, p), &s, ec); if (!ec) { @@ -417,7 +417,7 @@ namespace libtorrent // ignore pad files if (file_iter->pad_file) continue; - std::string file_path = combine_path(m_save_path, files().file_path(*file_iter)); + std::string file_path = files().file_path(*file_iter, m_save_path); file_status s; stat_file(file_path, &s, ec); @@ -482,7 +482,7 @@ namespace libtorrent { error_code ec; file_status s; - stat_file(combine_path(m_save_path, files().file_path(*i)), &s, ec); + stat_file(files().file_path(*i, m_save_path), &s, ec); if (ec) continue; if (s.mode & file_status::regular_file && i->size > 0) return true; @@ -493,11 +493,13 @@ namespace libtorrent bool default_storage::rename_file(int index, std::string const& new_filename) { if (index < 0 || index >= files().num_files()) return true; - std::string old_name = combine_path(m_save_path, files().file_path(files().at(index))); + std::string old_name = files().file_path(index, m_save_path); m_pool.release(this, index); error_code ec; - std::string new_path = combine_path(m_save_path, new_filename); + std::string new_path; + if (is_complete(new_filename)) new_path = new_filename; + else new_path = combine_path(m_save_path, new_filename); std::string new_dir = parent_path(new_path); // create any missing directories that the new filename @@ -556,14 +558,18 @@ namespace libtorrent , end(files().end()); i != end; ++i) { std::string fp = files().file_path(*i); - std::string p = combine_path(m_save_path, fp); - std::string bp = parent_path(fp); - std::pair ret; - ret.second = true; - while (ret.second && !bp.empty()) + bool complete = is_complete(fp); + std::string p = complete ? fp : combine_path(m_save_path, fp); + if (!complete) { - ret = directories.insert(combine_path(m_save_path, bp)); - bp = parent_path(bp); + std::string bp = parent_path(fp); + std::pair ret; + ret.second = true; + while (ret.second && !bp.empty()) + { + ret = directories.insert(combine_path(m_save_path, bp)); + bp = parent_path(bp); + } } delete_one_file(p); } @@ -768,7 +774,10 @@ namespace libtorrent for (file_storage::iterator i = f.begin() , end(f.end()); i != end; ++i) { - std::string new_path = combine_path(save_path, f.file_path(*i)); + // files moved out to absolute paths are ignored + if (is_complete(f.file_path(*i))) continue; + + std::string new_path = f.file_path(*i, save_path); stat_file(new_path, &s, ec); if (ec != boost::system::errc::no_such_file_or_directory) return piece_manager::file_exist; @@ -783,6 +792,9 @@ namespace libtorrent for (file_storage::iterator i = f.begin() , end(f.end()); i != end; ++i) { + // files moved out to absolute paths are not moved + if (is_complete(f.file_path(*i))) continue; + std::string split = split_path(f.file_path(*i)); to_move.insert(to_move.begin(), split); } @@ -1199,7 +1211,7 @@ ret: // this means the directory the file is in doesn't exist. // so create it ec.clear(); - std::string path = combine_path(m_save_path, files().file_path(*file_iter)); + std::string path = files().file_path(*file_iter, m_save_path); create_directories(parent_path(path), ec); // if the directory creation failed, don't try to open the file again // but actually just fail @@ -1208,7 +1220,7 @@ ret: if (!file_handle || ec) { - std::string path = combine_path(m_save_path, files().file_path(*file_iter)); + std::string path = files().file_path(*file_iter, m_save_path); TORRENT_ASSERT(ec); set_error(path, ec); return -1; @@ -1253,7 +1265,7 @@ ret: if (ec) { - set_error(combine_path(m_save_path, files().file_path(*file_iter)), ec); + set_error(files().file_path(*file_iter, m_save_path), ec); return -1; } diff --git a/test/test_file_storage.cpp b/test/test_file_storage.cpp index bcd35b5a5..d62dbc9e1 100644 --- a/test/test_file_storage.cpp +++ b/test/test_file_storage.cpp @@ -77,9 +77,51 @@ void setup_test_storage(file_storage& st) int test_main() { { - file_storage a; - setup_test_storage(a); + file_storage st; + setup_test_storage(st); + st.rename_file(0, "test/c/d"); + TEST_EQUAL(st.file_path(0, "./"), "./test/c/d"); + + st.rename_file(0, "/tmp/a"); + TEST_EQUAL(st.file_path(0, "./"), "/tmp/a"); + } + + { + file_storage st; + st.add_file("a", 10000); + + st.rename_file(0, "test/c/d"); + TEST_EQUAL(st.file_path(0, "./"), "./test/c/d"); + + st.rename_file(0, "/tmp/a"); + TEST_EQUAL(st.file_path(0, "./"), "/tmp/a"); + } + + { + file_storage fs; + fs.set_piece_length(512); + fs.add_file("temp_storage/test1.tmp", 17); + fs.add_file("temp_storage/test2.tmp", 612); + fs.add_file("temp_storage/test3.tmp", 0); + fs.add_file("temp_storage/test4.tmp", 0); + fs.add_file("temp_storage/test5.tmp", 3253); + // size: 3882 + fs.add_file("temp_storage/test6.tmp", 841); + // size: 4723 + + peer_request rq = fs.map_file(0, 0, 10); + TEST_EQUAL(rq.piece, 0); + TEST_EQUAL(rq.start, 0); + TEST_EQUAL(rq.length, 10); + rq = fs.map_file(5, 0, 10); + TEST_EQUAL(rq.piece, 7); + TEST_EQUAL(rq.start, 298); + TEST_EQUAL(rq.length, 10); + rq = fs.map_file(5, 0, 1000); + TEST_EQUAL(rq.piece, 7); + TEST_EQUAL(rq.start, 298); + TEST_EQUAL(rq.length, 841); } return 0; diff --git a/test/test_storage.cpp b/test/test_storage.cpp index 7cee02b45..4bab7e7f5 100644 --- a/test/test_storage.cpp +++ b/test/test_storage.cpp @@ -901,6 +901,8 @@ void test_fastresume(std::string const& test_path) boost::intrusive_ptr t = ::create_torrent(&file); file.close(); TEST_CHECK(exists(combine_path(test_path, "tmp1/temporary"))); + if (!exists(combine_path(test_path, "tmp1/temporary"))) + return; entry resume; { @@ -914,23 +916,37 @@ void test_fastresume(std::string const& test_path) p.save_path = combine_path(test_path, "tmp1"); p.storage_mode = storage_mode_compact; torrent_handle h = ses.add_torrent(p, ec); + TEST_CHECK(exists(combine_path(p.save_path, "temporary"))); + if (!exists(combine_path(p.save_path, "temporary"))) + return; - for (int i = 0; i < 10; ++i) + torrent_status s; + for (int i = 0; i < 5; ++i) { print_alerts(ses, "ses"); test_sleep(1000); - torrent_status s = h.status(); + s = h.status(); if (s.progress == 1.0f) { std::cout << "progress: 1.0f" << std::endl; break; } } + + // the whole point of the test is to have a resume + // data which expects the file to exist in full. If + // we failed to do that, we might as well abort + TEST_EQUAL(s.progress, 1.0f); + if (s.progress != 1.0f) + return; + // TODO: 3 don't use this deprecated function resume = h.write_resume_data(); ses.remove_torrent(h, session::delete_files); } TEST_CHECK(!exists(combine_path(test_path, "tmp1/temporary"))); + if (exists(combine_path(test_path, "tmp1/temporary"))) + return; #if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM resume.print(std::cout); #endif @@ -962,6 +978,7 @@ void test_fastresume(std::string const& test_path) assert(a.get()); std::cerr << a->message() << std::endl; } + // we expect the fast resume to be rejected because the files were removed TEST_CHECK(dynamic_cast(a.get()) != 0); } remove_all(combine_path(test_path, "tmp1"), ec); @@ -1094,31 +1111,6 @@ int test_main() std::for_each(test_paths.begin(), test_paths.end(), boost::bind(&run_test, _1, true)); std::for_each(test_paths.begin(), test_paths.end(), boost::bind(&run_test, _1, false)); - file_storage fs; - fs.set_piece_length(512); - fs.add_file("temp_storage/test1.tmp", 17); - fs.add_file("temp_storage/test2.tmp", 612); - fs.add_file("temp_storage/test3.tmp", 0); - fs.add_file("temp_storage/test4.tmp", 0); - fs.add_file("temp_storage/test5.tmp", 3253); - // size: 3882 - fs.add_file("temp_storage/test6.tmp", 841); - // size: 4723 - - peer_request rq = fs.map_file(0, 0, 10); - TEST_EQUAL(rq.piece, 0); - TEST_EQUAL(rq.start, 0); - TEST_EQUAL(rq.length, 10); - rq = fs.map_file(5, 0, 10); - TEST_EQUAL(rq.piece, 7); - TEST_EQUAL(rq.start, 298); - TEST_EQUAL(rq.length, 10); - rq = fs.map_file(5, 0, 1000); - TEST_EQUAL(rq.piece, 7); - TEST_EQUAL(rq.start, 298); - TEST_EQUAL(rq.length, 841); - - return 0; }