fixes bug where priorities where lost when force-rechecking.
[libtorrent.git] / src / storage.cpp
blob750ecde1a80c8f496a872fe38cc753f7e9e2f365
1 /*
3 Copyright (c) 2003, Arvid Norberg, Daniel Wallin
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/pch.hpp"
35 #include <ctime>
36 #include <iterator>
37 #include <algorithm>
38 #include <set>
39 #include <functional>
41 #ifdef _MSC_VER
42 #pragma warning(push, 1)
43 #endif
45 #include <boost/lexical_cast.hpp>
46 #include <boost/filesystem/convenience.hpp>
47 #include <boost/filesystem/operations.hpp>
48 #include <boost/filesystem/fstream.hpp>
49 #include <boost/thread/mutex.hpp>
50 #include <boost/ref.hpp>
51 #include <boost/bind.hpp>
52 #include <boost/version.hpp>
53 #include <boost/multi_index_container.hpp>
54 #include <boost/multi_index/member.hpp>
55 #include <boost/multi_index/ordered_index.hpp>
57 #ifdef _MSC_VER
58 #pragma warning(pop)
59 #endif
61 #include "libtorrent/storage.hpp"
62 #include "libtorrent/torrent.hpp"
63 #include "libtorrent/hasher.hpp"
64 #include "libtorrent/session.hpp"
65 #include "libtorrent/peer_id.hpp"
66 #include "libtorrent/file.hpp"
67 #include "libtorrent/invariant_check.hpp"
68 #include "libtorrent/file_pool.hpp"
69 #include "libtorrent/aux_/session_impl.hpp"
70 #include "libtorrent/disk_buffer_holder.hpp"
72 #ifndef NDEBUG
73 #include <ios>
74 #include <iostream>
75 #include <iomanip>
76 #include <cstdio>
77 #endif
79 #if defined(__APPLE__)
80 // for getattrlist()
81 #include <sys/attr.h>
82 #include <unistd.h>
83 // for statfs()
84 #include <sys/param.h>
85 #include <sys/mount.h>
86 #endif
88 #if defined(__linux__)
89 #include <sys/statfs.h>
90 #endif
92 #if defined(__FreeBSD__)
93 // for statfs()
94 #include <sys/param.h>
95 #include <sys/mount.h>
96 #endif
98 #if TORRENT_USE_WPATH
100 #ifdef BOOST_WINDOWS
101 #include <windows.h>
102 #endif
104 #include <boost/filesystem/exception.hpp>
105 #include "libtorrent/utf8.hpp"
106 #include "libtorrent/buffer.hpp"
108 namespace libtorrent
110 std::wstring safe_convert(std::string const& s)
114 return libtorrent::utf8_wchar(s);
116 catch (std::exception)
118 std::wstring ret;
119 const char* end = &s[0] + s.size();
120 for (const char* i = &s[0]; i < end;)
122 wchar_t c = '.';
123 int result = std::mbtowc(&c, i, end - i);
124 if (result > 0) i += result;
125 else ++i;
126 ret += c;
128 return ret;
132 #endif
134 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
135 namespace
137 using libtorrent::safe_convert;
138 using namespace boost::filesystem;
140 // based on code from Boost.Fileystem
141 bool create_directories_win(const fs::path& ph)
143 if (ph.empty() || exists(ph))
145 if ( !ph.empty() && !is_directory(ph) )
146 boost::throw_exception( filesystem_error(
147 "boost::filesystem::create_directories",
148 ph, "path exists and is not a directory",
149 not_directory_error ) );
150 return false;
153 // First create branch, by calling ourself recursively
154 create_directories_win(ph.branch_path());
155 // Now that parent's path exists, create the directory
156 std::wstring wph(safe_convert(ph.native_directory_string()));
157 CreateDirectory(wph.c_str(), 0);
158 return true;
161 bool exists_win( const fs::path & ph )
163 std::wstring wpath(safe_convert(ph.string()));
164 if(::GetFileAttributes( wpath.c_str() ) == 0xFFFFFFFF)
166 UINT err = ::GetLastError();
167 if((err == ERROR_FILE_NOT_FOUND)
168 || (err == ERROR_INVALID_PARAMETER)
169 || (err == ERROR_NOT_READY)
170 || (err == ERROR_PATH_NOT_FOUND)
171 || (err == ERROR_INVALID_NAME)
172 || (err == ERROR_BAD_NETPATH ))
173 return false; // GetFileAttributes failed because the path does not exist
174 // for any other error we assume the file does exist and fall through,
175 // this may not be the best policy though... (JM 20040330)
176 return true;
178 return true;
181 boost::intmax_t file_size_win( const fs::path & ph )
183 std::wstring wpath(safe_convert(ph.string()));
184 // by now, intmax_t is 64-bits on all Windows compilers
185 WIN32_FILE_ATTRIBUTE_DATA fad;
186 if ( !::GetFileAttributesExW( wpath.c_str(),
187 ::GetFileExInfoStandard, &fad ) )
188 boost::throw_exception( filesystem_error(
189 "boost::filesystem::file_size",
190 ph, detail::system_error_code() ) );
191 if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 )
192 boost::throw_exception( filesystem_error(
193 "boost::filesystem::file_size",
194 ph, "invalid: is a directory",
195 is_directory_error ) );
196 return (static_cast<boost::intmax_t>(fad.nFileSizeHigh)
197 << (sizeof(fad.nFileSizeLow)*8))
198 + fad.nFileSizeLow;
201 std::time_t last_write_time_win( const fs::path & ph )
203 struct _stat path_stat;
204 std::wstring wph(safe_convert(ph.native_file_string()));
205 if ( ::_wstat( wph.c_str(), &path_stat ) != 0 )
206 boost::throw_exception( filesystem_error(
207 "boost::filesystem::last_write_time",
208 ph, detail::system_error_code() ) );
209 return path_stat.st_mtime;
212 void rename_win( const fs::path & old_path,
213 const fs::path & new_path )
215 std::wstring wold_path(safe_convert(old_path.string()));
216 std::wstring wnew_path(safe_convert(new_path.string()));
217 if ( !::MoveFile( wold_path.c_str(), wnew_path.c_str() ) )
218 boost::throw_exception( filesystem_error(
219 "boost::filesystem::rename",
220 old_path, new_path, detail::system_error_code() ) );
223 } // anonymous namespace
225 #endif
227 #if BOOST_VERSION < 103200
228 bool operator<(fs::path const& lhs, fs::path const& rhs)
230 return lhs.string() < rhs.string();
232 #endif
234 namespace fs = boost::filesystem;
235 using boost::bind;
236 using namespace ::boost::multi_index;
237 using boost::multi_index::multi_index_container;
239 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
240 namespace
242 using namespace libtorrent;
244 void print_to_log(const std::string& s)
246 static std::ofstream log("log.txt");
247 log << s;
248 log.flush();
251 #endif
253 namespace libtorrent
255 template <class Path>
256 void recursive_copy(Path const& old_path, Path const& new_path, error_code& ec)
258 using boost::filesystem::basic_directory_iterator;
259 #ifndef BOOST_NO_EXCEPTIONS
260 try {
261 #endif
262 TORRENT_ASSERT(!ec);
263 if (is_directory(old_path))
265 create_directory(new_path);
266 for (basic_directory_iterator<Path> i(old_path), end; i != end; ++i)
268 #if BOOST_VERSION < 103600
269 recursive_copy(i->path(), new_path / i->leaf(), ec);
270 #else
271 recursive_copy(i->path(), new_path / i->filename(), ec);
272 #endif
273 if (ec) return;
276 else
278 copy_file(old_path, new_path);
280 #ifndef BOOST_NO_EXCEPTIONS
281 } catch (std::exception& e) { ec = error_code(errno, get_posix_category()); }
282 #endif
285 template <class Path>
286 void recursive_remove(Path const& old_path)
288 using boost::filesystem::basic_directory_iterator;
289 #ifndef BOOST_NO_EXCEPTIONS
290 try {
291 #endif
292 if (is_directory(old_path))
294 for (basic_directory_iterator<Path> i(old_path), end; i != end; ++i)
295 recursive_remove(i->path());
296 remove(old_path);
298 else
300 remove(old_path);
302 #ifndef BOOST_NO_EXCEPTIONS
303 } catch (std::exception& e) {}
304 #endif
306 std::vector<std::pair<size_type, std::time_t> > get_filesizes(
307 file_storage const& s, fs::path p)
309 p = complete(p);
310 std::vector<std::pair<size_type, std::time_t> > sizes;
311 for (file_storage::iterator i = s.begin()
312 , end(s.end());i != end; ++i)
314 size_type size = 0;
315 std::time_t time = 0;
316 #if TORRENT_USE_WPATH
317 fs::wpath f = safe_convert((p / i->path).string());
318 #else
319 fs::path f = p / i->path;
320 #endif
321 #ifndef BOOST_NO_EXCEPTIONS
323 #else
324 if (exists(f))
325 #endif
327 size = file_size(f);
328 time = last_write_time(f);
330 #ifndef BOOST_NO_EXCEPTIONS
331 catch (std::exception&) {}
332 #endif
333 sizes.push_back(std::make_pair(size, time));
335 return sizes;
338 // matches the sizes and timestamps of the files passed in
339 // in non-compact mode, actual file sizes and timestamps
340 // are allowed to be bigger and more recent than the fast
341 // resume data. This is because full allocation will not move
342 // pieces, so any older version of the resume data will
343 // still be a correct subset of the actual data on disk.
344 bool match_filesizes(
345 file_storage const& fs
346 , fs::path p
347 , std::vector<std::pair<size_type, std::time_t> > const& sizes
348 , bool compact_mode
349 , std::string* error)
351 if ((int)sizes.size() != fs.num_files())
353 if (error) *error = "mismatching number of files";
354 return false;
356 p = complete(p);
358 std::vector<std::pair<size_type, std::time_t> >::const_iterator s
359 = sizes.begin();
360 for (file_storage::iterator i = fs.begin()
361 , end(fs.end());i != end; ++i, ++s)
363 size_type size = 0;
364 std::time_t time = 0;
366 #if TORRENT_USE_WPATH
367 fs::wpath f = safe_convert((p / i->path).string());
368 #else
369 fs::path f = p / i->path;
370 #endif
371 #ifndef BOOST_NO_EXCEPTIONS
373 #else
374 if (exists(f))
375 #endif
377 size = file_size(f);
378 time = last_write_time(f);
380 #ifndef BOOST_NO_EXCEPTIONS
381 catch (std::exception&) {}
382 #endif
383 if ((compact_mode && size != s->first)
384 || (!compact_mode && size < s->first))
386 if (error) *error = "filesize mismatch for file '"
387 + i->path.native_file_string()
388 + "', size: " + boost::lexical_cast<std::string>(size)
389 + ", expected to be " + boost::lexical_cast<std::string>(s->first)
390 + " bytes";
391 return false;
393 if ((compact_mode && time != s->second)
394 || (!compact_mode && time < s->second))
396 if (error) *error = "timestamp mismatch for file '"
397 + i->path.native_file_string()
398 + "', modification date: " + boost::lexical_cast<std::string>(time)
399 + ", expected to have modification date "
400 + boost::lexical_cast<std::string>(s->second);
401 return false;
404 return true;
407 class storage : public storage_interface, boost::noncopyable
409 public:
410 storage(file_storage const& fs, fs::path const& path, file_pool& fp)
411 : m_files(fs)
412 , m_pool(fp)
414 TORRENT_ASSERT(m_files.begin() != m_files.end());
415 m_save_path = fs::complete(path);
416 TORRENT_ASSERT(m_save_path.is_complete());
419 bool rename_file(int index, std::string const& new_filename);
420 bool release_files();
421 bool delete_files();
422 bool initialize(bool allocate_files);
423 bool move_storage(fs::path save_path);
424 int read(char* buf, int slot, int offset, int size);
425 int write(const char* buf, int slot, int offset, int size);
426 bool move_slot(int src_slot, int dst_slot);
427 bool swap_slots(int slot1, int slot2);
428 bool swap_slots3(int slot1, int slot2, int slot3);
429 bool verify_resume_data(lazy_entry const& rd, std::string& error);
430 bool write_resume_data(entry& rd) const;
431 sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size);
433 int read_impl(char* buf, int slot, int offset, int size, bool fill_zero);
435 ~storage()
436 { m_pool.release(this); }
438 file_storage const& files() const { return m_mapped_files?*m_mapped_files:m_files; }
440 boost::scoped_ptr<file_storage> m_mapped_files;
441 file_storage const& m_files;
443 std::vector<boost::uint8_t> m_file_priority;
444 fs::path m_save_path;
445 // the file pool is typically stored in
446 // the session, to make all storage
447 // instances use the same pool
448 file_pool& m_pool;
450 // temporary storage for moving pieces
451 buffer m_scratch_buffer;
454 sha1_hash storage::hash_for_slot(int slot, partial_hash& ph, int piece_size)
456 #ifndef NDEBUG
457 hasher partial;
458 hasher whole;
459 int slot_size1 = piece_size;
460 m_scratch_buffer.resize(slot_size1);
461 read_impl(&m_scratch_buffer[0], slot, 0, slot_size1, true);
462 if (ph.offset > 0)
463 partial.update(&m_scratch_buffer[0], ph.offset);
464 whole.update(&m_scratch_buffer[0], slot_size1);
465 hasher partial_copy = ph.h;
466 TORRENT_ASSERT(ph.offset == 0 || partial_copy.final() == partial.final());
467 #endif
468 int slot_size = piece_size - ph.offset;
469 if (slot_size > 0)
471 m_scratch_buffer.resize(slot_size);
472 read_impl(&m_scratch_buffer[0], slot, ph.offset, slot_size, true);
473 ph.h.update(&m_scratch_buffer[0], slot_size);
475 #ifndef NDEBUG
476 sha1_hash ret = ph.h.final();
477 TORRENT_ASSERT(ret == whole.final());
478 return ret;
479 #else
480 return ph.h.final();
481 #endif
484 bool storage::initialize(bool allocate_files)
486 error_code ec;
487 // first, create all missing directories
488 fs::path last_path;
489 for (file_storage::iterator file_iter = files().begin(),
490 end_iter = files().end(); file_iter != end_iter; ++file_iter)
492 fs::path dir = (m_save_path / file_iter->path).branch_path();
494 if (dir != last_path)
497 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
498 last_path = dir;
499 if (!exists_win(last_path))
500 create_directories_win(last_path);
501 #elif TORRENT_USE_WPATH
502 last_path = dir;
503 fs::wpath wp = safe_convert(last_path.string());
504 if (!exists(wp))
505 create_directories(wp);
506 #else
507 last_path = dir;
508 if (!exists(last_path))
509 create_directories(last_path);
510 #endif
513 // if the file is empty, just create it. But also make sure
514 // the directory exists.
515 if (file_iter->size == 0)
517 file(m_save_path / file_iter->path, file::out, ec);
518 if (ec)
520 set_error(m_save_path / file_iter->path, ec);
521 return true;
523 continue;
526 #ifndef BOOST_NO_EXCEPTIONS
527 try {
528 #endif
529 // don't allocate files with priority 0
530 int file_index = file_iter - files().begin();
531 if (allocate_files && (int(m_file_priority.size()) <= file_index
532 || m_file_priority[file_index] > 0))
534 error_code ec;
535 boost::shared_ptr<file> f = m_pool.open_file(this
536 , m_save_path / file_iter->path, file::in | file::out, ec);
537 if (ec) set_error(m_save_path / file_iter->path, ec);
538 else if (f)
540 f->set_size(file_iter->size, ec);
541 if (ec) set_error(m_save_path / file_iter->path, ec);
544 #ifndef BOOST_NO_EXCEPTIONS
546 catch (std::exception& e)
548 set_error(m_save_path / file_iter->path
549 , error_code(errno, get_posix_category()));
550 return true;
552 #endif
554 std::vector<boost::uint8_t>().swap(m_file_priority);
555 // close files that were opened in write mode
556 m_pool.release(this);
557 return false;
560 bool storage::rename_file(int index, std::string const& new_filename)
562 if (index < 0 || index >= m_files.num_files()) return true;
563 fs::path old_name = m_save_path / files().at(index).path;
564 m_pool.release(old_name);
566 #if TORRENT_USE_WPATH
567 fs::wpath old_path = safe_convert(old_name.string());
568 fs::wpath new_path = safe_convert((m_save_path / new_filename).string());
569 #else
570 fs::path const& old_path = old_name;
571 fs::path new_path = m_save_path / new_filename;
572 #endif
574 #ifndef BOOST_NO_EXCEPTIONS
577 #endif
578 rename(old_path, new_path);
580 error_code ec;
581 rename(old_path, new_path, ec);
582 if (ec)
584 set_error(old_path, ec);
585 return;
588 if (!m_mapped_files)
589 { m_mapped_files.reset(new file_storage(m_files)); }
590 m_mapped_files->rename_file(index, new_filename);
591 #ifndef BOOST_NO_EXCEPTIONS
593 catch (std::exception& e)
595 set_error(old_name, error_code(errno, get_posix_category()));
596 return true;
598 #endif
599 return false;
602 bool storage::release_files()
604 m_pool.release(this);
605 buffer().swap(m_scratch_buffer);
606 return false;
609 bool storage::delete_files()
611 // make sure we don't have the files open
612 m_pool.release(this);
613 buffer().swap(m_scratch_buffer);
615 int error = 0;
616 std::string error_file;
618 // delete the files from disk
619 std::set<std::string> directories;
620 typedef std::set<std::string>::iterator iter_t;
621 for (file_storage::iterator i = files().begin()
622 , end(files().end()); i != end; ++i)
624 std::string p = (m_save_path / i->path).string();
625 fs::path bp = i->path.branch_path();
626 std::pair<iter_t, bool> ret;
627 ret.second = true;
628 while (ret.second && !bp.empty())
630 std::pair<iter_t, bool> ret = directories.insert((m_save_path / bp).string());
631 bp = bp.branch_path();
633 #if TORRENT_USE_WPATH
635 { fs::remove(safe_convert(p)); }
636 catch (std::exception& e)
638 error = errno;
639 error_file = p;
641 #else
642 if (std::remove(p.c_str()) != 0 && errno != ENOENT)
644 error = errno;
645 error_file = p;
647 #endif
650 // remove the directories. Reverse order to delete
651 // subdirectories first
653 for (std::set<std::string>::reverse_iterator i = directories.rbegin()
654 , end(directories.rend()); i != end; ++i)
656 #if TORRENT_USE_WPATH
658 { fs::remove(safe_convert(*i)); }
659 catch (std::exception& e)
661 error = errno;
662 error_file = *i;
664 #else
665 if (std::remove(i->c_str()) != 0 && errno != ENOENT)
667 error = errno;
668 error_file = *i;
670 #endif
673 if (error)
675 m_error = error_code(error, get_posix_category());
676 m_error_file.swap(error_file);
677 return true;
679 return false;
682 bool storage::write_resume_data(entry& rd) const
684 TORRENT_ASSERT(rd.type() == entry::dictionary_t);
686 std::vector<std::pair<size_type, std::time_t> > file_sizes
687 = get_filesizes(files(), m_save_path);
689 entry::list_type& fl = rd["file sizes"].list();
690 for (std::vector<std::pair<size_type, std::time_t> >::iterator i
691 = file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
693 entry::list_type p;
694 p.push_back(entry(i->first));
695 p.push_back(entry(i->second));
696 fl.push_back(entry(p));
699 if (m_mapped_files)
701 entry::list_type& fl = rd["mapped_files"].list();
702 for (file_storage::iterator i = m_mapped_files->begin()
703 , end(m_mapped_files->end()); i != end; ++i)
705 fl.push_back(i->path.string());
709 return false;
712 bool storage::verify_resume_data(lazy_entry const& rd, std::string& error)
714 lazy_entry const* file_priority = rd.dict_find_list("file_priority");
715 if (file_priority && file_priority->list_size()
716 == files().num_files())
718 m_file_priority.resize(file_priority->list_size());
719 for (int i = 0; i < file_priority->list_size(); ++i)
720 m_file_priority[i] = file_priority->list_int_value_at(i, 1);
723 lazy_entry const* mapped_files = rd.dict_find_list("mapped_files");
724 if (mapped_files && mapped_files->list_size() == m_files.num_files())
726 if (!m_mapped_files)
727 { m_mapped_files.reset(new file_storage(m_files)); }
728 for (int i = 0; i < m_files.num_files(); ++i)
729 m_mapped_files->rename_file(i, mapped_files->list_string_value_at(i));
732 std::vector<std::pair<size_type, std::time_t> > file_sizes;
733 lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes");
734 if (file_sizes_ent == 0)
736 error = "missing or invalid 'file sizes' entry in resume data";
737 return false;
740 for (int i = 0; i < file_sizes_ent->list_size(); ++i)
742 lazy_entry const* e = file_sizes_ent->list_at(i);
743 if (e->type() != lazy_entry::list_t
744 || e->list_size() != 2
745 || e->list_at(0)->type() != lazy_entry::int_t
746 || e->list_at(1)->type() != lazy_entry::int_t)
747 continue;
748 file_sizes.push_back(std::pair<size_type, std::time_t>(
749 e->list_int_value_at(0), std::time_t(e->list_int_value_at(1))));
752 if (file_sizes.empty())
754 error = "the number of files in resume data is 0";
755 return false;
758 bool seed = false;
760 lazy_entry const* slots = rd.dict_find_list("slots");
761 if (slots)
763 if (int(slots->list_size()) == m_files.num_pieces())
765 seed = true;
766 for (int i = 0; i < slots->list_size(); ++i)
768 lazy_entry const* e = slots->list_at(i);
769 if (e->list_int_value_at(i, -1) >= 0) continue;
770 seed = false;
771 break;
775 else if (lazy_entry const* pieces = rd.dict_find_string("pieces"))
777 if (int(pieces->string_length()) == m_files.num_pieces())
779 seed = true;
780 char const* p = pieces->string_ptr();
781 for (int i = 0; i < pieces->string_length(); ++i)
783 if ((p[i] & 1) == 1) continue;
784 seed = false;
785 break;
789 else
791 error = "missing 'slots' and 'pieces' entry in resume data";
792 return false;
795 bool full_allocation_mode = false;
796 if (rd.dict_find_string_value("allocation") != "compact")
797 full_allocation_mode = true;
799 if (seed)
801 if (files().num_files() != (int)file_sizes.size())
803 error = "the number of files does not match the torrent (num: "
804 + boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
805 + boost::lexical_cast<std::string>(files().num_files()) + ")";
806 return false;
809 std::vector<std::pair<size_type, std::time_t> >::iterator
810 fs = file_sizes.begin();
811 // the resume data says we have the entire torrent
812 // make sure the file sizes are the right ones
813 for (file_storage::iterator i = files().begin()
814 , end(files().end()); i != end; ++i, ++fs)
816 if (i->size != fs->first)
818 error = "file size for '" + i->path.native_file_string()
819 + "' was expected to be "
820 + boost::lexical_cast<std::string>(i->size) + " bytes";
821 return false;
826 return match_filesizes(files(), m_save_path, file_sizes
827 , !full_allocation_mode, &error);
830 // returns true on success
831 bool storage::move_storage(fs::path save_path)
833 #if TORRENT_USE_WPATH
834 fs::wpath old_path;
835 fs::wpath new_path;
836 #else
837 fs::path old_path;
838 fs::path new_path;
839 #endif
841 save_path = complete(save_path);
843 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
844 std::wstring wsave_path(safe_convert(save_path.native_file_string()));
845 if (!exists_win(save_path))
846 CreateDirectory(wsave_path.c_str(), 0);
847 else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0)
848 return false;
849 #elif TORRENT_USE_WPATH
850 fs::wpath wp = safe_convert(save_path.string());
851 if (!exists(wp))
852 create_directory(wp);
853 else if (!is_directory(wp))
854 return false;
855 #else
856 if (!exists(save_path))
857 create_directory(save_path);
858 else if (!is_directory(save_path))
859 return false;
860 #endif
862 m_pool.release(this);
864 #if TORRENT_USE_WPATH
865 old_path = safe_convert((m_save_path / files().name()).string());
866 new_path = safe_convert((save_path / files().name()).string());
867 #else
868 old_path = m_save_path / files().name();
869 new_path = save_path / files().name();
870 #endif
872 #ifndef BOOST_NO_EXCEPTIONS
875 #endif
876 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
877 rename_win(old_path, new_path);
878 #else
879 rename(old_path, new_path);
880 #endif
881 m_save_path = save_path;
882 return true;
883 #ifndef BOOST_NO_EXCEPTIONS
885 catch (std::exception& e)
887 error_code ec;
888 recursive_copy(old_path, new_path, ec);
889 if (ec)
891 set_error(m_save_path / files().name(), ec);
892 return true;
894 m_save_path = save_path;
895 recursive_remove(old_path);
897 #endif
898 return false;
901 #ifndef NDEBUG
903 void storage::shuffle()
905 int num_pieces = files().num_pieces();
907 std::vector<int> pieces(num_pieces);
908 for (std::vector<int>::iterator i = pieces.begin();
909 i != pieces.end(); ++i)
911 *i = static_cast<int>(i - pieces.begin());
913 std::srand((unsigned int)std::time(0));
914 std::vector<int> targets(pieces);
915 std::random_shuffle(pieces.begin(), pieces.end());
916 std::random_shuffle(targets.begin(), targets.end());
918 for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i)
920 const int slot_index = targets[i];
921 const int piece_index = pieces[i];
922 const int slot_size =static_cast<int>(m_files.piece_size(slot_index));
923 std::vector<char> buf(slot_size);
924 read(&buf[0], piece_index, 0, slot_size);
925 write(&buf[0], slot_index, 0, slot_size);
929 #endif
931 bool storage::move_slot(int src_slot, int dst_slot)
933 int piece_size = m_files.piece_size(dst_slot);
934 m_scratch_buffer.resize(piece_size);
935 int ret1 = read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true);
936 int ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
937 return ret1 != piece_size || ret2 != piece_size;
940 bool storage::swap_slots(int slot1, int slot2)
942 // the size of the target slot is the size of the piece
943 int piece_size = m_files.piece_length();
944 int piece1_size = m_files.piece_size(slot2);
945 int piece2_size = m_files.piece_size(slot1);
946 m_scratch_buffer.resize(piece_size * 2);
947 int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
948 int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
949 int ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
950 int ret4 = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size);
951 return ret1 != piece1_size || ret2 != piece2_size
952 || ret3 != piece1_size || ret4 != piece2_size;
955 bool storage::swap_slots3(int slot1, int slot2, int slot3)
957 // the size of the target slot is the size of the piece
958 int piece_size = m_files.piece_length();
959 int piece1_size = m_files.piece_size(slot2);
960 int piece2_size = m_files.piece_size(slot3);
961 int piece3_size = m_files.piece_size(slot1);
962 m_scratch_buffer.resize(piece_size * 2);
963 int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
964 int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
965 int ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
966 int ret4 = read_impl(&m_scratch_buffer[0], slot3, 0, piece3_size, true);
967 int ret5 = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size);
968 int ret6 = write(&m_scratch_buffer[0], slot1, 0, piece3_size);
969 return ret1 != piece1_size || ret2 != piece2_size
970 || ret3 != piece1_size || ret4 != piece3_size
971 || ret5 != piece2_size || ret6 != piece3_size;
974 int storage::read(
975 char* buf
976 , int slot
977 , int offset
978 , int size)
980 return read_impl(buf, slot, offset, size, false);
983 int storage::read_impl(
984 char* buf
985 , int slot
986 , int offset
987 , int size
988 , bool fill_zero)
990 TORRENT_ASSERT(buf != 0);
991 TORRENT_ASSERT(slot >= 0 && slot < m_files.num_pieces());
992 TORRENT_ASSERT(offset >= 0);
993 TORRENT_ASSERT(offset < m_files.piece_size(slot));
994 TORRENT_ASSERT(size > 0);
996 #ifndef NDEBUG
997 std::vector<file_slice> slices
998 = files().map_block(slot, offset, size);
999 TORRENT_ASSERT(!slices.empty());
1000 #endif
1002 size_type start = slot * (size_type)m_files.piece_length() + offset;
1003 TORRENT_ASSERT(start + size <= m_files.total_size());
1005 // find the file iterator and file offset
1006 size_type file_offset = start;
1007 std::vector<file_entry>::const_iterator file_iter;
1009 for (file_iter = files().begin();;)
1011 if (file_offset < file_iter->size)
1012 break;
1014 file_offset -= file_iter->size;
1015 ++file_iter;
1018 int buf_pos = 0;
1019 error_code ec;
1020 boost::shared_ptr<file> in(m_pool.open_file(
1021 this, m_save_path / file_iter->path, file::in, ec));
1022 if (!in || ec)
1024 set_error(m_save_path / file_iter->path, ec);
1025 return -1;
1027 TORRENT_ASSERT(file_offset < file_iter->size);
1028 TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base);
1030 size_type new_pos = in->seek(file_offset + file_iter->file_base, file::begin, ec);
1031 if (new_pos != file_offset + file_iter->file_base || ec)
1033 // the file was not big enough
1034 if (!fill_zero)
1036 set_error(m_save_path / file_iter->path, ec);
1037 return -1;
1039 std::memset(buf + buf_pos, 0, size - buf_pos);
1040 return size;
1043 #ifndef NDEBUG
1044 size_type in_tell = in->tell(ec);
1045 TORRENT_ASSERT(in_tell == file_offset + file_iter->file_base && !ec);
1046 #endif
1048 int left_to_read = size;
1049 int slot_size = static_cast<int>(m_files.piece_size(slot));
1051 if (offset + left_to_read > slot_size)
1052 left_to_read = slot_size - offset;
1054 TORRENT_ASSERT(left_to_read >= 0);
1056 size_type result = left_to_read;
1058 #ifndef NDEBUG
1059 int counter = 0;
1060 #endif
1062 while (left_to_read > 0)
1064 int read_bytes = left_to_read;
1065 if (file_offset + read_bytes > file_iter->size)
1066 read_bytes = static_cast<int>(file_iter->size - file_offset);
1068 if (read_bytes > 0)
1070 #ifndef NDEBUG
1071 TORRENT_ASSERT(int(slices.size()) > counter);
1072 size_type slice_size = slices[counter].size;
1073 TORRENT_ASSERT(slice_size == read_bytes);
1074 TORRENT_ASSERT(files().at(slices[counter].file_index).path
1075 == file_iter->path);
1076 #endif
1078 int actual_read = int(in->read(buf + buf_pos, read_bytes, ec));
1080 if (read_bytes != actual_read || ec)
1082 // the file was not big enough
1083 if (actual_read > 0) buf_pos += actual_read;
1084 if (!fill_zero)
1086 set_error(m_save_path / file_iter->path, ec);
1087 return -1;
1089 std::memset(buf + buf_pos, 0, size - buf_pos);
1090 return size;
1093 left_to_read -= read_bytes;
1094 buf_pos += read_bytes;
1095 TORRENT_ASSERT(buf_pos >= 0);
1096 file_offset += read_bytes;
1099 if (left_to_read > 0)
1101 ++file_iter;
1102 #ifndef NDEBUG
1103 // empty files are not returned by map_block, so if
1104 // this file was empty, don't increment the slice counter
1105 if (read_bytes > 0) ++counter;
1106 #endif
1107 fs::path path = m_save_path / file_iter->path;
1109 file_offset = 0;
1110 error_code ec;
1111 in = m_pool.open_file( this, path, file::in, ec);
1112 if (!in || ec)
1114 set_error(path, ec);
1115 return -1;
1117 size_type pos = in->seek(file_iter->file_base, file::begin, ec);
1118 if (pos != file_iter->file_base || ec)
1120 if (!fill_zero)
1122 set_error(m_save_path / file_iter->path, ec);
1123 return -1;
1125 std::memset(buf + buf_pos, 0, size - buf_pos);
1126 return size;
1130 return result;
1133 int storage::write(
1134 const char* buf
1135 , int slot
1136 , int offset
1137 , int size)
1139 TORRENT_ASSERT(buf != 0);
1140 TORRENT_ASSERT(slot >= 0);
1141 TORRENT_ASSERT(slot < m_files.num_pieces());
1142 TORRENT_ASSERT(offset >= 0);
1143 TORRENT_ASSERT(size > 0);
1145 #ifndef NDEBUG
1146 std::vector<file_slice> slices
1147 = files().map_block(slot, offset, size);
1148 TORRENT_ASSERT(!slices.empty());
1149 #endif
1151 size_type start = slot * (size_type)m_files.piece_length() + offset;
1153 // find the file iterator and file offset
1154 size_type file_offset = start;
1155 std::vector<file_entry>::const_iterator file_iter;
1157 for (file_iter = files().begin();;)
1159 if (file_offset < file_iter->size)
1160 break;
1162 file_offset -= file_iter->size;
1163 ++file_iter;
1164 TORRENT_ASSERT(file_iter != files().end());
1167 fs::path p(m_save_path / file_iter->path);
1168 error_code ec;
1169 boost::shared_ptr<file> out = m_pool.open_file(
1170 this, p, file::out | file::in, ec);
1172 if (!out || ec)
1174 set_error(p, ec);
1175 return -1;
1177 TORRENT_ASSERT(file_offset < file_iter->size);
1178 TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base);
1180 size_type pos = out->seek(file_offset + file_iter->file_base, file::begin, ec);
1182 if (pos != file_offset + file_iter->file_base || ec)
1184 set_error(p, ec);
1185 return -1;
1188 int left_to_write = size;
1189 int slot_size = static_cast<int>(m_files.piece_size(slot));
1191 if (offset + left_to_write > slot_size)
1192 left_to_write = slot_size - offset;
1194 TORRENT_ASSERT(left_to_write >= 0);
1196 int buf_pos = 0;
1197 #ifndef NDEBUG
1198 int counter = 0;
1199 #endif
1200 while (left_to_write > 0)
1202 int write_bytes = left_to_write;
1203 if (file_offset + write_bytes > file_iter->size)
1205 TORRENT_ASSERT(file_iter->size >= file_offset);
1206 write_bytes = static_cast<int>(file_iter->size - file_offset);
1209 if (write_bytes > 0)
1211 TORRENT_ASSERT(int(slices.size()) > counter);
1212 TORRENT_ASSERT(slices[counter].size == write_bytes);
1213 TORRENT_ASSERT(files().at(slices[counter].file_index).path
1214 == file_iter->path);
1216 TORRENT_ASSERT(buf_pos >= 0);
1217 TORRENT_ASSERT(write_bytes >= 0);
1218 error_code ec;
1219 size_type written = out->write(buf + buf_pos, write_bytes, ec);
1221 if (written != write_bytes || ec)
1223 set_error(m_save_path / file_iter->path, ec);
1224 return -1;
1227 left_to_write -= write_bytes;
1228 buf_pos += write_bytes;
1229 TORRENT_ASSERT(buf_pos >= 0);
1230 file_offset += write_bytes;
1231 TORRENT_ASSERT(file_offset <= file_iter->size);
1234 if (left_to_write > 0)
1236 #ifndef NDEBUG
1237 if (write_bytes > 0) ++counter;
1238 #endif
1239 ++file_iter;
1241 TORRENT_ASSERT(file_iter != files().end());
1242 fs::path p = m_save_path / file_iter->path;
1243 file_offset = 0;
1244 error_code ec;
1245 out = m_pool.open_file(
1246 this, p, file::out | file::in, ec);
1248 if (!out || ec)
1250 set_error(p, ec);
1251 return -1;
1254 size_type pos = out->seek(file_iter->file_base, file::begin, ec);
1256 if (pos != file_iter->file_base || ec)
1258 set_error(p, ec);
1259 return -1;
1263 return size;
1266 storage_interface* default_storage_constructor(file_storage const& fs
1267 , fs::path const& path, file_pool& fp)
1269 return new storage(fs, path, fp);
1272 // -- piece_manager -----------------------------------------------------
1274 piece_manager::piece_manager(
1275 boost::shared_ptr<void> const& torrent
1276 , boost::intrusive_ptr<torrent_info const> info
1277 , fs::path const& save_path
1278 , file_pool& fp
1279 , disk_io_thread& io
1280 , storage_constructor_type sc
1281 , storage_mode_t sm)
1282 : m_info(info)
1283 , m_files(m_info->files())
1284 , m_storage(sc(m_files, save_path, fp))
1285 , m_storage_mode(sm)
1286 , m_save_path(complete(save_path))
1287 , m_state(state_none)
1288 , m_current_slot(0)
1289 , m_out_of_place(false)
1290 , m_scratch_piece(-1)
1291 , m_storage_constructor(sc)
1292 , m_io_thread(io)
1293 , m_torrent(torrent)
1297 piece_manager::~piece_manager()
1301 void piece_manager::async_save_resume_data(
1302 boost::function<void(int, disk_io_job const&)> const& handler)
1304 disk_io_job j;
1305 j.storage = this;
1306 j.action = disk_io_job::save_resume_data;
1307 m_io_thread.add_job(j, handler);
1310 void piece_manager::async_clear_read_cache(
1311 boost::function<void(int, disk_io_job const&)> const& handler)
1313 disk_io_job j;
1314 j.storage = this;
1315 j.action = disk_io_job::clear_read_cache;
1316 m_io_thread.add_job(j, handler);
1319 void piece_manager::async_release_files(
1320 boost::function<void(int, disk_io_job const&)> const& handler)
1322 disk_io_job j;
1323 j.storage = this;
1324 j.action = disk_io_job::release_files;
1325 m_io_thread.add_job(j, handler);
1328 void piece_manager::async_delete_files(
1329 boost::function<void(int, disk_io_job const&)> const& handler)
1331 disk_io_job j;
1332 j.storage = this;
1333 j.action = disk_io_job::delete_files;
1334 m_io_thread.add_job(j, handler);
1337 void piece_manager::async_move_storage(fs::path const& p
1338 , boost::function<void(int, disk_io_job const&)> const& handler)
1340 disk_io_job j;
1341 j.storage = this;
1342 j.action = disk_io_job::move_storage;
1343 j.str = p.string();
1344 m_io_thread.add_job(j, handler);
1347 void piece_manager::async_check_fastresume(lazy_entry const* resume_data
1348 , boost::function<void(int, disk_io_job const&)> const& handler)
1350 TORRENT_ASSERT(resume_data != 0);
1351 disk_io_job j;
1352 j.storage = this;
1353 j.action = disk_io_job::check_fastresume;
1354 j.buffer = (char*)resume_data;
1355 m_io_thread.add_job(j, handler);
1358 void piece_manager::async_rename_file(int index, std::string const& name
1359 , boost::function<void(int, disk_io_job const&)> const& handler)
1361 disk_io_job j;
1362 j.storage = this;
1363 j.piece = index;
1364 j.str = name;
1365 j.action = disk_io_job::rename_file;
1366 m_io_thread.add_job(j, handler);
1369 void piece_manager::async_check_files(
1370 boost::function<void(int, disk_io_job const&)> const& handler)
1372 disk_io_job j;
1373 j.storage = this;
1374 j.action = disk_io_job::check_files;
1375 m_io_thread.add_job(j, handler);
1378 void piece_manager::async_read(
1379 peer_request const& r
1380 , boost::function<void(int, disk_io_job const&)> const& handler
1381 , int priority)
1383 disk_io_job j;
1384 j.storage = this;
1385 j.action = disk_io_job::read;
1386 j.piece = r.piece;
1387 j.offset = r.start;
1388 j.buffer_size = r.length;
1389 j.buffer = 0;
1390 j.priority = priority;
1391 // if a buffer is not specified, only one block can be read
1392 // since that is the size of the pool allocator's buffers
1393 TORRENT_ASSERT(r.length <= 16 * 1024);
1394 m_io_thread.add_job(j, handler);
1395 #ifndef NDEBUG
1396 boost::recursive_mutex::scoped_lock l(m_mutex);
1397 // if this assert is hit, it suggests
1398 // that check_files was not successful
1399 TORRENT_ASSERT(slot_for(r.piece) >= 0);
1400 #endif
1403 void piece_manager::async_write(
1404 peer_request const& r
1405 , disk_buffer_holder& buffer
1406 , boost::function<void(int, disk_io_job const&)> const& handler)
1408 TORRENT_ASSERT(r.length <= 16 * 1024);
1409 // the buffer needs to be allocated through the io_thread
1410 TORRENT_ASSERT(m_io_thread.is_disk_buffer(buffer.get()));
1412 disk_io_job j;
1413 j.storage = this;
1414 j.action = disk_io_job::write;
1415 j.piece = r.piece;
1416 j.offset = r.start;
1417 j.buffer_size = r.length;
1418 j.buffer = buffer.get();
1419 m_io_thread.add_job(j, handler);
1420 buffer.release();
1423 void piece_manager::async_hash(int piece
1424 , boost::function<void(int, disk_io_job const&)> const& handler)
1426 disk_io_job j;
1427 j.storage = this;
1428 j.action = disk_io_job::hash;
1429 j.piece = piece;
1431 m_io_thread.add_job(j, handler);
1434 fs::path piece_manager::save_path() const
1436 boost::recursive_mutex::scoped_lock l(m_mutex);
1437 return m_save_path;
1440 sha1_hash piece_manager::hash_for_piece_impl(int piece)
1442 partial_hash ph;
1444 std::map<int, partial_hash>::iterator i = m_piece_hasher.find(piece);
1445 if (i != m_piece_hasher.end())
1447 ph = i->second;
1448 m_piece_hasher.erase(i);
1451 int slot = slot_for(piece);
1452 TORRENT_ASSERT(slot != has_no_slot);
1453 return m_storage->hash_for_slot(slot, ph, m_files.piece_size(piece));
1456 int piece_manager::move_storage_impl(fs::path const& save_path)
1458 if (m_storage->move_storage(save_path))
1460 m_save_path = fs::complete(save_path);
1461 return 0;
1463 return -1;
1466 void piece_manager::write_resume_data(entry& rd) const
1468 boost::recursive_mutex::scoped_lock lock(m_mutex);
1470 INVARIANT_CHECK;
1472 m_storage->write_resume_data(rd);
1474 if (m_storage_mode == storage_mode_compact)
1476 entry::list_type& slots = rd["slots"].list();
1477 slots.clear();
1478 std::vector<int>::const_reverse_iterator last;
1479 for (last = m_slot_to_piece.rbegin();
1480 last != m_slot_to_piece.rend(); ++last)
1482 if (*last != unallocated) break;
1485 for (std::vector<int>::const_iterator i =
1486 m_slot_to_piece.begin();
1487 i != last.base(); ++i)
1489 slots.push_back((*i >= 0) ? *i : unassigned);
1493 rd["allocation"] = m_storage_mode == storage_mode_sparse?"sparse"
1494 :m_storage_mode == storage_mode_allocate?"full":"compact";
1497 void piece_manager::mark_failed(int piece_index)
1499 INVARIANT_CHECK;
1501 if (m_storage_mode != storage_mode_compact) return;
1503 TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
1504 int slot_index = m_piece_to_slot[piece_index];
1505 TORRENT_ASSERT(slot_index >= 0);
1507 m_slot_to_piece[slot_index] = unassigned;
1508 m_piece_to_slot[piece_index] = has_no_slot;
1509 m_free_slots.push_back(slot_index);
1512 int piece_manager::read_impl(
1513 char* buf
1514 , int piece_index
1515 , int offset
1516 , int size)
1518 TORRENT_ASSERT(buf);
1519 TORRENT_ASSERT(offset >= 0);
1520 TORRENT_ASSERT(size > 0);
1521 int slot = slot_for(piece_index);
1522 return m_storage->read(buf, slot, offset, size);
1525 int piece_manager::write_impl(
1526 const char* buf
1527 , int piece_index
1528 , int offset
1529 , int size)
1531 TORRENT_ASSERT(buf);
1532 TORRENT_ASSERT(offset >= 0);
1533 TORRENT_ASSERT(size > 0);
1534 TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces());
1536 int slot = allocate_slot_for_piece(piece_index);
1537 int ret = m_storage->write(buf, slot, offset, size);
1538 // only save the partial hash if the write succeeds
1539 if (ret != size) return ret;
1541 // std::ofstream out("partial_hash.log", std::ios::app);
1543 if (offset == 0)
1545 partial_hash& ph = m_piece_hasher[piece_index];
1546 TORRENT_ASSERT(ph.offset == 0);
1547 ph.offset = size;
1548 ph.h.update(buf, size);
1550 out << time_now_string() << " NEW ["
1551 " s: " << this
1552 << " p: " << piece_index
1553 << " off: " << offset
1554 << " size: " << size
1555 << " entries: " << m_piece_hasher.size()
1556 << " ]" << std::endl;
1559 else
1561 std::map<int, partial_hash>::iterator i = m_piece_hasher.find(piece_index);
1562 if (i != m_piece_hasher.end())
1564 #ifndef NDEBUG
1565 TORRENT_ASSERT(i->second.offset > 0);
1566 int hash_offset = i->second.offset;
1567 TORRENT_ASSERT(offset >= hash_offset);
1568 #endif
1569 if (offset == i->second.offset)
1572 out << time_now_string() << " UPDATING ["
1573 " s: " << this
1574 << " p: " << piece_index
1575 << " off: " << offset
1576 << " size: " << size
1577 << " entries: " << m_piece_hasher.size()
1578 << " ]" << std::endl;
1580 i->second.offset += size;
1581 i->second.h.update(buf, size);
1583 /* else
1585 out << time_now_string() << " SKIPPING (out of order) ["
1586 " s: " << this
1587 << " p: " << piece_index
1588 << " off: " << offset
1589 << " size: " << size
1590 << " entries: " << m_piece_hasher.size()
1591 << " ]" << std::endl;
1593 */ }
1594 /* else
1596 out << time_now_string() << " SKIPPING (no entry) ["
1597 " s: " << this
1598 << " p: " << piece_index
1599 << " off: " << offset
1600 << " size: " << size
1601 << " entries: " << m_piece_hasher.size()
1602 << " ]" << std::endl;
1607 return ret;
1610 int piece_manager::identify_data(
1611 const std::vector<char>& piece_data
1612 , int current_slot)
1614 // INVARIANT_CHECK;
1616 const int piece_size = static_cast<int>(m_files.piece_length());
1617 const int last_piece_size = static_cast<int>(m_files.piece_size(
1618 m_files.num_pieces() - 1));
1620 TORRENT_ASSERT((int)piece_data.size() >= last_piece_size);
1622 // calculate a small digest, with the same
1623 // size as the last piece. And a large digest
1624 // which has the same size as a normal piece
1625 hasher small_digest;
1626 small_digest.update(&piece_data[0], last_piece_size);
1627 hasher large_digest(small_digest);
1628 TORRENT_ASSERT(piece_size - last_piece_size >= 0);
1629 if (piece_size - last_piece_size > 0)
1631 large_digest.update(
1632 &piece_data[last_piece_size]
1633 , piece_size - last_piece_size);
1635 sha1_hash large_hash = large_digest.final();
1636 sha1_hash small_hash = small_digest.final();
1638 typedef std::multimap<sha1_hash, int>::const_iterator map_iter;
1639 map_iter begin1;
1640 map_iter end1;
1641 map_iter begin2;
1642 map_iter end2;
1644 // makes the lookups for the small digest and the large digest
1645 boost::tie(begin1, end1) = m_hash_to_piece.equal_range(small_hash);
1646 boost::tie(begin2, end2) = m_hash_to_piece.equal_range(large_hash);
1648 // copy all potential piece indices into this vector
1649 std::vector<int> matching_pieces;
1650 for (map_iter i = begin1; i != end1; ++i)
1651 matching_pieces.push_back(i->second);
1652 for (map_iter i = begin2; i != end2; ++i)
1653 matching_pieces.push_back(i->second);
1655 // no piece matched the data in the slot
1656 if (matching_pieces.empty())
1657 return unassigned;
1659 // ------------------------------------------
1660 // CHECK IF THE PIECE IS IN ITS CORRECT PLACE
1661 // ------------------------------------------
1663 if (std::find(
1664 matching_pieces.begin()
1665 , matching_pieces.end()
1666 , current_slot) != matching_pieces.end())
1668 // the current slot is among the matching pieces, so
1669 // we will assume that the piece is in the right place
1670 const int piece_index = current_slot;
1672 int other_slot = m_piece_to_slot[piece_index];
1673 if (other_slot >= 0)
1675 // we have already found a piece with
1676 // this index.
1678 // take one of the other matching pieces
1679 // that hasn't already been assigned
1680 int other_piece = -1;
1681 for (std::vector<int>::iterator i = matching_pieces.begin();
1682 i != matching_pieces.end(); ++i)
1684 if (m_piece_to_slot[*i] >= 0 || *i == piece_index) continue;
1685 other_piece = *i;
1686 break;
1688 if (other_piece >= 0)
1690 // replace the old slot with 'other_piece'
1691 m_slot_to_piece[other_slot] = other_piece;
1692 m_piece_to_slot[other_piece] = other_slot;
1694 else
1696 // this index is the only piece with this
1697 // hash. The previous slot we found with
1698 // this hash must be the same piece. Mark
1699 // that piece as unassigned, since this slot
1700 // is the correct place for the piece.
1701 m_slot_to_piece[other_slot] = unassigned;
1702 if (m_storage_mode == storage_mode_compact)
1703 m_free_slots.push_back(other_slot);
1705 TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot);
1706 TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0);
1707 m_piece_to_slot[piece_index] = has_no_slot;
1710 TORRENT_ASSERT(m_piece_to_slot[piece_index] == has_no_slot);
1712 return piece_index;
1715 // find a matching piece that hasn't
1716 // already been assigned
1717 int free_piece = unassigned;
1718 for (std::vector<int>::iterator i = matching_pieces.begin();
1719 i != matching_pieces.end(); ++i)
1721 if (m_piece_to_slot[*i] >= 0) continue;
1722 free_piece = *i;
1723 break;
1726 if (free_piece >= 0)
1728 TORRENT_ASSERT(m_piece_to_slot[free_piece] == has_no_slot);
1729 return free_piece;
1731 else
1733 TORRENT_ASSERT(free_piece == unassigned);
1734 return unassigned;
1738 int piece_manager::check_no_fastresume(std::string& error)
1740 file_storage::iterator i = m_files.begin();
1741 file_storage::iterator end = m_files.end();
1743 for (; i != end; ++i)
1745 bool file_exists = false;
1746 fs::path f = m_save_path / i->path;
1747 #ifndef BOOST_NO_EXCEPTIONS
1750 #endif
1751 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
1752 file_exists = exists_win(f);
1753 #elif TORRENT_USE_WPATH
1754 fs::wpath wf = safe_convert(f.string());
1755 file_exists = exists(wf);
1756 #else
1757 file_exists = exists(f);
1758 #endif
1759 #ifndef BOOST_NO_EXCEPTIONS
1761 catch (std::exception& e)
1763 error = f.string();
1764 error += ": ";
1765 error += e.what();
1766 TORRENT_ASSERT(!error.empty());
1767 return fatal_disk_error;
1769 #endif
1770 if (file_exists && i->size > 0)
1772 m_state = state_full_check;
1773 m_piece_to_slot.clear();
1774 m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
1775 m_slot_to_piece.clear();
1776 m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
1777 if (m_storage_mode == storage_mode_compact)
1779 m_unallocated_slots.clear();
1780 m_free_slots.clear();
1782 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
1783 return need_full_check;
1787 if (m_storage_mode == storage_mode_compact)
1789 // in compact mode without checking, we need to
1790 // populate the unallocated list
1791 TORRENT_ASSERT(m_unallocated_slots.empty());
1792 for (int i = 0, end(m_files.num_pieces()); i < end; ++i)
1793 m_unallocated_slots.push_back(i);
1794 m_piece_to_slot.clear();
1795 m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
1796 m_slot_to_piece.clear();
1797 m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
1800 return check_init_storage(error);
1803 int piece_manager::check_init_storage(std::string& error)
1805 if (m_storage->initialize(m_storage_mode == storage_mode_allocate))
1807 error = m_storage->error().message();
1808 TORRENT_ASSERT(!error.empty());
1809 return fatal_disk_error;
1811 m_state = state_finished;
1812 buffer().swap(m_scratch_buffer);
1813 buffer().swap(m_scratch_buffer2);
1814 if (m_storage_mode != storage_mode_compact)
1816 // if no piece is out of place
1817 // since we're in full allocation mode, we can
1818 // forget the piece allocation tables
1819 std::vector<int>().swap(m_piece_to_slot);
1820 std::vector<int>().swap(m_slot_to_piece);
1821 std::vector<int>().swap(m_free_slots);
1822 std::vector<int>().swap(m_unallocated_slots);
1824 return no_error;
1827 // check if the fastresume data is up to date
1828 // if it is, use it and return true. If it
1829 // isn't return false and the full check
1830 // will be run
1831 int piece_manager::check_fastresume(
1832 lazy_entry const& rd, std::string& error)
1834 boost::recursive_mutex::scoped_lock lock(m_mutex);
1836 INVARIANT_CHECK;
1838 TORRENT_ASSERT(m_files.piece_length() > 0);
1840 // if we don't have any resume data, return
1841 if (rd.type() == lazy_entry::none_t) return check_no_fastresume(error);
1843 if (rd.type() != lazy_entry::dict_t)
1845 error = "invalid fastresume data (not a dictionary)";
1846 return check_no_fastresume(error);
1849 int block_size = (std::min)(16 * 1024, m_files.piece_length());
1850 int blocks_per_piece = rd.dict_find_int_value("blocks per piece", -1);
1851 if (blocks_per_piece != -1
1852 && blocks_per_piece != m_files.piece_length() / block_size)
1854 error = "invalid 'blocks per piece' entry";
1855 return check_no_fastresume(error);
1858 storage_mode_t storage_mode = storage_mode_compact;
1859 if (rd.dict_find_string_value("allocation") != "compact")
1860 storage_mode = storage_mode_sparse;
1862 if (!m_storage->verify_resume_data(rd, error))
1863 return check_no_fastresume(error);
1865 // assume no piece is out of place (i.e. in a slot
1866 // other than the one it should be in)
1867 bool out_of_place = false;
1869 // if we don't have a piece map, we need the slots
1870 // if we're in compact mode, we also need the slots map
1871 if (storage_mode == storage_mode_compact || rd.dict_find("pieces") == 0)
1873 // read slots map
1874 lazy_entry const* slots = rd.dict_find_list("slots");
1875 if (slots == 0)
1877 error = "missing slot list";
1878 return check_no_fastresume(error);
1881 if ((int)slots->list_size() > m_files.num_pieces())
1883 error = "file has more slots than torrent (slots: "
1884 + boost::lexical_cast<std::string>(slots->list_size()) + " size: "
1885 + boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
1886 return check_no_fastresume(error);
1889 if (m_storage_mode == storage_mode_compact)
1891 int num_pieces = int(m_files.num_pieces());
1892 m_slot_to_piece.resize(num_pieces, unallocated);
1893 m_piece_to_slot.resize(num_pieces, has_no_slot);
1894 for (int i = 0; i < slots->list_size(); ++i)
1896 lazy_entry const* e = slots->list_at(i);
1897 if (e->type() != lazy_entry::int_t)
1899 error = "invalid entry type in slot list";
1900 return check_no_fastresume(error);
1903 int index = int(e->int_value());
1904 if (index >= num_pieces || index < -2)
1906 error = "too high index number in slot map (index: "
1907 + boost::lexical_cast<std::string>(index) + " size: "
1908 + boost::lexical_cast<std::string>(num_pieces) + ")";
1909 return check_no_fastresume(error);
1911 if (index >= 0)
1913 m_slot_to_piece[i] = index;
1914 m_piece_to_slot[index] = i;
1915 if (i != index) out_of_place = true;
1917 else if (index == unassigned)
1919 if (m_storage_mode == storage_mode_compact)
1920 m_free_slots.push_back(i);
1922 else
1924 TORRENT_ASSERT(index == unallocated);
1925 if (m_storage_mode == storage_mode_compact)
1926 m_unallocated_slots.push_back(i);
1930 else
1932 for (int i = 0; i < slots->list_size(); ++i)
1934 lazy_entry const* e = slots->list_at(i);
1935 if (e->type() != lazy_entry::int_t)
1937 error = "invalid entry type in slot list";
1938 return check_no_fastresume(error);
1941 int index = int(e->int_value());
1942 if (index != i && index >= 0)
1944 error = "invalid slot index";
1945 return check_no_fastresume(error);
1950 // This will corrupt the storage
1951 // use while debugging to find
1952 // states that cannot be scanned
1953 // by check_pieces.
1954 // m_storage->shuffle();
1956 if (m_storage_mode == storage_mode_compact)
1958 if (m_unallocated_slots.empty()) switch_to_full_mode();
1960 else
1962 TORRENT_ASSERT(m_free_slots.empty());
1963 TORRENT_ASSERT(m_unallocated_slots.empty());
1965 if (out_of_place)
1967 // in this case we're in full allocation mode, but
1968 // we're resuming a compact allocated storage
1969 m_state = state_expand_pieces;
1970 m_current_slot = 0;
1971 error = "pieces needs to be reordered";
1972 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
1973 return need_full_check;
1978 else if (m_storage_mode == storage_mode_compact)
1980 // read piece map
1981 lazy_entry const* pieces = rd.dict_find("pieces");
1982 if (pieces == 0 || pieces->type() != lazy_entry::string_t)
1984 error = "missing pieces entry";
1985 return check_no_fastresume(error);
1988 if ((int)pieces->string_length() != m_files.num_pieces())
1990 error = "file has more slots than torrent (slots: "
1991 + boost::lexical_cast<std::string>(pieces->string_length()) + " size: "
1992 + boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
1993 return check_no_fastresume(error);
1996 int num_pieces = int(m_files.num_pieces());
1997 m_slot_to_piece.resize(num_pieces, unallocated);
1998 m_piece_to_slot.resize(num_pieces, has_no_slot);
1999 char const* have_pieces = pieces->string_ptr();
2000 for (int i = 0; i < num_pieces; ++i)
2002 if (have_pieces[i] & 1)
2004 m_slot_to_piece[i] = i;
2005 m_piece_to_slot[i] = i;
2007 else
2009 m_free_slots.push_back(i);
2012 if (m_unallocated_slots.empty()) switch_to_full_mode();
2015 return check_init_storage(error);
2019 state chart:
2021 check_fastresume() ----------+
2023 | | |
2024 | v v
2025 | +------------+ +---------------+
2026 | | full_check |-->| expand_pieses |
2027 | +------------+ +---------------+
2028 | | |
2029 | v |
2030 | +--------------+ |
2031 +->| finished | <------+
2032 +--------------+
2036 // performs the full check and full allocation
2037 // (if necessary). returns true if finished and
2038 // false if it should be called again
2039 // the second return value is the progress the
2040 // file check is at. 0 is nothing done, and 1
2041 // is finished
2042 int piece_manager::check_files(int& current_slot, int& have_piece, std::string& error)
2044 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2046 current_slot = m_current_slot;
2047 have_piece = -1;
2048 if (m_state == state_expand_pieces)
2050 INVARIANT_CHECK;
2052 if (m_scratch_piece >= 0)
2054 int piece = m_scratch_piece;
2055 int other_piece = m_slot_to_piece[piece];
2056 m_scratch_piece = -1;
2058 if (other_piece >= 0)
2060 if (m_scratch_buffer2.empty())
2061 m_scratch_buffer2.resize(m_files.piece_length());
2063 int piece_size = m_files.piece_size(other_piece);
2064 if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size)
2065 != piece_size)
2067 error = m_storage->error().message();
2068 TORRENT_ASSERT(!error.empty());
2069 return fatal_disk_error;
2071 m_scratch_piece = other_piece;
2072 m_piece_to_slot[other_piece] = unassigned;
2075 // the slot where this piece belongs is
2076 // free. Just move the piece there.
2077 int piece_size = m_files.piece_size(piece);
2078 if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
2080 error = m_storage->error().message();
2081 TORRENT_ASSERT(!error.empty());
2082 return fatal_disk_error;
2084 m_piece_to_slot[piece] = piece;
2085 m_slot_to_piece[piece] = piece;
2087 if (other_piece >= 0)
2088 m_scratch_buffer.swap(m_scratch_buffer2);
2090 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2091 return need_full_check;
2094 while (m_current_slot < m_files.num_pieces()
2095 && (m_slot_to_piece[m_current_slot] == m_current_slot
2096 || m_slot_to_piece[m_current_slot] < 0))
2098 ++m_current_slot;
2101 if (m_current_slot == m_files.num_pieces())
2103 return check_init_storage(error);
2106 TORRENT_ASSERT(m_current_slot < m_files.num_pieces());
2108 int piece = m_slot_to_piece[m_current_slot];
2109 TORRENT_ASSERT(piece >= 0);
2110 int other_piece = m_slot_to_piece[piece];
2111 if (other_piece >= 0)
2113 // there is another piece in the slot
2114 // where this one goes. Store it in the scratch
2115 // buffer until next iteration.
2116 if (m_scratch_buffer.empty())
2117 m_scratch_buffer.resize(m_files.piece_length());
2119 int piece_size = m_files.piece_size(other_piece);
2120 if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
2122 error = m_storage->error().message();
2123 TORRENT_ASSERT(!error.empty());
2124 return fatal_disk_error;
2126 m_scratch_piece = other_piece;
2127 m_piece_to_slot[other_piece] = unassigned;
2130 // the slot where this piece belongs is
2131 // free. Just move the piece there.
2132 m_storage->move_slot(m_current_slot, piece);
2133 m_piece_to_slot[piece] = piece;
2134 m_slot_to_piece[m_current_slot] = unassigned;
2135 m_slot_to_piece[piece] = piece;
2137 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2138 return need_full_check;
2141 TORRENT_ASSERT(m_state == state_full_check);
2143 int skip = check_one_piece(have_piece);
2144 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2146 if (skip == -1)
2148 error = m_storage->error().message();
2149 TORRENT_ASSERT(!error.empty());
2150 return fatal_disk_error;
2153 if (skip)
2155 clear_error();
2156 // skip means that the piece we checked failed to be read from disk
2157 // completely. We should skip all pieces belonging to that file.
2158 // find the file that failed, and skip all the pieces in that file
2159 size_type file_offset = 0;
2160 size_type current_offset = size_type(m_current_slot) * m_files.piece_length();
2161 for (file_storage::iterator i = m_files.begin()
2162 , end(m_files.end()); i != end; ++i)
2164 file_offset += i->size;
2165 if (file_offset > current_offset) break;
2168 TORRENT_ASSERT(file_offset > current_offset);
2169 int skip_blocks = static_cast<int>(
2170 (file_offset - current_offset + m_files.piece_length() - 1)
2171 / m_files.piece_length());
2172 TORRENT_ASSERT(skip_blocks >= 1);
2174 if (m_storage_mode == storage_mode_compact)
2176 for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i)
2178 TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
2179 m_unallocated_slots.push_back(i);
2183 // current slot will increase by one at the end of the for-loop too
2184 m_current_slot += skip_blocks - 1;
2185 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2188 ++m_current_slot;
2189 current_slot = m_current_slot;
2191 if (m_current_slot >= m_files.num_pieces())
2193 TORRENT_ASSERT(m_current_slot == m_files.num_pieces());
2195 // clear the memory we've been using
2196 std::vector<char>().swap(m_piece_data);
2197 std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
2199 if (m_storage_mode != storage_mode_compact)
2201 if (!m_out_of_place)
2203 // if no piece is out of place
2204 // since we're in full allocation mode, we can
2205 // forget the piece allocation tables
2207 std::vector<int>().swap(m_piece_to_slot);
2208 std::vector<int>().swap(m_slot_to_piece);
2209 return check_init_storage(error);
2211 else
2213 // in this case we're in full allocation mode, but
2214 // we're resuming a compact allocated storage
2215 m_state = state_expand_pieces;
2216 m_current_slot = 0;
2217 current_slot = m_current_slot;
2218 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2219 return need_full_check;
2222 else if (m_unallocated_slots.empty())
2224 switch_to_full_mode();
2226 return check_init_storage(error);
2229 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2230 return need_full_check;
2233 // -1=error 0=ok 1=skip
2234 int piece_manager::check_one_piece(int& have_piece)
2236 // ------------------------
2237 // DO THE FULL CHECK
2238 // ------------------------
2240 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2241 TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_files.num_pieces());
2242 TORRENT_ASSERT(have_piece == -1);
2244 // initialization for the full check
2245 if (m_hash_to_piece.empty())
2247 for (int i = 0; i < m_files.num_pieces(); ++i)
2248 m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i));
2251 m_piece_data.resize(int(m_files.piece_length()));
2252 int piece_size = m_files.piece_size(m_current_slot);
2253 int num_read = m_storage->read(&m_piece_data[0]
2254 , m_current_slot, 0, piece_size);
2256 if (num_read < 0)
2258 if (m_storage->error()
2259 && m_storage->error() != error_code(ENOENT, get_posix_category()))
2261 std::cerr << m_storage->error().message() << std::endl;
2262 return -1;
2264 return 1;
2267 // if the file is incomplete, skip the rest of it
2268 if (num_read != piece_size)
2269 return 1;
2271 int piece_index = identify_data(m_piece_data, m_current_slot);
2273 if (piece_index >= 0) have_piece = piece_index;
2275 if (piece_index != m_current_slot
2276 && piece_index >= 0)
2277 m_out_of_place = true;
2279 TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0);
2281 const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
2282 const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot;
2284 // check if this piece should be swapped with any other slot
2285 // this section will ensure that the storage is correctly sorted
2286 // libtorrent will never leave the storage in a state that
2287 // requires this sorting, but other clients may.
2289 // example of worst case:
2290 // | m_current_slot = 5
2291 // V
2292 // +---+- - - +---+- - - +---+- -
2293 // | x | | 5 | | 3 | <- piece data in slots
2294 // +---+- - - +---+- - - +---+- -
2295 // 3 y 5 <- slot index
2297 // in this example, the data in the m_current_slot (5)
2298 // is piece 3. It has to be moved into slot 3. The data
2299 // in slot y (piece 5) should be moved into the m_current_slot.
2300 // and the data in slot 3 (piece x) should be moved to slot y.
2302 // there are three possible cases.
2303 // 1. There's another piece that should be placed into this slot
2304 // 2. This piece should be placed into another slot.
2305 // 3. There's another piece that should be placed into this slot
2306 // and this piece should be placed into another slot
2308 // swap piece_index with this slot
2310 // case 1
2311 if (this_should_move && !other_should_move)
2313 TORRENT_ASSERT(piece_index != m_current_slot);
2315 const int other_slot = piece_index;
2316 TORRENT_ASSERT(other_slot >= 0);
2317 int other_piece = m_slot_to_piece[other_slot];
2319 m_slot_to_piece[other_slot] = piece_index;
2320 m_slot_to_piece[m_current_slot] = other_piece;
2321 m_piece_to_slot[piece_index] = piece_index;
2322 if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot;
2324 if (other_piece == unassigned)
2326 std::vector<int>::iterator i =
2327 std::find(m_free_slots.begin(), m_free_slots.end(), other_slot);
2328 TORRENT_ASSERT(i != m_free_slots.end());
2329 if (m_storage_mode == storage_mode_compact)
2331 m_free_slots.erase(i);
2332 m_free_slots.push_back(m_current_slot);
2336 bool ret = false;
2337 if (other_piece >= 0)
2338 ret |= m_storage->swap_slots(other_slot, m_current_slot);
2339 else
2340 ret |= m_storage->move_slot(m_current_slot, other_slot);
2342 if (ret) return 1;
2344 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2345 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2347 // case 2
2348 else if (!this_should_move && other_should_move)
2350 TORRENT_ASSERT(piece_index != m_current_slot);
2352 const int other_piece = m_current_slot;
2353 const int other_slot = m_piece_to_slot[other_piece];
2354 TORRENT_ASSERT(other_slot >= 0);
2356 m_slot_to_piece[m_current_slot] = other_piece;
2357 m_slot_to_piece[other_slot] = piece_index;
2358 m_piece_to_slot[other_piece] = m_current_slot;
2360 if (piece_index == unassigned
2361 && m_storage_mode == storage_mode_compact)
2362 m_free_slots.push_back(other_slot);
2364 bool ret = false;
2365 if (piece_index >= 0)
2367 m_piece_to_slot[piece_index] = other_slot;
2368 ret |= m_storage->swap_slots(other_slot, m_current_slot);
2370 else
2372 ret |= m_storage->move_slot(other_slot, m_current_slot);
2375 if (ret) return 1;
2377 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2378 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2380 else if (this_should_move && other_should_move)
2382 TORRENT_ASSERT(piece_index != m_current_slot);
2383 TORRENT_ASSERT(piece_index >= 0);
2385 const int piece1 = m_slot_to_piece[piece_index];
2386 const int piece2 = m_current_slot;
2387 const int slot1 = piece_index;
2388 const int slot2 = m_piece_to_slot[piece2];
2390 TORRENT_ASSERT(slot1 >= 0);
2391 TORRENT_ASSERT(slot2 >= 0);
2392 TORRENT_ASSERT(piece2 >= 0);
2394 if (slot1 == slot2)
2396 // this means there are only two pieces involved in the swap
2397 TORRENT_ASSERT(piece1 >= 0);
2399 // movement diagram:
2400 // +-------------------------------+
2401 // | |
2402 // +--> slot1 --> m_current_slot --+
2404 m_slot_to_piece[slot1] = piece_index;
2405 m_slot_to_piece[m_current_slot] = piece1;
2407 m_piece_to_slot[piece_index] = slot1;
2408 m_piece_to_slot[piece1] = m_current_slot;
2410 TORRENT_ASSERT(piece1 == m_current_slot);
2411 TORRENT_ASSERT(piece_index == slot1);
2413 m_storage->swap_slots(m_current_slot, slot1);
2415 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2416 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2418 else
2420 TORRENT_ASSERT(slot1 != slot2);
2421 TORRENT_ASSERT(piece1 != piece2);
2423 // movement diagram:
2424 // +-----------------------------------------+
2425 // | |
2426 // +--> slot1 --> slot2 --> m_current_slot --+
2428 m_slot_to_piece[slot1] = piece_index;
2429 m_slot_to_piece[slot2] = piece1;
2430 m_slot_to_piece[m_current_slot] = piece2;
2432 m_piece_to_slot[piece_index] = slot1;
2433 m_piece_to_slot[m_current_slot] = piece2;
2435 if (piece1 == unassigned)
2437 std::vector<int>::iterator i =
2438 std::find(m_free_slots.begin(), m_free_slots.end(), slot1);
2439 TORRENT_ASSERT(i != m_free_slots.end());
2440 if (m_storage_mode == storage_mode_compact)
2442 m_free_slots.erase(i);
2443 m_free_slots.push_back(slot2);
2447 bool ret = false;
2448 if (piece1 >= 0)
2450 m_piece_to_slot[piece1] = slot2;
2451 ret |= m_storage->swap_slots3(m_current_slot, slot1, slot2);
2453 else
2455 ret |= m_storage->move_slot(m_current_slot, slot1);
2456 ret |= m_storage->move_slot(slot2, m_current_slot);
2459 if (ret) return 1;
2461 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2462 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2465 else
2467 TORRENT_ASSERT(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot);
2468 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unallocated);
2469 TORRENT_ASSERT(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot);
2471 // the slot was identified as piece 'piece_index'
2472 if (piece_index != unassigned)
2473 m_piece_to_slot[piece_index] = m_current_slot;
2474 else if (m_storage_mode == storage_mode_compact)
2475 m_free_slots.push_back(m_current_slot);
2477 m_slot_to_piece[m_current_slot] = piece_index;
2479 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2480 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2482 return 0;
2485 void piece_manager::switch_to_full_mode()
2487 TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
2488 TORRENT_ASSERT(m_unallocated_slots.empty());
2489 // we have allocated all slots, switch to
2490 // full allocation mode in order to free
2491 // some unnecessary memory.
2492 m_storage_mode = storage_mode_sparse;
2493 std::vector<int>().swap(m_unallocated_slots);
2494 std::vector<int>().swap(m_free_slots);
2495 std::vector<int>().swap(m_piece_to_slot);
2496 std::vector<int>().swap(m_slot_to_piece);
2499 int piece_manager::allocate_slot_for_piece(int piece_index)
2501 boost::recursive_mutex::scoped_lock lock(m_mutex);
2503 if (m_storage_mode != storage_mode_compact) return piece_index;
2505 INVARIANT_CHECK;
2507 TORRENT_ASSERT(piece_index >= 0);
2508 TORRENT_ASSERT(piece_index < (int)m_piece_to_slot.size());
2509 TORRENT_ASSERT(m_piece_to_slot.size() == m_slot_to_piece.size());
2511 int slot_index = m_piece_to_slot[piece_index];
2513 if (slot_index != has_no_slot)
2515 TORRENT_ASSERT(slot_index >= 0);
2516 TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
2517 return slot_index;
2520 if (m_free_slots.empty())
2522 allocate_slots(1);
2523 TORRENT_ASSERT(!m_free_slots.empty());
2526 std::vector<int>::iterator iter(
2527 std::find(
2528 m_free_slots.begin()
2529 , m_free_slots.end()
2530 , piece_index));
2532 if (iter == m_free_slots.end())
2534 TORRENT_ASSERT(m_slot_to_piece[piece_index] != unassigned);
2535 TORRENT_ASSERT(!m_free_slots.empty());
2536 iter = m_free_slots.end() - 1;
2538 // special case to make sure we don't use the last slot
2539 // when we shouldn't, since it's smaller than ordinary slots
2540 if (*iter == m_files.num_pieces() - 1 && piece_index != *iter)
2542 if (m_free_slots.size() == 1)
2543 allocate_slots(1);
2544 TORRENT_ASSERT(m_free_slots.size() > 1);
2545 // assumes that all allocated slots
2546 // are put at the end of the free_slots vector
2547 iter = m_free_slots.end() - 1;
2551 slot_index = *iter;
2552 m_free_slots.erase(iter);
2554 TORRENT_ASSERT(m_slot_to_piece[slot_index] == unassigned);
2556 m_slot_to_piece[slot_index] = piece_index;
2557 m_piece_to_slot[piece_index] = slot_index;
2559 // there is another piece already assigned to
2560 // the slot we are interested in, swap positions
2561 if (slot_index != piece_index
2562 && m_slot_to_piece[piece_index] >= 0)
2565 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2566 std::stringstream s;
2568 s << "there is another piece at our slot, swapping..";
2570 s << "\n piece_index: ";
2571 s << piece_index;
2572 s << "\n slot_index: ";
2573 s << slot_index;
2574 s << "\n piece at our slot: ";
2575 s << m_slot_to_piece[piece_index];
2576 s << "\n";
2578 print_to_log(s.str());
2579 debug_log();
2580 #endif
2582 int piece_at_our_slot = m_slot_to_piece[piece_index];
2583 TORRENT_ASSERT(m_piece_to_slot[piece_at_our_slot] == piece_index);
2585 std::swap(
2586 m_slot_to_piece[piece_index]
2587 , m_slot_to_piece[slot_index]);
2589 std::swap(
2590 m_piece_to_slot[piece_index]
2591 , m_piece_to_slot[piece_at_our_slot]);
2593 m_storage->move_slot(piece_index, slot_index);
2595 TORRENT_ASSERT(m_slot_to_piece[piece_index] == piece_index);
2596 TORRENT_ASSERT(m_piece_to_slot[piece_index] == piece_index);
2598 slot_index = piece_index;
2600 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2601 debug_log();
2602 #endif
2604 TORRENT_ASSERT(slot_index >= 0);
2605 TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
2607 if (m_free_slots.empty() && m_unallocated_slots.empty())
2608 switch_to_full_mode();
2610 return slot_index;
2613 bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk)
2615 boost::recursive_mutex::scoped_lock lock(m_mutex);
2616 TORRENT_ASSERT(num_slots > 0);
2618 INVARIANT_CHECK;
2620 TORRENT_ASSERT(!m_unallocated_slots.empty());
2621 TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
2623 bool written = false;
2625 for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i)
2627 // INVARIANT_CHECK;
2629 int pos = m_unallocated_slots.front();
2630 TORRENT_ASSERT(m_slot_to_piece[pos] == unallocated);
2631 TORRENT_ASSERT(m_piece_to_slot[pos] != pos);
2633 int new_free_slot = pos;
2634 if (m_piece_to_slot[pos] != has_no_slot)
2636 new_free_slot = m_piece_to_slot[pos];
2637 m_storage->move_slot(new_free_slot, pos);
2638 m_slot_to_piece[pos] = pos;
2639 m_piece_to_slot[pos] = pos;
2640 written = true;
2642 m_unallocated_slots.erase(m_unallocated_slots.begin());
2643 m_slot_to_piece[new_free_slot] = unassigned;
2644 m_free_slots.push_back(new_free_slot);
2645 if (abort_on_disk && written) break;
2648 TORRENT_ASSERT(m_free_slots.size() > 0);
2649 return written;
2652 int piece_manager::slot_for(int piece) const
2654 if (m_storage_mode != storage_mode_compact) return piece;
2655 TORRENT_ASSERT(piece < int(m_piece_to_slot.size()));
2656 TORRENT_ASSERT(piece >= 0);
2657 return m_piece_to_slot[piece];
2660 int piece_manager::piece_for(int slot) const
2662 if (m_storage_mode != storage_mode_compact) return slot;
2663 TORRENT_ASSERT(slot < int(m_slot_to_piece.size()));
2664 TORRENT_ASSERT(slot >= 0);
2665 return m_slot_to_piece[slot];
2668 #ifndef NDEBUG
2669 void piece_manager::check_invariant() const
2671 boost::recursive_mutex::scoped_lock lock(m_mutex);
2673 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2675 if (m_unallocated_slots.empty()
2676 && m_free_slots.empty()
2677 && m_state == state_finished)
2679 TORRENT_ASSERT(m_storage_mode != storage_mode_compact
2680 || m_files.num_pieces() == 0);
2683 if (m_storage_mode != storage_mode_compact)
2685 TORRENT_ASSERT(m_unallocated_slots.empty());
2686 TORRENT_ASSERT(m_free_slots.empty());
2689 if (m_storage_mode != storage_mode_compact
2690 && m_state != state_expand_pieces
2691 && m_state != state_full_check)
2693 TORRENT_ASSERT(m_piece_to_slot.empty());
2694 TORRENT_ASSERT(m_slot_to_piece.empty());
2696 else
2698 if (m_piece_to_slot.empty()) return;
2700 TORRENT_ASSERT((int)m_piece_to_slot.size() == m_files.num_pieces());
2701 TORRENT_ASSERT((int)m_slot_to_piece.size() == m_files.num_pieces());
2703 for (std::vector<int>::const_iterator i = m_free_slots.begin();
2704 i != m_free_slots.end(); ++i)
2706 TORRENT_ASSERT(*i < (int)m_slot_to_piece.size());
2707 TORRENT_ASSERT(*i >= 0);
2708 TORRENT_ASSERT(m_slot_to_piece[*i] == unassigned);
2709 TORRENT_ASSERT(std::find(i+1, m_free_slots.end(), *i)
2710 == m_free_slots.end());
2713 for (std::vector<int>::const_iterator i = m_unallocated_slots.begin();
2714 i != m_unallocated_slots.end(); ++i)
2716 TORRENT_ASSERT(*i < (int)m_slot_to_piece.size());
2717 TORRENT_ASSERT(*i >= 0);
2718 TORRENT_ASSERT(m_slot_to_piece[*i] == unallocated);
2719 TORRENT_ASSERT(std::find(i+1, m_unallocated_slots.end(), *i)
2720 == m_unallocated_slots.end());
2723 for (int i = 0; i < m_files.num_pieces(); ++i)
2725 // Check domain of piece_to_slot's elements
2726 if (m_piece_to_slot[i] != has_no_slot)
2728 TORRENT_ASSERT(m_piece_to_slot[i] >= 0);
2729 TORRENT_ASSERT(m_piece_to_slot[i] < (int)m_slot_to_piece.size());
2732 // Check domain of slot_to_piece's elements
2733 if (m_slot_to_piece[i] != unallocated
2734 && m_slot_to_piece[i] != unassigned)
2736 TORRENT_ASSERT(m_slot_to_piece[i] >= 0);
2737 TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
2740 // do more detailed checks on piece_to_slot
2741 if (m_piece_to_slot[i] >= 0)
2743 TORRENT_ASSERT(m_slot_to_piece[m_piece_to_slot[i]] == i);
2744 if (m_piece_to_slot[i] != i)
2746 TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
2749 else
2751 TORRENT_ASSERT(m_piece_to_slot[i] == has_no_slot);
2754 // do more detailed checks on slot_to_piece
2756 if (m_slot_to_piece[i] >= 0)
2758 TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
2759 TORRENT_ASSERT(m_piece_to_slot[m_slot_to_piece[i]] == i);
2760 #ifdef TORRENT_STORAGE_DEBUG
2761 TORRENT_ASSERT(
2762 std::find(
2763 m_unallocated_slots.begin()
2764 , m_unallocated_slots.end()
2765 , i) == m_unallocated_slots.end()
2767 TORRENT_ASSERT(
2768 std::find(
2769 m_free_slots.begin()
2770 , m_free_slots.end()
2771 , i) == m_free_slots.end()
2773 #endif
2775 else if (m_slot_to_piece[i] == unallocated)
2777 #ifdef TORRENT_STORAGE_DEBUG
2778 TORRENT_ASSERT(m_unallocated_slots.empty()
2779 || (std::find(
2780 m_unallocated_slots.begin()
2781 , m_unallocated_slots.end()
2782 , i) != m_unallocated_slots.end())
2784 #endif
2786 else if (m_slot_to_piece[i] == unassigned)
2788 #ifdef TORRENT_STORAGE_DEBUG
2789 TORRENT_ASSERT(
2790 std::find(
2791 m_free_slots.begin()
2792 , m_free_slots.end()
2793 , i) != m_free_slots.end()
2795 #endif
2797 else
2799 TORRENT_ASSERT(false && "m_slot_to_piece[i] is invalid");
2805 #ifdef TORRENT_STORAGE_DEBUG
2806 void piece_manager::debug_log() const
2808 std::stringstream s;
2810 s << "index\tslot\tpiece\n";
2812 for (int i = 0; i < m_files.num_pieces(); ++i)
2814 s << i << "\t" << m_slot_to_piece[i] << "\t";
2815 s << m_piece_to_slot[i] << "\n";
2818 s << "---------------------------------\n";
2820 print_to_log(s.str());
2822 #endif
2823 #endif
2824 } // namespace libtorrent