delete_files bug fix
[libtorrent.git] / src / storage.cpp
blobdffea82675d5832a1cf47aff07c6ad4b7cf0cd44
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 recursive_copy(i->path(), new_path / i->leaf(), ec);
269 if (ec) return;
272 else
274 copy_file(old_path, new_path);
276 #ifndef BOOST_NO_EXCEPTIONS
277 } catch (std::exception& e) { ec = error_code(errno, get_posix_category()); }
278 #endif
281 template <class Path>
282 void recursive_remove(Path const& old_path)
284 using boost::filesystem::basic_directory_iterator;
285 #ifndef BOOST_NO_EXCEPTIONS
286 try {
287 #endif
288 if (is_directory(old_path))
290 for (basic_directory_iterator<Path> i(old_path), end; i != end; ++i)
291 recursive_remove(i->path());
292 remove(old_path);
294 else
296 remove(old_path);
298 #ifndef BOOST_NO_EXCEPTIONS
299 } catch (std::exception& e) {}
300 #endif
302 std::vector<std::pair<size_type, std::time_t> > get_filesizes(
303 file_storage const& s, fs::path p)
305 p = complete(p);
306 std::vector<std::pair<size_type, std::time_t> > sizes;
307 for (file_storage::iterator i = s.begin()
308 , end(s.end());i != end; ++i)
310 size_type size = 0;
311 std::time_t time = 0;
312 #if TORRENT_USE_WPATH
313 fs::wpath f = safe_convert((p / i->path).string());
314 #else
315 fs::path f = p / i->path;
316 #endif
317 #ifndef BOOST_NO_EXCEPTIONS
319 #else
320 if (exists(f))
321 #endif
323 size = file_size(f);
324 time = last_write_time(f);
326 #ifndef BOOST_NO_EXCEPTIONS
327 catch (std::exception&) {}
328 #endif
329 sizes.push_back(std::make_pair(size, time));
331 return sizes;
334 // matches the sizes and timestamps of the files passed in
335 // in non-compact mode, actual file sizes and timestamps
336 // are allowed to be bigger and more recent than the fast
337 // resume data. This is because full allocation will not move
338 // pieces, so any older version of the resume data will
339 // still be a correct subset of the actual data on disk.
340 bool match_filesizes(
341 file_storage const& fs
342 , fs::path p
343 , std::vector<std::pair<size_type, std::time_t> > const& sizes
344 , bool compact_mode
345 , std::string* error)
347 if ((int)sizes.size() != fs.num_files())
349 if (error) *error = "mismatching number of files";
350 return false;
352 p = complete(p);
354 std::vector<std::pair<size_type, std::time_t> >::const_iterator s
355 = sizes.begin();
356 for (file_storage::iterator i = fs.begin()
357 , end(fs.end());i != end; ++i, ++s)
359 size_type size = 0;
360 std::time_t time = 0;
362 #if TORRENT_USE_WPATH
363 fs::wpath f = safe_convert((p / i->path).string());
364 #else
365 fs::path f = p / i->path;
366 #endif
367 #ifndef BOOST_NO_EXCEPTIONS
369 #else
370 if (exists(f))
371 #endif
373 size = file_size(f);
374 time = last_write_time(f);
376 #ifndef BOOST_NO_EXCEPTIONS
377 catch (std::exception&) {}
378 #endif
379 if ((compact_mode && size != s->first)
380 || (!compact_mode && size < s->first))
382 if (error) *error = "filesize mismatch for file '"
383 + i->path.native_file_string()
384 + "', size: " + boost::lexical_cast<std::string>(size)
385 + ", expected to be " + boost::lexical_cast<std::string>(s->first)
386 + " bytes";
387 return false;
389 if ((compact_mode && time != s->second)
390 || (!compact_mode && time < s->second))
392 if (error) *error = "timestamp mismatch for file '"
393 + i->path.native_file_string()
394 + "', modification date: " + boost::lexical_cast<std::string>(time)
395 + ", expected to have modification date "
396 + boost::lexical_cast<std::string>(s->second);
397 return false;
400 return true;
403 class storage : public storage_interface, boost::noncopyable
405 public:
406 storage(file_storage const& fs, fs::path const& path, file_pool& fp)
407 : m_files(fs)
408 , m_pool(fp)
410 TORRENT_ASSERT(m_files.begin() != m_files.end());
411 m_save_path = fs::complete(path);
412 TORRENT_ASSERT(m_save_path.is_complete());
415 bool rename_file(int index, std::string const& new_filename);
416 bool release_files();
417 bool delete_files();
418 bool initialize(bool allocate_files);
419 bool move_storage(fs::path save_path);
420 int read(char* buf, int slot, int offset, int size);
421 int write(const char* buf, int slot, int offset, int size);
422 bool move_slot(int src_slot, int dst_slot);
423 bool swap_slots(int slot1, int slot2);
424 bool swap_slots3(int slot1, int slot2, int slot3);
425 bool verify_resume_data(lazy_entry const& rd, std::string& error);
426 bool write_resume_data(entry& rd) const;
427 sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size);
429 int read_impl(char* buf, int slot, int offset, int size, bool fill_zero);
431 ~storage()
432 { m_pool.release(this); }
434 file_storage const& files() const { return m_mapped_files?*m_mapped_files:m_files; }
436 boost::scoped_ptr<file_storage> m_mapped_files;
437 file_storage const& m_files;
439 std::vector<boost::uint8_t> m_file_priority;
440 fs::path m_save_path;
441 // the file pool is typically stored in
442 // the session, to make all storage
443 // instances use the same pool
444 file_pool& m_pool;
446 // temporary storage for moving pieces
447 buffer m_scratch_buffer;
450 sha1_hash storage::hash_for_slot(int slot, partial_hash& ph, int piece_size)
452 #ifndef NDEBUG
453 hasher partial;
454 hasher whole;
455 int slot_size1 = piece_size;
456 m_scratch_buffer.resize(slot_size1);
457 read_impl(&m_scratch_buffer[0], slot, 0, slot_size1, true);
458 if (ph.offset > 0)
459 partial.update(&m_scratch_buffer[0], ph.offset);
460 whole.update(&m_scratch_buffer[0], slot_size1);
461 hasher partial_copy = ph.h;
462 TORRENT_ASSERT(ph.offset == 0 || partial_copy.final() == partial.final());
463 #endif
464 int slot_size = piece_size - ph.offset;
465 if (slot_size > 0)
467 m_scratch_buffer.resize(slot_size);
468 read_impl(&m_scratch_buffer[0], slot, ph.offset, slot_size, true);
469 ph.h.update(&m_scratch_buffer[0], slot_size);
471 #ifndef NDEBUG
472 sha1_hash ret = ph.h.final();
473 TORRENT_ASSERT(ret == whole.final());
474 return ret;
475 #else
476 return ph.h.final();
477 #endif
480 bool storage::initialize(bool allocate_files)
482 error_code ec;
483 // first, create all missing directories
484 fs::path last_path;
485 for (file_storage::iterator file_iter = files().begin(),
486 end_iter = files().end(); file_iter != end_iter; ++file_iter)
488 fs::path dir = (m_save_path / file_iter->path).branch_path();
490 if (dir != last_path)
493 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
494 last_path = dir;
495 if (!exists_win(last_path))
496 create_directories_win(last_path);
497 #elif TORRENT_USE_WPATH
498 last_path = dir;
499 fs::wpath wp = safe_convert(last_path.string());
500 if (!exists(wp))
501 create_directories(wp);
502 #else
503 last_path = dir;
504 if (!exists(last_path))
505 create_directories(last_path);
506 #endif
509 // if the file is empty, just create it. But also make sure
510 // the directory exists.
511 if (file_iter->size == 0)
513 file(m_save_path / file_iter->path, file::out, ec);
514 if (ec)
516 set_error(m_save_path / file_iter->path, ec);
517 return true;
519 continue;
522 #ifndef BOOST_NO_EXCEPTIONS
523 try {
524 #endif
525 // don't allocate files with priority 0
526 int file_index = file_iter - files().begin();
527 if (allocate_files && (m_file_priority.size() <= file_index
528 || m_file_priority[file_index] > 0))
530 error_code ec;
531 boost::shared_ptr<file> f = m_pool.open_file(this
532 , m_save_path / file_iter->path, file::in | file::out, ec);
533 if (ec) set_error(m_save_path / file_iter->path, ec);
534 else if (f)
536 f->set_size(file_iter->size, ec);
537 if (ec) set_error(m_save_path / file_iter->path, ec);
540 #ifndef BOOST_NO_EXCEPTIONS
542 catch (std::exception& e)
544 set_error(m_save_path / file_iter->path
545 , error_code(errno, get_posix_category()));
546 return true;
548 #endif
550 std::vector<boost::uint8_t>().swap(m_file_priority);
551 // close files that were opened in write mode
552 m_pool.release(this);
553 return false;
556 bool storage::rename_file(int index, std::string const& new_filename)
558 if (index < 0 || index >= m_files.num_files()) return true;
559 fs::path old_name = m_save_path / files().at(index).path;
560 m_pool.release(old_name);
562 #if TORRENT_USE_WPATH
563 fs::wpath old_path = safe_convert(old_name.string());
564 fs::wpath new_path = safe_convert((m_save_path / new_filename).string());
565 #else
566 fs::path const& old_path = old_name;
567 fs::path new_path = m_save_path / new_filename;
568 #endif
570 #ifndef BOOST_NO_EXCEPTIONS
573 #endif
574 rename(old_path, new_path);
576 error_code ec;
577 rename(old_path, new_path, ec);
578 if (ec)
580 set_error(old_path, ec);
581 return;
584 if (!m_mapped_files)
585 { m_mapped_files.reset(new file_storage(m_files)); }
586 m_mapped_files->rename_file(index, new_filename);
587 #ifndef BOOST_NO_EXCEPTIONS
589 catch (std::exception& e)
591 set_error(old_name, error_code(errno, get_posix_category()));
592 return true;
594 #endif
595 return false;
598 bool storage::release_files()
600 m_pool.release(this);
601 buffer().swap(m_scratch_buffer);
602 return false;
605 bool storage::delete_files()
607 // make sure we don't have the files open
608 m_pool.release(this);
609 buffer().swap(m_scratch_buffer);
611 int error = 0;
612 std::string error_file;
614 // delete the files from disk
615 std::set<std::string> directories;
616 typedef std::set<std::string>::iterator iter_t;
617 for (file_storage::iterator i = files().begin()
618 , end(files().end()); i != end; ++i)
620 std::string p = (m_save_path / i->path).string();
621 fs::path bp = i->path.branch_path();
622 std::pair<iter_t, bool> ret;
623 ret.second = true;
624 while (ret.second && !bp.empty())
626 std::pair<iter_t, bool> ret = directories.insert((m_save_path / bp).string());
627 bp = bp.branch_path();
629 #if TORRENT_USE_WPATH
631 { fs::remove(safe_convert(p)); }
632 catch (std::exception& e)
634 error = errno;
635 error_file = p;
637 #else
638 if (std::remove(p.c_str()) != 0 && errno != ENOENT)
640 error = errno;
641 error_file = p;
643 #endif
646 // remove the directories. Reverse order to delete
647 // subdirectories first
649 for (std::set<std::string>::reverse_iterator i = directories.rbegin()
650 , end(directories.rend()); i != end; ++i)
652 #if TORRENT_USE_WPATH
654 { fs::remove(safe_convert(*i)); }
655 catch (std::exception& e)
657 error = errno;
658 error_file = *i;
660 #else
661 if (std::remove(i->c_str()) != 0 && errno != ENOENT)
663 error = errno;
664 error_file = *i;
666 #endif
669 if (error)
671 m_error = error_code(error, get_posix_category());
672 m_error_file.swap(error_file);
673 return true;
675 return false;
678 bool storage::write_resume_data(entry& rd) const
680 TORRENT_ASSERT(rd.type() == entry::dictionary_t);
682 std::vector<std::pair<size_type, std::time_t> > file_sizes
683 = get_filesizes(files(), m_save_path);
685 entry::list_type& fl = rd["file sizes"].list();
686 for (std::vector<std::pair<size_type, std::time_t> >::iterator i
687 = file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
689 entry::list_type p;
690 p.push_back(entry(i->first));
691 p.push_back(entry(i->second));
692 fl.push_back(entry(p));
695 if (m_mapped_files)
697 entry::list_type& fl = rd["mapped_files"].list();
698 for (file_storage::iterator i = m_mapped_files->begin()
699 , end(m_mapped_files->end()); i != end; ++i)
701 fl.push_back(i->path.string());
705 return false;
708 bool storage::verify_resume_data(lazy_entry const& rd, std::string& error)
710 lazy_entry const* file_priority = rd.dict_find_list("file_priority");
711 if (file_priority && file_priority->list_size()
712 == files().num_files())
714 m_file_priority.resize(file_priority->list_size());
715 for (int i = 0; i < file_priority->list_size(); ++i)
716 m_file_priority[i] = file_priority->list_int_value_at(i, 1);
719 lazy_entry const* mapped_files = rd.dict_find_list("mapped_files");
720 if (mapped_files && mapped_files->list_size() == m_files.num_files())
722 if (!m_mapped_files)
723 { m_mapped_files.reset(new file_storage(m_files)); }
724 for (int i = 0; i < m_files.num_files(); ++i)
725 m_mapped_files->rename_file(i, mapped_files->list_string_value_at(i));
728 std::vector<std::pair<size_type, std::time_t> > file_sizes;
729 lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes");
730 if (file_sizes_ent == 0)
732 error = "missing or invalid 'file sizes' entry in resume data";
733 return false;
736 for (int i = 0; i < file_sizes_ent->list_size(); ++i)
738 lazy_entry const* e = file_sizes_ent->list_at(i);
739 if (e->type() != lazy_entry::list_t
740 || e->list_size() != 2
741 || e->list_at(0)->type() != lazy_entry::int_t
742 || e->list_at(1)->type() != lazy_entry::int_t)
743 continue;
744 file_sizes.push_back(std::pair<size_type, std::time_t>(
745 e->list_int_value_at(0), std::time_t(e->list_int_value_at(1))));
748 if (file_sizes.empty())
750 error = "the number of files in resume data is 0";
751 return false;
754 bool seed = false;
756 lazy_entry const* slots = rd.dict_find_list("slots");
757 if (slots)
759 if (int(slots->list_size()) == m_files.num_pieces())
761 seed = true;
762 for (int i = 0; i < slots->list_size(); ++i)
764 lazy_entry const* e = slots->list_at(i);
765 if (e->list_int_value_at(i, -1) >= 0) continue;
766 seed = false;
767 break;
771 else if (lazy_entry const* pieces = rd.dict_find_string("pieces"))
773 if (int(pieces->string_length()) == m_files.num_pieces())
775 seed = true;
776 char const* p = pieces->string_ptr();
777 for (int i = 0; i < pieces->string_length(); ++i)
779 if ((p[i] & 1) == 1) continue;
780 seed = false;
781 break;
785 else
787 error = "missing 'slots' and 'pieces' entry in resume data";
788 return false;
791 bool full_allocation_mode = false;
792 if (rd.dict_find_string_value("allocation") != "compact")
793 full_allocation_mode = true;
795 if (seed)
797 if (files().num_files() != (int)file_sizes.size())
799 error = "the number of files does not match the torrent (num: "
800 + boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
801 + boost::lexical_cast<std::string>(files().num_files()) + ")";
802 return false;
805 std::vector<std::pair<size_type, std::time_t> >::iterator
806 fs = file_sizes.begin();
807 // the resume data says we have the entire torrent
808 // make sure the file sizes are the right ones
809 for (file_storage::iterator i = files().begin()
810 , end(files().end()); i != end; ++i, ++fs)
812 if (i->size != fs->first)
814 error = "file size for '" + i->path.native_file_string()
815 + "' was expected to be "
816 + boost::lexical_cast<std::string>(i->size) + " bytes";
817 return false;
822 return match_filesizes(files(), m_save_path, file_sizes
823 , !full_allocation_mode, &error);
826 // returns true on success
827 bool storage::move_storage(fs::path save_path)
829 #if TORRENT_USE_WPATH
830 fs::wpath old_path;
831 fs::wpath new_path;
832 #else
833 fs::path old_path;
834 fs::path new_path;
835 #endif
837 save_path = complete(save_path);
839 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
840 std::wstring wsave_path(safe_convert(save_path.native_file_string()));
841 if (!exists_win(save_path))
842 CreateDirectory(wsave_path.c_str(), 0);
843 else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0)
844 return false;
845 #elif TORRENT_USE_WPATH
846 fs::wpath wp = safe_convert(save_path.string());
847 if (!exists(wp))
848 create_directory(wp);
849 else if (!is_directory(wp))
850 return false;
851 #else
852 if (!exists(save_path))
853 create_directory(save_path);
854 else if (!is_directory(save_path))
855 return false;
856 #endif
858 m_pool.release(this);
860 #if TORRENT_USE_WPATH
861 old_path = safe_convert((m_save_path / files().name()).string());
862 new_path = safe_convert((save_path / files().name()).string());
863 #else
864 old_path = m_save_path / files().name();
865 new_path = save_path / files().name();
866 #endif
868 #ifndef BOOST_NO_EXCEPTIONS
871 #endif
872 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
873 rename_win(old_path, new_path);
874 #else
875 rename(old_path, new_path);
876 #endif
877 m_save_path = save_path;
878 return true;
879 #ifndef BOOST_NO_EXCEPTIONS
881 catch (std::exception& e)
883 error_code ec;
884 recursive_copy(old_path, new_path, ec);
885 if (ec)
887 set_error(m_save_path / files().name(), ec);
888 return true;
890 m_save_path = save_path;
891 recursive_remove(old_path);
893 #endif
894 return false;
897 #ifndef NDEBUG
899 void storage::shuffle()
901 int num_pieces = files().num_pieces();
903 std::vector<int> pieces(num_pieces);
904 for (std::vector<int>::iterator i = pieces.begin();
905 i != pieces.end(); ++i)
907 *i = static_cast<int>(i - pieces.begin());
909 std::srand((unsigned int)std::time(0));
910 std::vector<int> targets(pieces);
911 std::random_shuffle(pieces.begin(), pieces.end());
912 std::random_shuffle(targets.begin(), targets.end());
914 for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i)
916 const int slot_index = targets[i];
917 const int piece_index = pieces[i];
918 const int slot_size =static_cast<int>(m_files.piece_size(slot_index));
919 std::vector<char> buf(slot_size);
920 read(&buf[0], piece_index, 0, slot_size);
921 write(&buf[0], slot_index, 0, slot_size);
925 #endif
927 bool storage::move_slot(int src_slot, int dst_slot)
929 int piece_size = m_files.piece_size(dst_slot);
930 m_scratch_buffer.resize(piece_size);
931 int ret1 = read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true);
932 int ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
933 return ret1 != piece_size || ret2 != piece_size;
936 bool storage::swap_slots(int slot1, int slot2)
938 // the size of the target slot is the size of the piece
939 int piece_size = m_files.piece_length();
940 int piece1_size = m_files.piece_size(slot2);
941 int piece2_size = m_files.piece_size(slot1);
942 m_scratch_buffer.resize(piece_size * 2);
943 int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
944 int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
945 int ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
946 int ret4 = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size);
947 return ret1 != piece1_size || ret2 != piece2_size
948 || ret3 != piece1_size || ret4 != piece2_size;
951 bool storage::swap_slots3(int slot1, int slot2, int slot3)
953 // the size of the target slot is the size of the piece
954 int piece_size = m_files.piece_length();
955 int piece1_size = m_files.piece_size(slot2);
956 int piece2_size = m_files.piece_size(slot3);
957 int piece3_size = m_files.piece_size(slot1);
958 m_scratch_buffer.resize(piece_size * 2);
959 int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
960 int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
961 int ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
962 int ret4 = read_impl(&m_scratch_buffer[0], slot3, 0, piece3_size, true);
963 int ret5 = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size);
964 int ret6 = write(&m_scratch_buffer[0], slot1, 0, piece3_size);
965 return ret1 != piece1_size || ret2 != piece2_size
966 || ret3 != piece1_size || ret4 != piece3_size
967 || ret5 != piece2_size || ret6 != piece3_size;
970 int storage::read(
971 char* buf
972 , int slot
973 , int offset
974 , int size)
976 return read_impl(buf, slot, offset, size, false);
979 int storage::read_impl(
980 char* buf
981 , int slot
982 , int offset
983 , int size
984 , bool fill_zero)
986 TORRENT_ASSERT(buf != 0);
987 TORRENT_ASSERT(slot >= 0 && slot < m_files.num_pieces());
988 TORRENT_ASSERT(offset >= 0);
989 TORRENT_ASSERT(offset < m_files.piece_size(slot));
990 TORRENT_ASSERT(size > 0);
992 #ifndef NDEBUG
993 std::vector<file_slice> slices
994 = files().map_block(slot, offset, size);
995 TORRENT_ASSERT(!slices.empty());
996 #endif
998 size_type start = slot * (size_type)m_files.piece_length() + offset;
999 TORRENT_ASSERT(start + size <= m_files.total_size());
1001 // find the file iterator and file offset
1002 size_type file_offset = start;
1003 std::vector<file_entry>::const_iterator file_iter;
1005 for (file_iter = files().begin();;)
1007 if (file_offset < file_iter->size)
1008 break;
1010 file_offset -= file_iter->size;
1011 ++file_iter;
1014 int buf_pos = 0;
1015 error_code ec;
1016 boost::shared_ptr<file> in(m_pool.open_file(
1017 this, m_save_path / file_iter->path, file::in, ec));
1018 if (!in || ec)
1020 set_error(m_save_path / file_iter->path, ec);
1021 return -1;
1023 TORRENT_ASSERT(file_offset < file_iter->size);
1024 TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base);
1026 size_type new_pos = in->seek(file_offset + file_iter->file_base, file::begin, ec);
1027 if (new_pos != file_offset + file_iter->file_base || ec)
1029 // the file was not big enough
1030 if (!fill_zero)
1032 set_error(m_save_path / file_iter->path, ec);
1033 return -1;
1035 std::memset(buf + buf_pos, 0, size - buf_pos);
1036 return size;
1039 #ifndef NDEBUG
1040 size_type in_tell = in->tell(ec);
1041 TORRENT_ASSERT(in_tell == file_offset + file_iter->file_base && !ec);
1042 #endif
1044 int left_to_read = size;
1045 int slot_size = static_cast<int>(m_files.piece_size(slot));
1047 if (offset + left_to_read > slot_size)
1048 left_to_read = slot_size - offset;
1050 TORRENT_ASSERT(left_to_read >= 0);
1052 size_type result = left_to_read;
1054 #ifndef NDEBUG
1055 int counter = 0;
1056 #endif
1058 while (left_to_read > 0)
1060 int read_bytes = left_to_read;
1061 if (file_offset + read_bytes > file_iter->size)
1062 read_bytes = static_cast<int>(file_iter->size - file_offset);
1064 if (read_bytes > 0)
1066 #ifndef NDEBUG
1067 TORRENT_ASSERT(int(slices.size()) > counter);
1068 size_type slice_size = slices[counter].size;
1069 TORRENT_ASSERT(slice_size == read_bytes);
1070 TORRENT_ASSERT(files().at(slices[counter].file_index).path
1071 == file_iter->path);
1072 #endif
1074 int actual_read = int(in->read(buf + buf_pos, read_bytes, ec));
1076 if (read_bytes != actual_read || ec)
1078 // the file was not big enough
1079 if (actual_read > 0) buf_pos += actual_read;
1080 if (!fill_zero)
1082 set_error(m_save_path / file_iter->path, ec);
1083 return -1;
1085 std::memset(buf + buf_pos, 0, size - buf_pos);
1086 return size;
1089 left_to_read -= read_bytes;
1090 buf_pos += read_bytes;
1091 TORRENT_ASSERT(buf_pos >= 0);
1092 file_offset += read_bytes;
1095 if (left_to_read > 0)
1097 ++file_iter;
1098 #ifndef NDEBUG
1099 // empty files are not returned by map_block, so if
1100 // this file was empty, don't increment the slice counter
1101 if (read_bytes > 0) ++counter;
1102 #endif
1103 fs::path path = m_save_path / file_iter->path;
1105 file_offset = 0;
1106 error_code ec;
1107 in = m_pool.open_file( this, path, file::in, ec);
1108 if (!in || ec)
1110 set_error(path, ec);
1111 return -1;
1113 size_type pos = in->seek(file_iter->file_base, file::begin, ec);
1114 if (pos != file_iter->file_base || ec)
1116 if (!fill_zero)
1118 set_error(m_save_path / file_iter->path, ec);
1119 return -1;
1121 std::memset(buf + buf_pos, 0, size - buf_pos);
1122 return size;
1126 return result;
1129 int storage::write(
1130 const char* buf
1131 , int slot
1132 , int offset
1133 , int size)
1135 TORRENT_ASSERT(buf != 0);
1136 TORRENT_ASSERT(slot >= 0);
1137 TORRENT_ASSERT(slot < m_files.num_pieces());
1138 TORRENT_ASSERT(offset >= 0);
1139 TORRENT_ASSERT(size > 0);
1141 #ifndef NDEBUG
1142 std::vector<file_slice> slices
1143 = files().map_block(slot, offset, size);
1144 TORRENT_ASSERT(!slices.empty());
1145 #endif
1147 size_type start = slot * (size_type)m_files.piece_length() + offset;
1149 // find the file iterator and file offset
1150 size_type file_offset = start;
1151 std::vector<file_entry>::const_iterator file_iter;
1153 for (file_iter = files().begin();;)
1155 if (file_offset < file_iter->size)
1156 break;
1158 file_offset -= file_iter->size;
1159 ++file_iter;
1160 TORRENT_ASSERT(file_iter != files().end());
1163 fs::path p(m_save_path / file_iter->path);
1164 error_code ec;
1165 boost::shared_ptr<file> out = m_pool.open_file(
1166 this, p, file::out | file::in, ec);
1168 if (!out || ec)
1170 set_error(p, ec);
1171 return -1;
1173 TORRENT_ASSERT(file_offset < file_iter->size);
1174 TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base);
1176 size_type pos = out->seek(file_offset + file_iter->file_base, file::begin, ec);
1178 if (pos != file_offset + file_iter->file_base || ec)
1180 set_error(p, ec);
1181 return -1;
1184 int left_to_write = size;
1185 int slot_size = static_cast<int>(m_files.piece_size(slot));
1187 if (offset + left_to_write > slot_size)
1188 left_to_write = slot_size - offset;
1190 TORRENT_ASSERT(left_to_write >= 0);
1192 int buf_pos = 0;
1193 #ifndef NDEBUG
1194 int counter = 0;
1195 #endif
1196 while (left_to_write > 0)
1198 int write_bytes = left_to_write;
1199 if (file_offset + write_bytes > file_iter->size)
1201 TORRENT_ASSERT(file_iter->size >= file_offset);
1202 write_bytes = static_cast<int>(file_iter->size - file_offset);
1205 if (write_bytes > 0)
1207 TORRENT_ASSERT(int(slices.size()) > counter);
1208 TORRENT_ASSERT(slices[counter].size == write_bytes);
1209 TORRENT_ASSERT(files().at(slices[counter].file_index).path
1210 == file_iter->path);
1212 TORRENT_ASSERT(buf_pos >= 0);
1213 TORRENT_ASSERT(write_bytes >= 0);
1214 error_code ec;
1215 size_type written = out->write(buf + buf_pos, write_bytes, ec);
1217 if (written != write_bytes || ec)
1219 set_error(m_save_path / file_iter->path, ec);
1220 return -1;
1223 left_to_write -= write_bytes;
1224 buf_pos += write_bytes;
1225 TORRENT_ASSERT(buf_pos >= 0);
1226 file_offset += write_bytes;
1227 TORRENT_ASSERT(file_offset <= file_iter->size);
1230 if (left_to_write > 0)
1232 #ifndef NDEBUG
1233 if (write_bytes > 0) ++counter;
1234 #endif
1235 ++file_iter;
1237 TORRENT_ASSERT(file_iter != files().end());
1238 fs::path p = m_save_path / file_iter->path;
1239 file_offset = 0;
1240 error_code ec;
1241 out = m_pool.open_file(
1242 this, p, file::out | file::in, ec);
1244 if (!out || ec)
1246 set_error(p, ec);
1247 return -1;
1250 size_type pos = out->seek(file_iter->file_base, file::begin, ec);
1252 if (pos != file_iter->file_base || ec)
1254 set_error(p, ec);
1255 return -1;
1259 return size;
1262 storage_interface* default_storage_constructor(file_storage const& fs
1263 , fs::path const& path, file_pool& fp)
1265 return new storage(fs, path, fp);
1268 // -- piece_manager -----------------------------------------------------
1270 piece_manager::piece_manager(
1271 boost::shared_ptr<void> const& torrent
1272 , boost::intrusive_ptr<torrent_info const> info
1273 , fs::path const& save_path
1274 , file_pool& fp
1275 , disk_io_thread& io
1276 , storage_constructor_type sc
1277 , storage_mode_t sm)
1278 : m_info(info)
1279 , m_files(m_info->files())
1280 , m_storage(sc(m_files, save_path, fp))
1281 , m_storage_mode(sm)
1282 , m_save_path(complete(save_path))
1283 , m_state(state_none)
1284 , m_current_slot(0)
1285 , m_out_of_place(false)
1286 , m_scratch_piece(-1)
1287 , m_storage_constructor(sc)
1288 , m_io_thread(io)
1289 , m_torrent(torrent)
1293 piece_manager::~piece_manager()
1297 void piece_manager::async_save_resume_data(
1298 boost::function<void(int, disk_io_job const&)> const& handler)
1300 disk_io_job j;
1301 j.storage = this;
1302 j.action = disk_io_job::save_resume_data;
1303 m_io_thread.add_job(j, handler);
1306 void piece_manager::async_clear_read_cache(
1307 boost::function<void(int, disk_io_job const&)> const& handler)
1309 disk_io_job j;
1310 j.storage = this;
1311 j.action = disk_io_job::clear_read_cache;
1312 m_io_thread.add_job(j, handler);
1315 void piece_manager::async_release_files(
1316 boost::function<void(int, disk_io_job const&)> const& handler)
1318 disk_io_job j;
1319 j.storage = this;
1320 j.action = disk_io_job::release_files;
1321 m_io_thread.add_job(j, handler);
1324 void piece_manager::async_delete_files(
1325 boost::function<void(int, disk_io_job const&)> const& handler)
1327 disk_io_job j;
1328 j.storage = this;
1329 j.action = disk_io_job::delete_files;
1330 m_io_thread.add_job(j, handler);
1333 void piece_manager::async_move_storage(fs::path const& p
1334 , boost::function<void(int, disk_io_job const&)> const& handler)
1336 disk_io_job j;
1337 j.storage = this;
1338 j.action = disk_io_job::move_storage;
1339 j.str = p.string();
1340 m_io_thread.add_job(j, handler);
1343 void piece_manager::async_check_fastresume(lazy_entry const* resume_data
1344 , boost::function<void(int, disk_io_job const&)> const& handler)
1346 TORRENT_ASSERT(resume_data != 0);
1347 disk_io_job j;
1348 j.storage = this;
1349 j.action = disk_io_job::check_fastresume;
1350 j.buffer = (char*)resume_data;
1351 m_io_thread.add_job(j, handler);
1354 void piece_manager::async_rename_file(int index, std::string const& name
1355 , boost::function<void(int, disk_io_job const&)> const& handler)
1357 disk_io_job j;
1358 j.storage = this;
1359 j.piece = index;
1360 j.str = name;
1361 j.action = disk_io_job::rename_file;
1362 m_io_thread.add_job(j, handler);
1365 void piece_manager::async_check_files(
1366 boost::function<void(int, disk_io_job const&)> const& handler)
1368 disk_io_job j;
1369 j.storage = this;
1370 j.action = disk_io_job::check_files;
1371 m_io_thread.add_job(j, handler);
1374 void piece_manager::async_read(
1375 peer_request const& r
1376 , boost::function<void(int, disk_io_job const&)> const& handler
1377 , int priority)
1379 disk_io_job j;
1380 j.storage = this;
1381 j.action = disk_io_job::read;
1382 j.piece = r.piece;
1383 j.offset = r.start;
1384 j.buffer_size = r.length;
1385 j.buffer = 0;
1386 j.priority = priority;
1387 // if a buffer is not specified, only one block can be read
1388 // since that is the size of the pool allocator's buffers
1389 TORRENT_ASSERT(r.length <= 16 * 1024);
1390 m_io_thread.add_job(j, handler);
1391 #ifndef NDEBUG
1392 boost::recursive_mutex::scoped_lock l(m_mutex);
1393 // if this assert is hit, it suggests
1394 // that check_files was not successful
1395 TORRENT_ASSERT(slot_for(r.piece) >= 0);
1396 #endif
1399 void piece_manager::async_write(
1400 peer_request const& r
1401 , disk_buffer_holder& buffer
1402 , boost::function<void(int, disk_io_job const&)> const& handler)
1404 TORRENT_ASSERT(r.length <= 16 * 1024);
1405 // the buffer needs to be allocated through the io_thread
1406 TORRENT_ASSERT(m_io_thread.is_disk_buffer(buffer.get()));
1408 disk_io_job j;
1409 j.storage = this;
1410 j.action = disk_io_job::write;
1411 j.piece = r.piece;
1412 j.offset = r.start;
1413 j.buffer_size = r.length;
1414 j.buffer = buffer.get();
1415 m_io_thread.add_job(j, handler);
1416 buffer.release();
1419 void piece_manager::async_hash(int piece
1420 , boost::function<void(int, disk_io_job const&)> const& handler)
1422 disk_io_job j;
1423 j.storage = this;
1424 j.action = disk_io_job::hash;
1425 j.piece = piece;
1427 m_io_thread.add_job(j, handler);
1430 fs::path piece_manager::save_path() const
1432 boost::recursive_mutex::scoped_lock l(m_mutex);
1433 return m_save_path;
1436 sha1_hash piece_manager::hash_for_piece_impl(int piece)
1438 partial_hash ph;
1440 std::map<int, partial_hash>::iterator i = m_piece_hasher.find(piece);
1441 if (i != m_piece_hasher.end())
1443 ph = i->second;
1444 m_piece_hasher.erase(i);
1447 int slot = slot_for(piece);
1448 TORRENT_ASSERT(slot != has_no_slot);
1449 return m_storage->hash_for_slot(slot, ph, m_files.piece_size(piece));
1452 int piece_manager::move_storage_impl(fs::path const& save_path)
1454 if (m_storage->move_storage(save_path))
1456 m_save_path = fs::complete(save_path);
1457 return 0;
1459 return -1;
1462 void piece_manager::write_resume_data(entry& rd) const
1464 boost::recursive_mutex::scoped_lock lock(m_mutex);
1466 INVARIANT_CHECK;
1468 m_storage->write_resume_data(rd);
1470 if (m_storage_mode == storage_mode_compact)
1472 entry::list_type& slots = rd["slots"].list();
1473 slots.clear();
1474 std::vector<int>::const_reverse_iterator last;
1475 for (last = m_slot_to_piece.rbegin();
1476 last != m_slot_to_piece.rend(); ++last)
1478 if (*last != unallocated) break;
1481 for (std::vector<int>::const_iterator i =
1482 m_slot_to_piece.begin();
1483 i != last.base(); ++i)
1485 slots.push_back((*i >= 0) ? *i : unassigned);
1489 rd["allocation"] = m_storage_mode == storage_mode_sparse?"sparse"
1490 :m_storage_mode == storage_mode_allocate?"full":"compact";
1493 void piece_manager::mark_failed(int piece_index)
1495 INVARIANT_CHECK;
1497 if (m_storage_mode != storage_mode_compact) return;
1499 TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
1500 int slot_index = m_piece_to_slot[piece_index];
1501 TORRENT_ASSERT(slot_index >= 0);
1503 m_slot_to_piece[slot_index] = unassigned;
1504 m_piece_to_slot[piece_index] = has_no_slot;
1505 m_free_slots.push_back(slot_index);
1508 int piece_manager::read_impl(
1509 char* buf
1510 , int piece_index
1511 , int offset
1512 , int size)
1514 TORRENT_ASSERT(buf);
1515 TORRENT_ASSERT(offset >= 0);
1516 TORRENT_ASSERT(size > 0);
1517 int slot = slot_for(piece_index);
1518 return m_storage->read(buf, slot, offset, size);
1521 int piece_manager::write_impl(
1522 const char* buf
1523 , int piece_index
1524 , int offset
1525 , int size)
1527 TORRENT_ASSERT(buf);
1528 TORRENT_ASSERT(offset >= 0);
1529 TORRENT_ASSERT(size > 0);
1530 TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces());
1532 int slot = allocate_slot_for_piece(piece_index);
1533 int ret = m_storage->write(buf, slot, offset, size);
1534 // only save the partial hash if the write succeeds
1535 if (ret != size) return ret;
1537 // std::ofstream out("partial_hash.log", std::ios::app);
1539 if (offset == 0)
1541 partial_hash& ph = m_piece_hasher[piece_index];
1542 TORRENT_ASSERT(ph.offset == 0);
1543 ph.offset = size;
1544 ph.h.update(buf, size);
1546 out << time_now_string() << " NEW ["
1547 " s: " << this
1548 << " p: " << piece_index
1549 << " off: " << offset
1550 << " size: " << size
1551 << " entries: " << m_piece_hasher.size()
1552 << " ]" << std::endl;
1555 else
1557 std::map<int, partial_hash>::iterator i = m_piece_hasher.find(piece_index);
1558 if (i != m_piece_hasher.end())
1560 #ifndef NDEBUG
1561 TORRENT_ASSERT(i->second.offset > 0);
1562 int hash_offset = i->second.offset;
1563 TORRENT_ASSERT(offset >= hash_offset);
1564 #endif
1565 if (offset == i->second.offset)
1568 out << time_now_string() << " UPDATING ["
1569 " s: " << this
1570 << " p: " << piece_index
1571 << " off: " << offset
1572 << " size: " << size
1573 << " entries: " << m_piece_hasher.size()
1574 << " ]" << std::endl;
1576 i->second.offset += size;
1577 i->second.h.update(buf, size);
1579 /* else
1581 out << time_now_string() << " SKIPPING (out of order) ["
1582 " s: " << this
1583 << " p: " << piece_index
1584 << " off: " << offset
1585 << " size: " << size
1586 << " entries: " << m_piece_hasher.size()
1587 << " ]" << std::endl;
1589 */ }
1590 /* else
1592 out << time_now_string() << " SKIPPING (no entry) ["
1593 " s: " << this
1594 << " p: " << piece_index
1595 << " off: " << offset
1596 << " size: " << size
1597 << " entries: " << m_piece_hasher.size()
1598 << " ]" << std::endl;
1603 return ret;
1606 int piece_manager::identify_data(
1607 const std::vector<char>& piece_data
1608 , int current_slot)
1610 // INVARIANT_CHECK;
1612 const int piece_size = static_cast<int>(m_files.piece_length());
1613 const int last_piece_size = static_cast<int>(m_files.piece_size(
1614 m_files.num_pieces() - 1));
1616 TORRENT_ASSERT((int)piece_data.size() >= last_piece_size);
1618 // calculate a small digest, with the same
1619 // size as the last piece. And a large digest
1620 // which has the same size as a normal piece
1621 hasher small_digest;
1622 small_digest.update(&piece_data[0], last_piece_size);
1623 hasher large_digest(small_digest);
1624 TORRENT_ASSERT(piece_size - last_piece_size >= 0);
1625 if (piece_size - last_piece_size > 0)
1627 large_digest.update(
1628 &piece_data[last_piece_size]
1629 , piece_size - last_piece_size);
1631 sha1_hash large_hash = large_digest.final();
1632 sha1_hash small_hash = small_digest.final();
1634 typedef std::multimap<sha1_hash, int>::const_iterator map_iter;
1635 map_iter begin1;
1636 map_iter end1;
1637 map_iter begin2;
1638 map_iter end2;
1640 // makes the lookups for the small digest and the large digest
1641 boost::tie(begin1, end1) = m_hash_to_piece.equal_range(small_hash);
1642 boost::tie(begin2, end2) = m_hash_to_piece.equal_range(large_hash);
1644 // copy all potential piece indices into this vector
1645 std::vector<int> matching_pieces;
1646 for (map_iter i = begin1; i != end1; ++i)
1647 matching_pieces.push_back(i->second);
1648 for (map_iter i = begin2; i != end2; ++i)
1649 matching_pieces.push_back(i->second);
1651 // no piece matched the data in the slot
1652 if (matching_pieces.empty())
1653 return unassigned;
1655 // ------------------------------------------
1656 // CHECK IF THE PIECE IS IN ITS CORRECT PLACE
1657 // ------------------------------------------
1659 if (std::find(
1660 matching_pieces.begin()
1661 , matching_pieces.end()
1662 , current_slot) != matching_pieces.end())
1664 // the current slot is among the matching pieces, so
1665 // we will assume that the piece is in the right place
1666 const int piece_index = current_slot;
1668 int other_slot = m_piece_to_slot[piece_index];
1669 if (other_slot >= 0)
1671 // we have already found a piece with
1672 // this index.
1674 // take one of the other matching pieces
1675 // that hasn't already been assigned
1676 int other_piece = -1;
1677 for (std::vector<int>::iterator i = matching_pieces.begin();
1678 i != matching_pieces.end(); ++i)
1680 if (m_piece_to_slot[*i] >= 0 || *i == piece_index) continue;
1681 other_piece = *i;
1682 break;
1684 if (other_piece >= 0)
1686 // replace the old slot with 'other_piece'
1687 m_slot_to_piece[other_slot] = other_piece;
1688 m_piece_to_slot[other_piece] = other_slot;
1690 else
1692 // this index is the only piece with this
1693 // hash. The previous slot we found with
1694 // this hash must be the same piece. Mark
1695 // that piece as unassigned, since this slot
1696 // is the correct place for the piece.
1697 m_slot_to_piece[other_slot] = unassigned;
1698 if (m_storage_mode == storage_mode_compact)
1699 m_free_slots.push_back(other_slot);
1701 TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot);
1702 TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0);
1703 m_piece_to_slot[piece_index] = has_no_slot;
1706 TORRENT_ASSERT(m_piece_to_slot[piece_index] == has_no_slot);
1708 return piece_index;
1711 // find a matching piece that hasn't
1712 // already been assigned
1713 int free_piece = unassigned;
1714 for (std::vector<int>::iterator i = matching_pieces.begin();
1715 i != matching_pieces.end(); ++i)
1717 if (m_piece_to_slot[*i] >= 0) continue;
1718 free_piece = *i;
1719 break;
1722 if (free_piece >= 0)
1724 TORRENT_ASSERT(m_piece_to_slot[free_piece] == has_no_slot);
1725 return free_piece;
1727 else
1729 TORRENT_ASSERT(free_piece == unassigned);
1730 return unassigned;
1734 int piece_manager::check_no_fastresume(std::string& error)
1736 file_storage::iterator i = m_files.begin();
1737 file_storage::iterator end = m_files.end();
1739 for (; i != end; ++i)
1741 bool file_exists = false;
1742 fs::path f = m_save_path / i->path;
1743 #ifndef BOOST_NO_EXCEPTIONS
1746 #endif
1747 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
1748 file_exists = exists_win(f);
1749 #elif TORRENT_USE_WPATH
1750 fs::wpath wf = safe_convert(f.string());
1751 file_exists = exists(wf);
1752 #else
1753 file_exists = exists(f);
1754 #endif
1755 #ifndef BOOST_NO_EXCEPTIONS
1757 catch (std::exception& e)
1759 error = f.string();
1760 error += ": ";
1761 error += e.what();
1762 TORRENT_ASSERT(!error.empty());
1763 return fatal_disk_error;
1765 #endif
1766 if (file_exists && i->size > 0)
1768 m_state = state_full_check;
1769 m_piece_to_slot.clear();
1770 m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
1771 m_slot_to_piece.clear();
1772 m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
1773 if (m_storage_mode == storage_mode_compact)
1775 m_unallocated_slots.clear();
1776 m_free_slots.clear();
1778 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
1779 return need_full_check;
1783 if (m_storage_mode == storage_mode_compact)
1785 // in compact mode without checking, we need to
1786 // populate the unallocated list
1787 TORRENT_ASSERT(m_unallocated_slots.empty());
1788 for (int i = 0, end(m_files.num_pieces()); i < end; ++i)
1789 m_unallocated_slots.push_back(i);
1790 m_piece_to_slot.clear();
1791 m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
1792 m_slot_to_piece.clear();
1793 m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
1796 return check_init_storage(error);
1799 int piece_manager::check_init_storage(std::string& error)
1801 if (m_storage->initialize(m_storage_mode == storage_mode_allocate))
1803 error = m_storage->error().message();
1804 TORRENT_ASSERT(!error.empty());
1805 return fatal_disk_error;
1807 m_state = state_finished;
1808 buffer().swap(m_scratch_buffer);
1809 buffer().swap(m_scratch_buffer2);
1810 if (m_storage_mode != storage_mode_compact)
1812 // if no piece is out of place
1813 // since we're in full allocation mode, we can
1814 // forget the piece allocation tables
1815 std::vector<int>().swap(m_piece_to_slot);
1816 std::vector<int>().swap(m_slot_to_piece);
1817 std::vector<int>().swap(m_free_slots);
1818 std::vector<int>().swap(m_unallocated_slots);
1820 return no_error;
1823 // check if the fastresume data is up to date
1824 // if it is, use it and return true. If it
1825 // isn't return false and the full check
1826 // will be run
1827 int piece_manager::check_fastresume(
1828 lazy_entry const& rd, std::string& error)
1830 boost::recursive_mutex::scoped_lock lock(m_mutex);
1832 INVARIANT_CHECK;
1834 TORRENT_ASSERT(m_files.piece_length() > 0);
1836 // if we don't have any resume data, return
1837 if (rd.type() == lazy_entry::none_t) return check_no_fastresume(error);
1839 if (rd.type() != lazy_entry::dict_t)
1841 error = "invalid fastresume data (not a dictionary)";
1842 return check_no_fastresume(error);
1845 int block_size = (std::min)(16 * 1024, m_files.piece_length());
1846 int blocks_per_piece = rd.dict_find_int_value("blocks per piece", -1);
1847 if (blocks_per_piece != -1
1848 && blocks_per_piece != m_files.piece_length() / block_size)
1850 error = "invalid 'blocks per piece' entry";
1851 return check_no_fastresume(error);
1854 storage_mode_t storage_mode = storage_mode_compact;
1855 if (rd.dict_find_string_value("allocation") != "compact")
1856 storage_mode = storage_mode_sparse;
1858 if (!m_storage->verify_resume_data(rd, error))
1859 return check_no_fastresume(error);
1861 // assume no piece is out of place (i.e. in a slot
1862 // other than the one it should be in)
1863 bool out_of_place = false;
1865 // if we don't have a piece map, we need the slots
1866 // if we're in compact mode, we also need the slots map
1867 if (storage_mode == storage_mode_compact || rd.dict_find("pieces") == 0)
1869 // read slots map
1870 lazy_entry const* slots = rd.dict_find_list("slots");
1871 if (slots == 0)
1873 error = "missing slot list";
1874 return check_no_fastresume(error);
1877 if ((int)slots->list_size() > m_files.num_pieces())
1879 error = "file has more slots than torrent (slots: "
1880 + boost::lexical_cast<std::string>(slots->list_size()) + " size: "
1881 + boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
1882 return check_no_fastresume(error);
1885 if (m_storage_mode == storage_mode_compact)
1887 int num_pieces = int(m_files.num_pieces());
1888 m_slot_to_piece.resize(num_pieces, unallocated);
1889 m_piece_to_slot.resize(num_pieces, has_no_slot);
1890 for (int i = 0; i < slots->list_size(); ++i)
1892 lazy_entry const* e = slots->list_at(i);
1893 if (e->type() != lazy_entry::int_t)
1895 error = "invalid entry type in slot list";
1896 return check_no_fastresume(error);
1899 int index = int(e->int_value());
1900 if (index >= num_pieces || index < -2)
1902 error = "too high index number in slot map (index: "
1903 + boost::lexical_cast<std::string>(index) + " size: "
1904 + boost::lexical_cast<std::string>(num_pieces) + ")";
1905 return check_no_fastresume(error);
1907 if (index >= 0)
1909 m_slot_to_piece[i] = index;
1910 m_piece_to_slot[index] = i;
1911 if (i != index) out_of_place = true;
1913 else if (index == unassigned)
1915 if (m_storage_mode == storage_mode_compact)
1916 m_free_slots.push_back(i);
1918 else
1920 TORRENT_ASSERT(index == unallocated);
1921 if (m_storage_mode == storage_mode_compact)
1922 m_unallocated_slots.push_back(i);
1926 else
1928 for (int i = 0; i < slots->list_size(); ++i)
1930 lazy_entry const* e = slots->list_at(i);
1931 if (e->type() != lazy_entry::int_t)
1933 error = "invalid entry type in slot list";
1934 return check_no_fastresume(error);
1937 int index = int(e->int_value());
1938 if (index != i && index >= 0)
1940 error = "invalid slot index";
1941 return check_no_fastresume(error);
1946 // This will corrupt the storage
1947 // use while debugging to find
1948 // states that cannot be scanned
1949 // by check_pieces.
1950 // m_storage->shuffle();
1952 if (m_storage_mode == storage_mode_compact)
1954 if (m_unallocated_slots.empty()) switch_to_full_mode();
1956 else
1958 TORRENT_ASSERT(m_free_slots.empty());
1959 TORRENT_ASSERT(m_unallocated_slots.empty());
1961 if (out_of_place)
1963 // in this case we're in full allocation mode, but
1964 // we're resuming a compact allocated storage
1965 m_state = state_expand_pieces;
1966 m_current_slot = 0;
1967 error = "pieces needs to be reordered";
1968 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
1969 return need_full_check;
1974 else if (m_storage_mode == storage_mode_compact)
1976 // read piece map
1977 lazy_entry const* pieces = rd.dict_find("pieces");
1978 if (pieces == 0 || pieces->type() != lazy_entry::string_t)
1980 error = "missing pieces entry";
1981 return check_no_fastresume(error);
1984 if ((int)pieces->string_length() != m_files.num_pieces())
1986 error = "file has more slots than torrent (slots: "
1987 + boost::lexical_cast<std::string>(pieces->string_length()) + " size: "
1988 + boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
1989 return check_no_fastresume(error);
1992 int num_pieces = int(m_files.num_pieces());
1993 m_slot_to_piece.resize(num_pieces, unallocated);
1994 m_piece_to_slot.resize(num_pieces, has_no_slot);
1995 char const* have_pieces = pieces->string_ptr();
1996 for (int i = 0; i < num_pieces; ++i)
1998 if (have_pieces[i] & 1)
2000 m_slot_to_piece[i] = i;
2001 m_piece_to_slot[i] = i;
2003 else
2005 m_free_slots.push_back(i);
2008 if (m_unallocated_slots.empty()) switch_to_full_mode();
2011 return check_init_storage(error);
2015 state chart:
2017 check_fastresume() ----------+
2019 | | |
2020 | v v
2021 | +------------+ +---------------+
2022 | | full_check |-->| expand_pieses |
2023 | +------------+ +---------------+
2024 | | |
2025 | v |
2026 | +--------------+ |
2027 +->| finished | <------+
2028 +--------------+
2032 // performs the full check and full allocation
2033 // (if necessary). returns true if finished and
2034 // false if it should be called again
2035 // the second return value is the progress the
2036 // file check is at. 0 is nothing done, and 1
2037 // is finished
2038 int piece_manager::check_files(int& current_slot, int& have_piece, std::string& error)
2040 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2042 current_slot = m_current_slot;
2043 have_piece = -1;
2044 if (m_state == state_expand_pieces)
2046 INVARIANT_CHECK;
2048 if (m_scratch_piece >= 0)
2050 int piece = m_scratch_piece;
2051 int other_piece = m_slot_to_piece[piece];
2052 m_scratch_piece = -1;
2054 if (other_piece >= 0)
2056 if (m_scratch_buffer2.empty())
2057 m_scratch_buffer2.resize(m_files.piece_length());
2059 int piece_size = m_files.piece_size(other_piece);
2060 if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size)
2061 != piece_size)
2063 error = m_storage->error().message();
2064 TORRENT_ASSERT(!error.empty());
2065 return fatal_disk_error;
2067 m_scratch_piece = other_piece;
2068 m_piece_to_slot[other_piece] = unassigned;
2071 // the slot where this piece belongs is
2072 // free. Just move the piece there.
2073 int piece_size = m_files.piece_size(piece);
2074 if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
2076 error = m_storage->error().message();
2077 TORRENT_ASSERT(!error.empty());
2078 return fatal_disk_error;
2080 m_piece_to_slot[piece] = piece;
2081 m_slot_to_piece[piece] = piece;
2083 if (other_piece >= 0)
2084 m_scratch_buffer.swap(m_scratch_buffer2);
2086 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2087 return need_full_check;
2090 while (m_current_slot < m_files.num_pieces()
2091 && (m_slot_to_piece[m_current_slot] == m_current_slot
2092 || m_slot_to_piece[m_current_slot] < 0))
2094 ++m_current_slot;
2097 if (m_current_slot == m_files.num_pieces())
2099 return check_init_storage(error);
2102 TORRENT_ASSERT(m_current_slot < m_files.num_pieces());
2104 int piece = m_slot_to_piece[m_current_slot];
2105 TORRENT_ASSERT(piece >= 0);
2106 int other_piece = m_slot_to_piece[piece];
2107 if (other_piece >= 0)
2109 // there is another piece in the slot
2110 // where this one goes. Store it in the scratch
2111 // buffer until next iteration.
2112 if (m_scratch_buffer.empty())
2113 m_scratch_buffer.resize(m_files.piece_length());
2115 int piece_size = m_files.piece_size(other_piece);
2116 if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
2118 error = m_storage->error().message();
2119 TORRENT_ASSERT(!error.empty());
2120 return fatal_disk_error;
2122 m_scratch_piece = other_piece;
2123 m_piece_to_slot[other_piece] = unassigned;
2126 // the slot where this piece belongs is
2127 // free. Just move the piece there.
2128 m_storage->move_slot(m_current_slot, piece);
2129 m_piece_to_slot[piece] = piece;
2130 m_slot_to_piece[m_current_slot] = unassigned;
2131 m_slot_to_piece[piece] = piece;
2133 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2134 return need_full_check;
2137 TORRENT_ASSERT(m_state == state_full_check);
2139 int skip = check_one_piece(have_piece);
2140 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2142 if (skip == -1)
2144 error = m_storage->error().message();
2145 TORRENT_ASSERT(!error.empty());
2146 return fatal_disk_error;
2149 if (skip)
2151 clear_error();
2152 // skip means that the piece we checked failed to be read from disk
2153 // completely. We should skip all pieces belonging to that file.
2154 // find the file that failed, and skip all the pieces in that file
2155 size_type file_offset = 0;
2156 size_type current_offset = size_type(m_current_slot) * m_files.piece_length();
2157 for (file_storage::iterator i = m_files.begin()
2158 , end(m_files.end()); i != end; ++i)
2160 file_offset += i->size;
2161 if (file_offset > current_offset) break;
2164 TORRENT_ASSERT(file_offset > current_offset);
2165 int skip_blocks = static_cast<int>(
2166 (file_offset - current_offset + m_files.piece_length() - 1)
2167 / m_files.piece_length());
2169 if (m_storage_mode == storage_mode_compact)
2171 for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i)
2173 TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
2174 m_unallocated_slots.push_back(i);
2178 // current slot will increase by one at the end of the for-loop too
2179 m_current_slot += skip_blocks - 1;
2180 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2183 ++m_current_slot;
2184 current_slot = m_current_slot;
2186 if (m_current_slot >= m_files.num_pieces())
2188 TORRENT_ASSERT(m_current_slot == m_files.num_pieces());
2190 // clear the memory we've been using
2191 std::vector<char>().swap(m_piece_data);
2192 std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
2194 if (m_storage_mode != storage_mode_compact)
2196 if (!m_out_of_place)
2198 // if no piece is out of place
2199 // since we're in full allocation mode, we can
2200 // forget the piece allocation tables
2202 std::vector<int>().swap(m_piece_to_slot);
2203 std::vector<int>().swap(m_slot_to_piece);
2204 return check_init_storage(error);
2206 else
2208 // in this case we're in full allocation mode, but
2209 // we're resuming a compact allocated storage
2210 m_state = state_expand_pieces;
2211 m_current_slot = 0;
2212 current_slot = m_current_slot;
2213 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2214 return need_full_check;
2217 else if (m_unallocated_slots.empty())
2219 switch_to_full_mode();
2221 return check_init_storage(error);
2224 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2225 return need_full_check;
2228 // -1=error 0=ok 1=skip
2229 int piece_manager::check_one_piece(int& have_piece)
2231 // ------------------------
2232 // DO THE FULL CHECK
2233 // ------------------------
2235 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2236 TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_files.num_pieces());
2237 TORRENT_ASSERT(have_piece == -1);
2239 // initialization for the full check
2240 if (m_hash_to_piece.empty())
2242 for (int i = 0; i < m_files.num_pieces(); ++i)
2243 m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i));
2246 m_piece_data.resize(int(m_files.piece_length()));
2247 int piece_size = m_files.piece_size(m_current_slot);
2248 int num_read = m_storage->read(&m_piece_data[0]
2249 , m_current_slot, 0, piece_size);
2251 if (num_read < 0)
2253 if (m_storage->error()
2254 && m_storage->error() != error_code(ENOENT, get_posix_category()))
2256 std::cerr << m_storage->error().message() << std::endl;
2257 return -1;
2259 return 1;
2262 // if the file is incomplete, skip the rest of it
2263 if (num_read != piece_size)
2264 return 1;
2266 int piece_index = identify_data(m_piece_data, m_current_slot);
2268 if (piece_index >= 0) have_piece = piece_index;
2270 if (piece_index != m_current_slot
2271 && piece_index >= 0)
2272 m_out_of_place = true;
2274 TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0);
2276 const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
2277 const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot;
2279 // check if this piece should be swapped with any other slot
2280 // this section will ensure that the storage is correctly sorted
2281 // libtorrent will never leave the storage in a state that
2282 // requires this sorting, but other clients may.
2284 // example of worst case:
2285 // | m_current_slot = 5
2286 // V
2287 // +---+- - - +---+- - - +---+- -
2288 // | x | | 5 | | 3 | <- piece data in slots
2289 // +---+- - - +---+- - - +---+- -
2290 // 3 y 5 <- slot index
2292 // in this example, the data in the m_current_slot (5)
2293 // is piece 3. It has to be moved into slot 3. The data
2294 // in slot y (piece 5) should be moved into the m_current_slot.
2295 // and the data in slot 3 (piece x) should be moved to slot y.
2297 // there are three possible cases.
2298 // 1. There's another piece that should be placed into this slot
2299 // 2. This piece should be placed into another slot.
2300 // 3. There's another piece that should be placed into this slot
2301 // and this piece should be placed into another slot
2303 // swap piece_index with this slot
2305 // case 1
2306 if (this_should_move && !other_should_move)
2308 TORRENT_ASSERT(piece_index != m_current_slot);
2310 const int other_slot = piece_index;
2311 TORRENT_ASSERT(other_slot >= 0);
2312 int other_piece = m_slot_to_piece[other_slot];
2314 m_slot_to_piece[other_slot] = piece_index;
2315 m_slot_to_piece[m_current_slot] = other_piece;
2316 m_piece_to_slot[piece_index] = piece_index;
2317 if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot;
2319 if (other_piece == unassigned)
2321 std::vector<int>::iterator i =
2322 std::find(m_free_slots.begin(), m_free_slots.end(), other_slot);
2323 TORRENT_ASSERT(i != m_free_slots.end());
2324 if (m_storage_mode == storage_mode_compact)
2326 m_free_slots.erase(i);
2327 m_free_slots.push_back(m_current_slot);
2331 bool ret = false;
2332 if (other_piece >= 0)
2333 ret |= m_storage->swap_slots(other_slot, m_current_slot);
2334 else
2335 ret |= m_storage->move_slot(m_current_slot, other_slot);
2337 if (ret) return 1;
2339 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2340 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2342 // case 2
2343 else if (!this_should_move && other_should_move)
2345 TORRENT_ASSERT(piece_index != m_current_slot);
2347 const int other_piece = m_current_slot;
2348 const int other_slot = m_piece_to_slot[other_piece];
2349 TORRENT_ASSERT(other_slot >= 0);
2351 m_slot_to_piece[m_current_slot] = other_piece;
2352 m_slot_to_piece[other_slot] = piece_index;
2353 m_piece_to_slot[other_piece] = m_current_slot;
2355 if (piece_index == unassigned
2356 && m_storage_mode == storage_mode_compact)
2357 m_free_slots.push_back(other_slot);
2359 bool ret = false;
2360 if (piece_index >= 0)
2362 m_piece_to_slot[piece_index] = other_slot;
2363 ret |= m_storage->swap_slots(other_slot, m_current_slot);
2365 else
2367 ret |= m_storage->move_slot(other_slot, m_current_slot);
2370 if (ret) return 1;
2372 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2373 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2375 else if (this_should_move && other_should_move)
2377 TORRENT_ASSERT(piece_index != m_current_slot);
2378 TORRENT_ASSERT(piece_index >= 0);
2380 const int piece1 = m_slot_to_piece[piece_index];
2381 const int piece2 = m_current_slot;
2382 const int slot1 = piece_index;
2383 const int slot2 = m_piece_to_slot[piece2];
2385 TORRENT_ASSERT(slot1 >= 0);
2386 TORRENT_ASSERT(slot2 >= 0);
2387 TORRENT_ASSERT(piece2 >= 0);
2389 if (slot1 == slot2)
2391 // this means there are only two pieces involved in the swap
2392 TORRENT_ASSERT(piece1 >= 0);
2394 // movement diagram:
2395 // +-------------------------------+
2396 // | |
2397 // +--> slot1 --> m_current_slot --+
2399 m_slot_to_piece[slot1] = piece_index;
2400 m_slot_to_piece[m_current_slot] = piece1;
2402 m_piece_to_slot[piece_index] = slot1;
2403 m_piece_to_slot[piece1] = m_current_slot;
2405 TORRENT_ASSERT(piece1 == m_current_slot);
2406 TORRENT_ASSERT(piece_index == slot1);
2408 m_storage->swap_slots(m_current_slot, slot1);
2410 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2411 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2413 else
2415 TORRENT_ASSERT(slot1 != slot2);
2416 TORRENT_ASSERT(piece1 != piece2);
2418 // movement diagram:
2419 // +-----------------------------------------+
2420 // | |
2421 // +--> slot1 --> slot2 --> m_current_slot --+
2423 m_slot_to_piece[slot1] = piece_index;
2424 m_slot_to_piece[slot2] = piece1;
2425 m_slot_to_piece[m_current_slot] = piece2;
2427 m_piece_to_slot[piece_index] = slot1;
2428 m_piece_to_slot[m_current_slot] = piece2;
2430 if (piece1 == unassigned)
2432 std::vector<int>::iterator i =
2433 std::find(m_free_slots.begin(), m_free_slots.end(), slot1);
2434 TORRENT_ASSERT(i != m_free_slots.end());
2435 if (m_storage_mode == storage_mode_compact)
2437 m_free_slots.erase(i);
2438 m_free_slots.push_back(slot2);
2442 bool ret = false;
2443 if (piece1 >= 0)
2445 m_piece_to_slot[piece1] = slot2;
2446 ret |= m_storage->swap_slots3(m_current_slot, slot1, slot2);
2448 else
2450 ret |= m_storage->move_slot(m_current_slot, slot1);
2451 ret |= m_storage->move_slot(slot2, m_current_slot);
2454 if (ret) return 1;
2456 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2457 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2460 else
2462 TORRENT_ASSERT(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot);
2463 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unallocated);
2464 TORRENT_ASSERT(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot);
2466 // the slot was identified as piece 'piece_index'
2467 if (piece_index != unassigned)
2468 m_piece_to_slot[piece_index] = m_current_slot;
2469 else if (m_storage_mode == storage_mode_compact)
2470 m_free_slots.push_back(m_current_slot);
2472 m_slot_to_piece[m_current_slot] = piece_index;
2474 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2475 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2477 return 0;
2480 void piece_manager::switch_to_full_mode()
2482 TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
2483 TORRENT_ASSERT(m_unallocated_slots.empty());
2484 // we have allocated all slots, switch to
2485 // full allocation mode in order to free
2486 // some unnecessary memory.
2487 m_storage_mode = storage_mode_sparse;
2488 std::vector<int>().swap(m_unallocated_slots);
2489 std::vector<int>().swap(m_free_slots);
2490 std::vector<int>().swap(m_piece_to_slot);
2491 std::vector<int>().swap(m_slot_to_piece);
2494 int piece_manager::allocate_slot_for_piece(int piece_index)
2496 boost::recursive_mutex::scoped_lock lock(m_mutex);
2498 if (m_storage_mode != storage_mode_compact) return piece_index;
2500 INVARIANT_CHECK;
2502 TORRENT_ASSERT(piece_index >= 0);
2503 TORRENT_ASSERT(piece_index < (int)m_piece_to_slot.size());
2504 TORRENT_ASSERT(m_piece_to_slot.size() == m_slot_to_piece.size());
2506 int slot_index = m_piece_to_slot[piece_index];
2508 if (slot_index != has_no_slot)
2510 TORRENT_ASSERT(slot_index >= 0);
2511 TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
2512 return slot_index;
2515 if (m_free_slots.empty())
2517 allocate_slots(1);
2518 TORRENT_ASSERT(!m_free_slots.empty());
2521 std::vector<int>::iterator iter(
2522 std::find(
2523 m_free_slots.begin()
2524 , m_free_slots.end()
2525 , piece_index));
2527 if (iter == m_free_slots.end())
2529 TORRENT_ASSERT(m_slot_to_piece[piece_index] != unassigned);
2530 TORRENT_ASSERT(!m_free_slots.empty());
2531 iter = m_free_slots.end() - 1;
2533 // special case to make sure we don't use the last slot
2534 // when we shouldn't, since it's smaller than ordinary slots
2535 if (*iter == m_files.num_pieces() - 1 && piece_index != *iter)
2537 if (m_free_slots.size() == 1)
2538 allocate_slots(1);
2539 TORRENT_ASSERT(m_free_slots.size() > 1);
2540 // assumes that all allocated slots
2541 // are put at the end of the free_slots vector
2542 iter = m_free_slots.end() - 1;
2546 slot_index = *iter;
2547 m_free_slots.erase(iter);
2549 TORRENT_ASSERT(m_slot_to_piece[slot_index] == unassigned);
2551 m_slot_to_piece[slot_index] = piece_index;
2552 m_piece_to_slot[piece_index] = slot_index;
2554 // there is another piece already assigned to
2555 // the slot we are interested in, swap positions
2556 if (slot_index != piece_index
2557 && m_slot_to_piece[piece_index] >= 0)
2560 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2561 std::stringstream s;
2563 s << "there is another piece at our slot, swapping..";
2565 s << "\n piece_index: ";
2566 s << piece_index;
2567 s << "\n slot_index: ";
2568 s << slot_index;
2569 s << "\n piece at our slot: ";
2570 s << m_slot_to_piece[piece_index];
2571 s << "\n";
2573 print_to_log(s.str());
2574 debug_log();
2575 #endif
2577 int piece_at_our_slot = m_slot_to_piece[piece_index];
2578 TORRENT_ASSERT(m_piece_to_slot[piece_at_our_slot] == piece_index);
2580 std::swap(
2581 m_slot_to_piece[piece_index]
2582 , m_slot_to_piece[slot_index]);
2584 std::swap(
2585 m_piece_to_slot[piece_index]
2586 , m_piece_to_slot[piece_at_our_slot]);
2588 m_storage->move_slot(piece_index, slot_index);
2590 TORRENT_ASSERT(m_slot_to_piece[piece_index] == piece_index);
2591 TORRENT_ASSERT(m_piece_to_slot[piece_index] == piece_index);
2593 slot_index = piece_index;
2595 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2596 debug_log();
2597 #endif
2599 TORRENT_ASSERT(slot_index >= 0);
2600 TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
2602 if (m_free_slots.empty() && m_unallocated_slots.empty())
2603 switch_to_full_mode();
2605 return slot_index;
2608 bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk)
2610 boost::recursive_mutex::scoped_lock lock(m_mutex);
2611 TORRENT_ASSERT(num_slots > 0);
2613 INVARIANT_CHECK;
2615 TORRENT_ASSERT(!m_unallocated_slots.empty());
2616 TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
2618 bool written = false;
2620 for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i)
2622 // INVARIANT_CHECK;
2624 int pos = m_unallocated_slots.front();
2625 TORRENT_ASSERT(m_slot_to_piece[pos] == unallocated);
2626 TORRENT_ASSERT(m_piece_to_slot[pos] != pos);
2628 int new_free_slot = pos;
2629 if (m_piece_to_slot[pos] != has_no_slot)
2631 new_free_slot = m_piece_to_slot[pos];
2632 m_storage->move_slot(new_free_slot, pos);
2633 m_slot_to_piece[pos] = pos;
2634 m_piece_to_slot[pos] = pos;
2635 written = true;
2637 m_unallocated_slots.erase(m_unallocated_slots.begin());
2638 m_slot_to_piece[new_free_slot] = unassigned;
2639 m_free_slots.push_back(new_free_slot);
2640 if (abort_on_disk && written) break;
2643 TORRENT_ASSERT(m_free_slots.size() > 0);
2644 return written;
2647 int piece_manager::slot_for(int piece) const
2649 if (m_storage_mode != storage_mode_compact) return piece;
2650 TORRENT_ASSERT(piece < int(m_piece_to_slot.size()));
2651 TORRENT_ASSERT(piece >= 0);
2652 return m_piece_to_slot[piece];
2655 int piece_manager::piece_for(int slot) const
2657 if (m_storage_mode != storage_mode_compact) return slot;
2658 TORRENT_ASSERT(slot < int(m_slot_to_piece.size()));
2659 TORRENT_ASSERT(slot >= 0);
2660 return m_slot_to_piece[slot];
2663 #ifndef NDEBUG
2664 void piece_manager::check_invariant() const
2666 boost::recursive_mutex::scoped_lock lock(m_mutex);
2668 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2670 if (m_unallocated_slots.empty()
2671 && m_free_slots.empty()
2672 && m_state == state_finished)
2674 TORRENT_ASSERT(m_storage_mode != storage_mode_compact
2675 || m_files.num_pieces() == 0);
2678 if (m_storage_mode != storage_mode_compact)
2680 TORRENT_ASSERT(m_unallocated_slots.empty());
2681 TORRENT_ASSERT(m_free_slots.empty());
2684 if (m_storage_mode != storage_mode_compact
2685 && m_state != state_expand_pieces
2686 && m_state != state_full_check)
2688 TORRENT_ASSERT(m_piece_to_slot.empty());
2689 TORRENT_ASSERT(m_slot_to_piece.empty());
2691 else
2693 if (m_piece_to_slot.empty()) return;
2695 TORRENT_ASSERT((int)m_piece_to_slot.size() == m_files.num_pieces());
2696 TORRENT_ASSERT((int)m_slot_to_piece.size() == m_files.num_pieces());
2698 for (std::vector<int>::const_iterator i = m_free_slots.begin();
2699 i != m_free_slots.end(); ++i)
2701 TORRENT_ASSERT(*i < (int)m_slot_to_piece.size());
2702 TORRENT_ASSERT(*i >= 0);
2703 TORRENT_ASSERT(m_slot_to_piece[*i] == unassigned);
2704 TORRENT_ASSERT(std::find(i+1, m_free_slots.end(), *i)
2705 == m_free_slots.end());
2708 for (std::vector<int>::const_iterator i = m_unallocated_slots.begin();
2709 i != m_unallocated_slots.end(); ++i)
2711 TORRENT_ASSERT(*i < (int)m_slot_to_piece.size());
2712 TORRENT_ASSERT(*i >= 0);
2713 TORRENT_ASSERT(m_slot_to_piece[*i] == unallocated);
2714 TORRENT_ASSERT(std::find(i+1, m_unallocated_slots.end(), *i)
2715 == m_unallocated_slots.end());
2718 for (int i = 0; i < m_files.num_pieces(); ++i)
2720 // Check domain of piece_to_slot's elements
2721 if (m_piece_to_slot[i] != has_no_slot)
2723 TORRENT_ASSERT(m_piece_to_slot[i] >= 0);
2724 TORRENT_ASSERT(m_piece_to_slot[i] < (int)m_slot_to_piece.size());
2727 // Check domain of slot_to_piece's elements
2728 if (m_slot_to_piece[i] != unallocated
2729 && m_slot_to_piece[i] != unassigned)
2731 TORRENT_ASSERT(m_slot_to_piece[i] >= 0);
2732 TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
2735 // do more detailed checks on piece_to_slot
2736 if (m_piece_to_slot[i] >= 0)
2738 TORRENT_ASSERT(m_slot_to_piece[m_piece_to_slot[i]] == i);
2739 if (m_piece_to_slot[i] != i)
2741 TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
2744 else
2746 TORRENT_ASSERT(m_piece_to_slot[i] == has_no_slot);
2749 // do more detailed checks on slot_to_piece
2751 if (m_slot_to_piece[i] >= 0)
2753 TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
2754 TORRENT_ASSERT(m_piece_to_slot[m_slot_to_piece[i]] == i);
2755 #ifdef TORRENT_STORAGE_DEBUG
2756 TORRENT_ASSERT(
2757 std::find(
2758 m_unallocated_slots.begin()
2759 , m_unallocated_slots.end()
2760 , i) == m_unallocated_slots.end()
2762 TORRENT_ASSERT(
2763 std::find(
2764 m_free_slots.begin()
2765 , m_free_slots.end()
2766 , i) == m_free_slots.end()
2768 #endif
2770 else if (m_slot_to_piece[i] == unallocated)
2772 #ifdef TORRENT_STORAGE_DEBUG
2773 TORRENT_ASSERT(m_unallocated_slots.empty()
2774 || (std::find(
2775 m_unallocated_slots.begin()
2776 , m_unallocated_slots.end()
2777 , i) != m_unallocated_slots.end())
2779 #endif
2781 else if (m_slot_to_piece[i] == unassigned)
2783 #ifdef TORRENT_STORAGE_DEBUG
2784 TORRENT_ASSERT(
2785 std::find(
2786 m_free_slots.begin()
2787 , m_free_slots.end()
2788 , i) != m_free_slots.end()
2790 #endif
2792 else
2794 TORRENT_ASSERT(false && "m_slot_to_piece[i] is invalid");
2800 #ifdef TORRENT_STORAGE_DEBUG
2801 void piece_manager::debug_log() const
2803 std::stringstream s;
2805 s << "index\tslot\tpiece\n";
2807 for (int i = 0; i < m_files.num_pieces(); ++i)
2809 s << i << "\t" << m_slot_to_piece[i] << "\t";
2810 s << m_piece_to_slot[i] << "\n";
2813 s << "---------------------------------\n";
2815 print_to_log(s.str());
2817 #endif
2818 #endif
2819 } // namespace libtorrent