added missing file error_code.cpp
[libtorrent.git] / src / storage.cpp
blob6bdd1b5fc8038bfabe26389b782c788e789ba689
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 fs::path m_save_path;
440 // the file pool is typically stored in
441 // the session, to make all storage
442 // instances use the same pool
443 file_pool& m_pool;
445 // temporary storage for moving pieces
446 buffer m_scratch_buffer;
449 sha1_hash storage::hash_for_slot(int slot, partial_hash& ph, int piece_size)
451 #ifndef NDEBUG
452 hasher partial;
453 hasher whole;
454 int slot_size1 = piece_size;
455 m_scratch_buffer.resize(slot_size1);
456 read_impl(&m_scratch_buffer[0], slot, 0, slot_size1, true);
457 if (ph.offset > 0)
458 partial.update(&m_scratch_buffer[0], ph.offset);
459 whole.update(&m_scratch_buffer[0], slot_size1);
460 hasher partial_copy = ph.h;
461 TORRENT_ASSERT(ph.offset == 0 || partial_copy.final() == partial.final());
462 #endif
463 int slot_size = piece_size - ph.offset;
464 if (slot_size > 0)
466 m_scratch_buffer.resize(slot_size);
467 read_impl(&m_scratch_buffer[0], slot, ph.offset, slot_size, true);
468 ph.h.update(&m_scratch_buffer[0], slot_size);
470 #ifndef NDEBUG
471 sha1_hash ret = ph.h.final();
472 TORRENT_ASSERT(ret == whole.final());
473 return ret;
474 #else
475 return ph.h.final();
476 #endif
479 bool storage::initialize(bool allocate_files)
481 error_code ec;
482 // first, create all missing directories
483 fs::path last_path;
484 for (file_storage::iterator file_iter = files().begin(),
485 end_iter = files().end(); file_iter != end_iter; ++file_iter)
487 fs::path dir = (m_save_path / file_iter->path).branch_path();
489 if (dir != last_path)
492 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
493 last_path = dir;
494 if (!exists_win(last_path))
495 create_directories_win(last_path);
496 #elif TORRENT_USE_WPATH
497 last_path = dir;
498 fs::wpath wp = safe_convert(last_path.string());
499 if (!exists(wp))
500 create_directories(wp);
501 #else
502 last_path = dir;
503 if (!exists(last_path))
504 create_directories(last_path);
505 #endif
508 // if the file is empty, just create it. But also make sure
509 // the directory exists.
510 if (file_iter->size == 0)
512 file(m_save_path / file_iter->path, file::out, ec);
513 if (ec)
515 set_error(m_save_path / file_iter->path, ec);
516 return true;
518 continue;
521 #ifndef BOOST_NO_EXCEPTIONS
522 try {
523 #endif
524 if (allocate_files)
526 error_code ec;
527 boost::shared_ptr<file> f = m_pool.open_file(this
528 , m_save_path / file_iter->path, file::in | file::out, ec);
529 if (ec) set_error(m_save_path / file_iter->path, ec);
530 else if (f)
532 f->set_size(file_iter->size, ec);
533 if (ec) set_error(m_save_path / file_iter->path, ec);
536 #ifndef BOOST_NO_EXCEPTIONS
538 catch (std::exception& e)
540 set_error(m_save_path / file_iter->path
541 , error_code(errno, get_posix_category()));
542 return true;
544 #endif
546 // close files that were opened in write mode
547 m_pool.release(this);
548 return false;
551 bool storage::rename_file(int index, std::string const& new_filename)
553 if (index < 0 || index >= m_files.num_files()) return true;
554 fs::path old_name = m_save_path / files().at(index).path;
555 m_pool.release(old_name);
557 #if TORRENT_USE_WPATH
558 fs::wpath old_path = safe_convert(old_name.string());
559 fs::wpath new_path = safe_convert((m_save_path / new_filename).string());
560 #else
561 fs::path const& old_path = old_name;
562 fs::path new_path = m_save_path / new_filename;
563 #endif
565 #ifndef BOOST_NO_EXCEPTIONS
568 #endif
569 rename(old_path, new_path);
571 error_code ec;
572 rename(old_path, new_path, ec);
573 if (ec)
575 set_error(old_path, ec);
576 return;
579 if (!m_mapped_files)
580 { m_mapped_files.reset(new file_storage(m_files)); }
581 m_mapped_files->rename_file(index, new_filename);
582 #ifndef BOOST_NO_EXCEPTIONS
584 catch (std::exception& e)
586 set_error(old_name, error_code(errno, get_posix_category()));
587 return true;
589 #endif
590 return false;
593 bool storage::release_files()
595 m_pool.release(this);
596 buffer().swap(m_scratch_buffer);
597 return false;
600 bool storage::delete_files()
602 // make sure we don't have the files open
603 m_pool.release(this);
604 buffer().swap(m_scratch_buffer);
606 int error;
607 std::string error_file;
609 // delete the files from disk
610 std::set<std::string> directories;
611 typedef std::set<std::string>::iterator iter_t;
612 for (file_storage::iterator i = files().begin()
613 , end(files().end()); i != end; ++i)
615 std::string p = (m_save_path / i->path).string();
616 fs::path bp = i->path.branch_path();
617 std::pair<iter_t, bool> ret;
618 ret.second = true;
619 while (ret.second && !bp.empty())
621 std::pair<iter_t, bool> ret = directories.insert((m_save_path / bp).string());
622 bp = bp.branch_path();
624 #if TORRENT_USE_WPATH
626 { fs::remove(safe_convert(p)); }
627 catch (std::exception& e)
629 error = errno;
630 error_file = p;
632 #else
633 if (std::remove(p.c_str()) != 0 && errno != ENOENT)
635 error = errno;
636 error_file = p;
638 #endif
641 // remove the directories. Reverse order to delete
642 // subdirectories first
644 for (std::set<std::string>::reverse_iterator i = directories.rbegin()
645 , end(directories.rend()); i != end; ++i)
647 #if TORRENT_USE_WPATH
649 { fs::remove(safe_convert(*i)); }
650 catch (std::exception& e)
652 error = errno;
653 error_file = *i;
655 #else
656 if (std::remove(i->c_str()) != 0 && errno != ENOENT)
658 error = errno;
659 error_file = *i;
661 #endif
664 if (error)
666 m_error = error_code(error, get_posix_category());
667 m_error_file.swap(error_file);
668 return true;
670 return false;
673 bool storage::write_resume_data(entry& rd) const
675 TORRENT_ASSERT(rd.type() == entry::dictionary_t);
677 std::vector<std::pair<size_type, std::time_t> > file_sizes
678 = get_filesizes(files(), m_save_path);
680 entry::list_type& fl = rd["file sizes"].list();
681 for (std::vector<std::pair<size_type, std::time_t> >::iterator i
682 = file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
684 entry::list_type p;
685 p.push_back(entry(i->first));
686 p.push_back(entry(i->second));
687 fl.push_back(entry(p));
689 return false;
692 bool storage::verify_resume_data(lazy_entry const& rd, std::string& error)
694 if (rd.type() != lazy_entry::dict_t)
696 error = "invalid fastresume file (not a dictionary)";
697 return true;
700 std::vector<std::pair<size_type, std::time_t> > file_sizes;
701 lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes");
702 if (file_sizes_ent == 0)
704 error = "missing or invalid 'file sizes' entry in resume data";
705 return false;
708 for (int i = 0; i < file_sizes_ent->list_size(); ++i)
710 lazy_entry const* e = file_sizes_ent->list_at(i);
711 if (e->type() != lazy_entry::list_t
712 || e->list_size() != 2
713 || e->list_at(0)->type() != lazy_entry::int_t
714 || e->list_at(1)->type() != lazy_entry::int_t)
715 continue;
716 file_sizes.push_back(std::pair<size_type, std::time_t>(
717 e->list_int_value_at(0), std::time_t(e->list_int_value_at(1))));
720 if (file_sizes.empty())
722 error = "the number of files in resume data is 0";
723 return false;
726 bool seed = false;
728 lazy_entry const* slots = rd.dict_find_list("slots");
729 if (slots)
731 if (int(slots->list_size()) == m_files.num_pieces())
733 seed = true;
734 for (int i = 0; i < slots->list_size(); ++i)
736 lazy_entry const* e = slots->list_at(i);
737 if (e->list_int_value_at(i, -1) >= 0) continue;
738 seed = false;
739 break;
743 else if (lazy_entry const* pieces = rd.dict_find_string("pieces"))
745 if (int(pieces->string_length()) == m_files.num_pieces())
747 seed = true;
748 char const* p = pieces->string_ptr();
749 for (int i = 0; i < pieces->string_length(); ++i)
751 if ((p[i] & 1) == 1) continue;
752 seed = false;
753 break;
757 else
759 error = "missing 'slots' and 'pieces' entry in resume data";
760 return false;
763 bool full_allocation_mode = false;
764 if (rd.dict_find_string_value("allocation") != "compact")
765 full_allocation_mode = true;
767 if (seed)
769 if (files().num_files() != (int)file_sizes.size())
771 error = "the number of files does not match the torrent (num: "
772 + boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
773 + boost::lexical_cast<std::string>(files().num_files()) + ")";
774 return false;
777 std::vector<std::pair<size_type, std::time_t> >::iterator
778 fs = file_sizes.begin();
779 // the resume data says we have the entire torrent
780 // make sure the file sizes are the right ones
781 for (file_storage::iterator i = files().begin()
782 , end(files().end()); i != end; ++i, ++fs)
784 if (i->size != fs->first)
786 error = "file size for '" + i->path.native_file_string()
787 + "' was expected to be "
788 + boost::lexical_cast<std::string>(i->size) + " bytes";
789 return false;
794 return match_filesizes(files(), m_save_path, file_sizes
795 , !full_allocation_mode, &error);
798 // returns true on success
799 bool storage::move_storage(fs::path save_path)
801 #if TORRENT_USE_WPATH
802 fs::wpath old_path;
803 fs::wpath new_path;
804 #else
805 fs::path old_path;
806 fs::path new_path;
807 #endif
809 save_path = complete(save_path);
811 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
812 std::wstring wsave_path(safe_convert(save_path.native_file_string()));
813 if (!exists_win(save_path))
814 CreateDirectory(wsave_path.c_str(), 0);
815 else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0)
816 return false;
817 #elif TORRENT_USE_WPATH
818 fs::wpath wp = safe_convert(save_path.string());
819 if (!exists(wp))
820 create_directory(wp);
821 else if (!is_directory(wp))
822 return false;
823 #else
824 if (!exists(save_path))
825 create_directory(save_path);
826 else if (!is_directory(save_path))
827 return false;
828 #endif
830 m_pool.release(this);
832 #if TORRENT_USE_WPATH
833 old_path = safe_convert((m_save_path / files().name()).string());
834 new_path = safe_convert((save_path / files().name()).string());
835 #else
836 old_path = m_save_path / files().name();
837 new_path = save_path / files().name();
838 #endif
840 #ifndef BOOST_NO_EXCEPTIONS
843 #endif
844 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
845 rename_win(old_path, new_path);
846 #else
847 rename(old_path, new_path);
848 #endif
849 m_save_path = save_path;
850 return true;
851 #ifndef BOOST_NO_EXCEPTIONS
853 catch (std::exception& e)
855 error_code ec;
856 recursive_copy(old_path, new_path, ec);
857 if (ec)
859 set_error(m_save_path / files().name(), ec);
860 return true;
862 m_save_path = save_path;
863 recursive_remove(old_path);
865 #endif
866 return false;
869 #ifndef NDEBUG
871 void storage::shuffle()
873 int num_pieces = files().num_pieces();
875 std::vector<int> pieces(num_pieces);
876 for (std::vector<int>::iterator i = pieces.begin();
877 i != pieces.end(); ++i)
879 *i = static_cast<int>(i - pieces.begin());
881 std::srand((unsigned int)std::time(0));
882 std::vector<int> targets(pieces);
883 std::random_shuffle(pieces.begin(), pieces.end());
884 std::random_shuffle(targets.begin(), targets.end());
886 for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i)
888 const int slot_index = targets[i];
889 const int piece_index = pieces[i];
890 const int slot_size =static_cast<int>(m_files.piece_size(slot_index));
891 std::vector<char> buf(slot_size);
892 read(&buf[0], piece_index, 0, slot_size);
893 write(&buf[0], slot_index, 0, slot_size);
897 #endif
899 bool storage::move_slot(int src_slot, int dst_slot)
901 int piece_size = m_files.piece_size(dst_slot);
902 m_scratch_buffer.resize(piece_size);
903 int ret1 = read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true);
904 int ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
905 return ret1 != piece_size || ret2 != piece_size;
908 bool storage::swap_slots(int slot1, int slot2)
910 // the size of the target slot is the size of the piece
911 int piece_size = m_files.piece_length();
912 int piece1_size = m_files.piece_size(slot2);
913 int piece2_size = m_files.piece_size(slot1);
914 m_scratch_buffer.resize(piece_size * 2);
915 int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
916 int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
917 int ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
918 int ret4 = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size);
919 return ret1 != piece1_size || ret2 != piece2_size
920 || ret3 != piece1_size || ret4 != piece2_size;
923 bool storage::swap_slots3(int slot1, int slot2, int slot3)
925 // the size of the target slot is the size of the piece
926 int piece_size = m_files.piece_length();
927 int piece1_size = m_files.piece_size(slot2);
928 int piece2_size = m_files.piece_size(slot3);
929 int piece3_size = m_files.piece_size(slot1);
930 m_scratch_buffer.resize(piece_size * 2);
931 int ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
932 int ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
933 int ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
934 int ret4 = read_impl(&m_scratch_buffer[0], slot3, 0, piece3_size, true);
935 int ret5 = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size);
936 int ret6 = write(&m_scratch_buffer[0], slot1, 0, piece3_size);
937 return ret1 != piece1_size || ret2 != piece2_size
938 || ret3 != piece1_size || ret4 != piece3_size
939 || ret5 != piece2_size || ret6 != piece3_size;
942 int storage::read(
943 char* buf
944 , int slot
945 , int offset
946 , int size)
948 return read_impl(buf, slot, offset, size, false);
951 int storage::read_impl(
952 char* buf
953 , int slot
954 , int offset
955 , int size
956 , bool fill_zero)
958 TORRENT_ASSERT(buf != 0);
959 TORRENT_ASSERT(slot >= 0 && slot < m_files.num_pieces());
960 TORRENT_ASSERT(offset >= 0);
961 TORRENT_ASSERT(offset < m_files.piece_size(slot));
962 TORRENT_ASSERT(size > 0);
964 #ifndef NDEBUG
965 std::vector<file_slice> slices
966 = files().map_block(slot, offset, size);
967 TORRENT_ASSERT(!slices.empty());
968 #endif
970 size_type start = slot * (size_type)m_files.piece_length() + offset;
971 TORRENT_ASSERT(start + size <= m_files.total_size());
973 // find the file iterator and file offset
974 size_type file_offset = start;
975 std::vector<file_entry>::const_iterator file_iter;
977 for (file_iter = files().begin();;)
979 if (file_offset < file_iter->size)
980 break;
982 file_offset -= file_iter->size;
983 ++file_iter;
986 int buf_pos = 0;
987 error_code ec;
988 boost::shared_ptr<file> in(m_pool.open_file(
989 this, m_save_path / file_iter->path, file::in, ec));
990 if (!in || ec)
992 set_error(m_save_path / file_iter->path, ec);
993 return -1;
995 TORRENT_ASSERT(file_offset < file_iter->size);
996 TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base);
998 size_type new_pos = in->seek(file_offset + file_iter->file_base, file::begin, ec);
999 if (new_pos != file_offset + file_iter->file_base || ec)
1001 // the file was not big enough
1002 if (!fill_zero)
1004 set_error(m_save_path / file_iter->path, ec);
1005 return -1;
1007 std::memset(buf + buf_pos, 0, size - buf_pos);
1008 return size;
1011 #ifndef NDEBUG
1012 size_type in_tell = in->tell(ec);
1013 TORRENT_ASSERT(in_tell == file_offset + file_iter->file_base && !ec);
1014 #endif
1016 int left_to_read = size;
1017 int slot_size = static_cast<int>(m_files.piece_size(slot));
1019 if (offset + left_to_read > slot_size)
1020 left_to_read = slot_size - offset;
1022 TORRENT_ASSERT(left_to_read >= 0);
1024 size_type result = left_to_read;
1026 #ifndef NDEBUG
1027 int counter = 0;
1028 #endif
1030 while (left_to_read > 0)
1032 int read_bytes = left_to_read;
1033 if (file_offset + read_bytes > file_iter->size)
1034 read_bytes = static_cast<int>(file_iter->size - file_offset);
1036 if (read_bytes > 0)
1038 #ifndef NDEBUG
1039 TORRENT_ASSERT(int(slices.size()) > counter);
1040 size_type slice_size = slices[counter].size;
1041 TORRENT_ASSERT(slice_size == read_bytes);
1042 TORRENT_ASSERT(files().at(slices[counter].file_index).path
1043 == file_iter->path);
1044 #endif
1046 int actual_read = int(in->read(buf + buf_pos, read_bytes, ec));
1048 if (read_bytes != actual_read || ec)
1050 // the file was not big enough
1051 if (actual_read > 0) buf_pos += actual_read;
1052 if (!fill_zero)
1054 set_error(m_save_path / file_iter->path, ec);
1055 return -1;
1057 std::memset(buf + buf_pos, 0, size - buf_pos);
1058 return size;
1061 left_to_read -= read_bytes;
1062 buf_pos += read_bytes;
1063 TORRENT_ASSERT(buf_pos >= 0);
1064 file_offset += read_bytes;
1067 if (left_to_read > 0)
1069 ++file_iter;
1070 #ifndef NDEBUG
1071 // empty files are not returned by map_block, so if
1072 // this file was empty, don't increment the slice counter
1073 if (read_bytes > 0) ++counter;
1074 #endif
1075 fs::path path = m_save_path / file_iter->path;
1077 file_offset = 0;
1078 error_code ec;
1079 in = m_pool.open_file( this, path, file::in, ec);
1080 if (!in || ec)
1082 set_error(path, ec);
1083 return -1;
1085 size_type pos = in->seek(file_iter->file_base, file::begin, ec);
1086 if (pos != file_iter->file_base || ec)
1088 if (!fill_zero)
1090 set_error(m_save_path / file_iter->path, ec);
1091 return -1;
1093 std::memset(buf + buf_pos, 0, size - buf_pos);
1094 return size;
1098 return result;
1101 int storage::write(
1102 const char* buf
1103 , int slot
1104 , int offset
1105 , int size)
1107 TORRENT_ASSERT(buf != 0);
1108 TORRENT_ASSERT(slot >= 0);
1109 TORRENT_ASSERT(slot < m_files.num_pieces());
1110 TORRENT_ASSERT(offset >= 0);
1111 TORRENT_ASSERT(size > 0);
1113 #ifndef NDEBUG
1114 std::vector<file_slice> slices
1115 = files().map_block(slot, offset, size);
1116 TORRENT_ASSERT(!slices.empty());
1117 #endif
1119 size_type start = slot * (size_type)m_files.piece_length() + offset;
1121 // find the file iterator and file offset
1122 size_type file_offset = start;
1123 std::vector<file_entry>::const_iterator file_iter;
1125 for (file_iter = files().begin();;)
1127 if (file_offset < file_iter->size)
1128 break;
1130 file_offset -= file_iter->size;
1131 ++file_iter;
1132 TORRENT_ASSERT(file_iter != files().end());
1135 fs::path p(m_save_path / file_iter->path);
1136 error_code ec;
1137 boost::shared_ptr<file> out = m_pool.open_file(
1138 this, p, file::out | file::in, ec);
1140 if (!out || ec)
1142 set_error(p, ec);
1143 return -1;
1145 TORRENT_ASSERT(file_offset < file_iter->size);
1146 TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base);
1148 size_type pos = out->seek(file_offset + file_iter->file_base, file::begin, ec);
1150 if (pos != file_offset + file_iter->file_base || ec)
1152 set_error(p, ec);
1153 return -1;
1156 int left_to_write = size;
1157 int slot_size = static_cast<int>(m_files.piece_size(slot));
1159 if (offset + left_to_write > slot_size)
1160 left_to_write = slot_size - offset;
1162 TORRENT_ASSERT(left_to_write >= 0);
1164 int buf_pos = 0;
1165 #ifndef NDEBUG
1166 int counter = 0;
1167 #endif
1168 while (left_to_write > 0)
1170 int write_bytes = left_to_write;
1171 if (file_offset + write_bytes > file_iter->size)
1173 TORRENT_ASSERT(file_iter->size >= file_offset);
1174 write_bytes = static_cast<int>(file_iter->size - file_offset);
1177 if (write_bytes > 0)
1179 TORRENT_ASSERT(int(slices.size()) > counter);
1180 TORRENT_ASSERT(slices[counter].size == write_bytes);
1181 TORRENT_ASSERT(files().at(slices[counter].file_index).path
1182 == file_iter->path);
1184 TORRENT_ASSERT(buf_pos >= 0);
1185 TORRENT_ASSERT(write_bytes >= 0);
1186 error_code ec;
1187 size_type written = out->write(buf + buf_pos, write_bytes, ec);
1189 if (written != write_bytes || ec)
1191 set_error(m_save_path / file_iter->path, ec);
1192 return -1;
1195 left_to_write -= write_bytes;
1196 buf_pos += write_bytes;
1197 TORRENT_ASSERT(buf_pos >= 0);
1198 file_offset += write_bytes;
1199 TORRENT_ASSERT(file_offset <= file_iter->size);
1202 if (left_to_write > 0)
1204 #ifndef NDEBUG
1205 if (write_bytes > 0) ++counter;
1206 #endif
1207 ++file_iter;
1209 TORRENT_ASSERT(file_iter != files().end());
1210 fs::path p = m_save_path / file_iter->path;
1211 file_offset = 0;
1212 error_code ec;
1213 out = m_pool.open_file(
1214 this, p, file::out | file::in, ec);
1216 if (!out || ec)
1218 set_error(p, ec);
1219 return -1;
1222 size_type pos = out->seek(file_iter->file_base, file::begin, ec);
1224 if (pos != file_iter->file_base || ec)
1226 set_error(p, ec);
1227 return -1;
1231 return size;
1234 storage_interface* default_storage_constructor(file_storage const& fs
1235 , fs::path const& path, file_pool& fp)
1237 return new storage(fs, path, fp);
1240 // -- piece_manager -----------------------------------------------------
1242 piece_manager::piece_manager(
1243 boost::shared_ptr<void> const& torrent
1244 , boost::intrusive_ptr<torrent_info const> info
1245 , fs::path const& save_path
1246 , file_pool& fp
1247 , disk_io_thread& io
1248 , storage_constructor_type sc
1249 , storage_mode_t sm)
1250 : m_info(info)
1251 , m_files(m_info->files())
1252 , m_storage(sc(m_files, save_path, fp))
1253 , m_storage_mode(sm)
1254 , m_save_path(complete(save_path))
1255 , m_state(state_none)
1256 , m_current_slot(0)
1257 , m_out_of_place(false)
1258 , m_scratch_piece(-1)
1259 , m_storage_constructor(sc)
1260 , m_io_thread(io)
1261 , m_torrent(torrent)
1265 piece_manager::~piece_manager()
1269 void piece_manager::async_save_resume_data(
1270 boost::function<void(int, disk_io_job const&)> const& handler)
1272 disk_io_job j;
1273 j.storage = this;
1274 j.action = disk_io_job::save_resume_data;
1275 m_io_thread.add_job(j, handler);
1278 void piece_manager::async_release_files(
1279 boost::function<void(int, disk_io_job const&)> const& handler)
1281 disk_io_job j;
1282 j.storage = this;
1283 j.action = disk_io_job::release_files;
1284 m_io_thread.add_job(j, handler);
1287 void piece_manager::async_delete_files(
1288 boost::function<void(int, disk_io_job const&)> const& handler)
1290 disk_io_job j;
1291 j.storage = this;
1292 j.action = disk_io_job::delete_files;
1293 m_io_thread.add_job(j, handler);
1296 void piece_manager::async_move_storage(fs::path const& p
1297 , boost::function<void(int, disk_io_job const&)> const& handler)
1299 disk_io_job j;
1300 j.storage = this;
1301 j.action = disk_io_job::move_storage;
1302 j.str = p.string();
1303 m_io_thread.add_job(j, handler);
1306 void piece_manager::async_check_fastresume(lazy_entry const* resume_data
1307 , boost::function<void(int, disk_io_job const&)> const& handler)
1309 TORRENT_ASSERT(resume_data != 0);
1310 disk_io_job j;
1311 j.storage = this;
1312 j.action = disk_io_job::check_fastresume;
1313 j.buffer = (char*)resume_data;
1314 m_io_thread.add_job(j, handler);
1317 void piece_manager::async_rename_file(int index, std::string const& name
1318 , boost::function<void(int, disk_io_job const&)> const& handler)
1320 disk_io_job j;
1321 j.storage = this;
1322 j.piece = index;
1323 j.str = name;
1324 j.action = disk_io_job::rename_file;
1325 m_io_thread.add_job(j, handler);
1328 void piece_manager::async_check_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::check_files;
1334 m_io_thread.add_job(j, handler);
1337 void piece_manager::async_read(
1338 peer_request const& r
1339 , boost::function<void(int, disk_io_job const&)> const& handler
1340 , int priority)
1342 disk_io_job j;
1343 j.storage = this;
1344 j.action = disk_io_job::read;
1345 j.piece = r.piece;
1346 j.offset = r.start;
1347 j.buffer_size = r.length;
1348 j.buffer = 0;
1349 j.priority = priority;
1350 // if a buffer is not specified, only one block can be read
1351 // since that is the size of the pool allocator's buffers
1352 TORRENT_ASSERT(r.length <= 16 * 1024);
1353 m_io_thread.add_job(j, handler);
1354 #ifndef NDEBUG
1355 boost::recursive_mutex::scoped_lock l(m_mutex);
1356 // if this assert is hit, it suggests
1357 // that check_files was not successful
1358 TORRENT_ASSERT(slot_for(r.piece) >= 0);
1359 #endif
1362 void piece_manager::async_write(
1363 peer_request const& r
1364 , disk_buffer_holder& buffer
1365 , boost::function<void(int, disk_io_job const&)> const& handler)
1367 TORRENT_ASSERT(r.length <= 16 * 1024);
1368 // the buffer needs to be allocated through the io_thread
1369 TORRENT_ASSERT(m_io_thread.is_disk_buffer(buffer.get()));
1371 disk_io_job j;
1372 j.storage = this;
1373 j.action = disk_io_job::write;
1374 j.piece = r.piece;
1375 j.offset = r.start;
1376 j.buffer_size = r.length;
1377 j.buffer = buffer.get();
1378 m_io_thread.add_job(j, handler);
1379 buffer.release();
1382 void piece_manager::async_hash(int piece
1383 , boost::function<void(int, disk_io_job const&)> const& handler)
1385 disk_io_job j;
1386 j.storage = this;
1387 j.action = disk_io_job::hash;
1388 j.piece = piece;
1390 m_io_thread.add_job(j, handler);
1393 fs::path piece_manager::save_path() const
1395 boost::recursive_mutex::scoped_lock l(m_mutex);
1396 return m_save_path;
1399 sha1_hash piece_manager::hash_for_piece_impl(int piece)
1401 partial_hash ph;
1403 std::map<int, partial_hash>::iterator i = m_piece_hasher.find(piece);
1404 if (i != m_piece_hasher.end())
1406 ph = i->second;
1407 m_piece_hasher.erase(i);
1410 int slot = slot_for(piece);
1411 TORRENT_ASSERT(slot != has_no_slot);
1412 return m_storage->hash_for_slot(slot, ph, m_files.piece_size(piece));
1415 int piece_manager::move_storage_impl(fs::path const& save_path)
1417 if (m_storage->move_storage(save_path))
1419 m_save_path = fs::complete(save_path);
1420 return 0;
1422 return -1;
1425 void piece_manager::write_resume_data(entry& rd) const
1427 boost::recursive_mutex::scoped_lock lock(m_mutex);
1429 INVARIANT_CHECK;
1431 m_storage->write_resume_data(rd);
1433 if (m_storage_mode == storage_mode_compact)
1435 entry::list_type& slots = rd["slots"].list();
1436 slots.clear();
1437 std::vector<int>::const_reverse_iterator last;
1438 for (last = m_slot_to_piece.rbegin();
1439 last != m_slot_to_piece.rend(); ++last)
1441 if (*last != unallocated) break;
1444 for (std::vector<int>::const_iterator i =
1445 m_slot_to_piece.begin();
1446 i != last.base(); ++i)
1448 slots.push_back((*i >= 0) ? *i : unassigned);
1452 rd["allocation"] = m_storage_mode == storage_mode_sparse?"sparse"
1453 :m_storage_mode == storage_mode_allocate?"full":"compact";
1456 void piece_manager::mark_failed(int piece_index)
1458 INVARIANT_CHECK;
1460 if (m_storage_mode != storage_mode_compact) return;
1462 TORRENT_ASSERT(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size());
1463 int slot_index = m_piece_to_slot[piece_index];
1464 TORRENT_ASSERT(slot_index >= 0);
1466 m_slot_to_piece[slot_index] = unassigned;
1467 m_piece_to_slot[piece_index] = has_no_slot;
1468 m_free_slots.push_back(slot_index);
1471 int piece_manager::read_impl(
1472 char* buf
1473 , int piece_index
1474 , int offset
1475 , int size)
1477 TORRENT_ASSERT(buf);
1478 TORRENT_ASSERT(offset >= 0);
1479 TORRENT_ASSERT(size > 0);
1480 int slot = slot_for(piece_index);
1481 return m_storage->read(buf, slot, offset, size);
1484 int piece_manager::write_impl(
1485 const char* buf
1486 , int piece_index
1487 , int offset
1488 , int size)
1490 TORRENT_ASSERT(buf);
1491 TORRENT_ASSERT(offset >= 0);
1492 TORRENT_ASSERT(size > 0);
1493 TORRENT_ASSERT(piece_index >= 0 && piece_index < m_files.num_pieces());
1495 int slot = allocate_slot_for_piece(piece_index);
1496 int ret = m_storage->write(buf, slot, offset, size);
1497 // only save the partial hash if the write succeeds
1498 if (ret != size) return ret;
1500 // std::ofstream out("partial_hash.log", std::ios::app);
1502 if (offset == 0)
1504 partial_hash& ph = m_piece_hasher[piece_index];
1505 TORRENT_ASSERT(ph.offset == 0);
1506 ph.offset = size;
1507 ph.h.update(buf, size);
1509 out << time_now_string() << " NEW ["
1510 " s: " << this
1511 << " p: " << piece_index
1512 << " off: " << offset
1513 << " size: " << size
1514 << " entries: " << m_piece_hasher.size()
1515 << " ]" << std::endl;
1518 else
1520 std::map<int, partial_hash>::iterator i = m_piece_hasher.find(piece_index);
1521 if (i != m_piece_hasher.end())
1523 #ifndef NDEBUG
1524 TORRENT_ASSERT(i->second.offset > 0);
1525 int hash_offset = i->second.offset;
1526 TORRENT_ASSERT(offset >= hash_offset);
1527 #endif
1528 if (offset == i->second.offset)
1531 out << time_now_string() << " UPDATING ["
1532 " s: " << this
1533 << " p: " << piece_index
1534 << " off: " << offset
1535 << " size: " << size
1536 << " entries: " << m_piece_hasher.size()
1537 << " ]" << std::endl;
1539 i->second.offset += size;
1540 i->second.h.update(buf, size);
1542 /* else
1544 out << time_now_string() << " SKIPPING (out of order) ["
1545 " s: " << this
1546 << " p: " << piece_index
1547 << " off: " << offset
1548 << " size: " << size
1549 << " entries: " << m_piece_hasher.size()
1550 << " ]" << std::endl;
1552 */ }
1553 /* else
1555 out << time_now_string() << " SKIPPING (no entry) ["
1556 " s: " << this
1557 << " p: " << piece_index
1558 << " off: " << offset
1559 << " size: " << size
1560 << " entries: " << m_piece_hasher.size()
1561 << " ]" << std::endl;
1566 return ret;
1569 int piece_manager::identify_data(
1570 const std::vector<char>& piece_data
1571 , int current_slot)
1573 // INVARIANT_CHECK;
1575 const int piece_size = static_cast<int>(m_files.piece_length());
1576 const int last_piece_size = static_cast<int>(m_files.piece_size(
1577 m_files.num_pieces() - 1));
1579 TORRENT_ASSERT((int)piece_data.size() >= last_piece_size);
1581 // calculate a small digest, with the same
1582 // size as the last piece. And a large digest
1583 // which has the same size as a normal piece
1584 hasher small_digest;
1585 small_digest.update(&piece_data[0], last_piece_size);
1586 hasher large_digest(small_digest);
1587 TORRENT_ASSERT(piece_size - last_piece_size >= 0);
1588 if (piece_size - last_piece_size > 0)
1590 large_digest.update(
1591 &piece_data[last_piece_size]
1592 , piece_size - last_piece_size);
1594 sha1_hash large_hash = large_digest.final();
1595 sha1_hash small_hash = small_digest.final();
1597 typedef std::multimap<sha1_hash, int>::const_iterator map_iter;
1598 map_iter begin1;
1599 map_iter end1;
1600 map_iter begin2;
1601 map_iter end2;
1603 // makes the lookups for the small digest and the large digest
1604 boost::tie(begin1, end1) = m_hash_to_piece.equal_range(small_hash);
1605 boost::tie(begin2, end2) = m_hash_to_piece.equal_range(large_hash);
1607 // copy all potential piece indices into this vector
1608 std::vector<int> matching_pieces;
1609 for (map_iter i = begin1; i != end1; ++i)
1610 matching_pieces.push_back(i->second);
1611 for (map_iter i = begin2; i != end2; ++i)
1612 matching_pieces.push_back(i->second);
1614 // no piece matched the data in the slot
1615 if (matching_pieces.empty())
1616 return unassigned;
1618 // ------------------------------------------
1619 // CHECK IF THE PIECE IS IN ITS CORRECT PLACE
1620 // ------------------------------------------
1622 if (std::find(
1623 matching_pieces.begin()
1624 , matching_pieces.end()
1625 , current_slot) != matching_pieces.end())
1627 // the current slot is among the matching pieces, so
1628 // we will assume that the piece is in the right place
1629 const int piece_index = current_slot;
1631 int other_slot = m_piece_to_slot[piece_index];
1632 if (other_slot >= 0)
1634 // we have already found a piece with
1635 // this index.
1637 // take one of the other matching pieces
1638 // that hasn't already been assigned
1639 int other_piece = -1;
1640 for (std::vector<int>::iterator i = matching_pieces.begin();
1641 i != matching_pieces.end(); ++i)
1643 if (m_piece_to_slot[*i] >= 0 || *i == piece_index) continue;
1644 other_piece = *i;
1645 break;
1647 if (other_piece >= 0)
1649 // replace the old slot with 'other_piece'
1650 m_slot_to_piece[other_slot] = other_piece;
1651 m_piece_to_slot[other_piece] = other_slot;
1653 else
1655 // this index is the only piece with this
1656 // hash. The previous slot we found with
1657 // this hash must be the same piece. Mark
1658 // that piece as unassigned, since this slot
1659 // is the correct place for the piece.
1660 m_slot_to_piece[other_slot] = unassigned;
1661 if (m_storage_mode == storage_mode_compact)
1662 m_free_slots.push_back(other_slot);
1664 TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot);
1665 TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0);
1666 m_piece_to_slot[piece_index] = has_no_slot;
1669 TORRENT_ASSERT(m_piece_to_slot[piece_index] == has_no_slot);
1671 return piece_index;
1674 // find a matching piece that hasn't
1675 // already been assigned
1676 int free_piece = unassigned;
1677 for (std::vector<int>::iterator i = matching_pieces.begin();
1678 i != matching_pieces.end(); ++i)
1680 if (m_piece_to_slot[*i] >= 0) continue;
1681 free_piece = *i;
1682 break;
1685 if (free_piece >= 0)
1687 TORRENT_ASSERT(m_piece_to_slot[free_piece] == has_no_slot);
1688 return free_piece;
1690 else
1692 TORRENT_ASSERT(free_piece == unassigned);
1693 return unassigned;
1697 int piece_manager::check_no_fastresume(std::string& error)
1699 file_storage::iterator i = m_files.begin();
1700 file_storage::iterator end = m_files.end();
1702 for (; i != end; ++i)
1704 bool file_exists = false;
1705 fs::path f = m_save_path / i->path;
1706 #ifndef BOOST_NO_EXCEPTIONS
1709 #endif
1710 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
1711 file_exists = exists_win(f);
1712 #elif TORRENT_USE_WPATH
1713 fs::wpath wf = safe_convert(f.string());
1714 file_exists = exists(wf);
1715 #else
1716 file_exists = exists(f);
1717 #endif
1718 #ifndef BOOST_NO_EXCEPTIONS
1720 catch (std::exception& e)
1722 error = f.string();
1723 error += ": ";
1724 error += e.what();
1725 TORRENT_ASSERT(!error.empty());
1726 return fatal_disk_error;
1728 #endif
1729 if (file_exists && i->size > 0)
1731 m_state = state_full_check;
1732 m_piece_to_slot.clear();
1733 m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
1734 m_slot_to_piece.clear();
1735 m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
1736 if (m_storage_mode == storage_mode_compact)
1738 m_unallocated_slots.clear();
1739 m_free_slots.clear();
1741 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
1742 return need_full_check;
1746 if (m_storage_mode == storage_mode_compact)
1748 // in compact mode without checking, we need to
1749 // populate the unallocated list
1750 TORRENT_ASSERT(m_unallocated_slots.empty());
1751 for (int i = 0, end(m_files.num_pieces()); i < end; ++i)
1752 m_unallocated_slots.push_back(i);
1753 m_piece_to_slot.clear();
1754 m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot);
1755 m_slot_to_piece.clear();
1756 m_slot_to_piece.resize(m_files.num_pieces(), unallocated);
1759 return check_init_storage(error);
1762 int piece_manager::check_init_storage(std::string& error)
1764 if (m_storage->initialize(m_storage_mode == storage_mode_allocate))
1766 error = m_storage->error().message();
1767 TORRENT_ASSERT(!error.empty());
1768 return fatal_disk_error;
1770 m_state = state_finished;
1771 buffer().swap(m_scratch_buffer);
1772 buffer().swap(m_scratch_buffer2);
1773 if (m_storage_mode != storage_mode_compact)
1775 // if no piece is out of place
1776 // since we're in full allocation mode, we can
1777 // forget the piece allocation tables
1778 std::vector<int>().swap(m_piece_to_slot);
1779 std::vector<int>().swap(m_slot_to_piece);
1780 std::vector<int>().swap(m_free_slots);
1781 std::vector<int>().swap(m_unallocated_slots);
1783 return no_error;
1786 // check if the fastresume data is up to date
1787 // if it is, use it and return true. If it
1788 // isn't return false and the full check
1789 // will be run
1790 int piece_manager::check_fastresume(
1791 lazy_entry const& rd, std::string& error)
1793 boost::recursive_mutex::scoped_lock lock(m_mutex);
1795 INVARIANT_CHECK;
1797 TORRENT_ASSERT(m_files.piece_length() > 0);
1799 // if we don't have any resume data, return
1800 if (rd.type() == lazy_entry::none_t) return check_no_fastresume(error);
1802 if (rd.type() != lazy_entry::dict_t)
1804 error = "invalid fastresume data (not a dictionary)";
1805 return check_no_fastresume(error);
1808 int block_size = (std::min)(16 * 1024, m_files.piece_length());
1809 int blocks_per_piece = rd.dict_find_int_value("blocks per piece", -1);
1810 if (blocks_per_piece != -1
1811 && blocks_per_piece != m_files.piece_length() / block_size)
1813 error = "invalid 'blocks per piece' entry";
1814 return check_no_fastresume(error);
1817 storage_mode_t storage_mode = storage_mode_compact;
1818 if (rd.dict_find_string_value("allocation") != "compact")
1819 storage_mode = storage_mode_sparse;
1821 if (!m_storage->verify_resume_data(rd, error))
1822 return check_no_fastresume(error);
1824 // assume no piece is out of place (i.e. in a slot
1825 // other than the one it should be in)
1826 bool out_of_place = false;
1828 // if we don't have a piece map, we need the slots
1829 // if we're in compact mode, we also need the slots map
1830 if (storage_mode == storage_mode_compact || rd.dict_find("pieces") == 0)
1832 // read slots map
1833 lazy_entry const* slots = rd.dict_find_list("slots");
1834 if (slots == 0)
1836 error = "missing slot list";
1837 return check_no_fastresume(error);
1840 if ((int)slots->list_size() > m_files.num_pieces())
1842 error = "file has more slots than torrent (slots: "
1843 + boost::lexical_cast<std::string>(slots->list_size()) + " size: "
1844 + boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
1845 return check_no_fastresume(error);
1848 if (m_storage_mode == storage_mode_compact)
1850 int num_pieces = int(m_files.num_pieces());
1851 m_slot_to_piece.resize(num_pieces, unallocated);
1852 m_piece_to_slot.resize(num_pieces, has_no_slot);
1853 for (int i = 0; i < slots->list_size(); ++i)
1855 lazy_entry const* e = slots->list_at(i);
1856 if (e->type() != lazy_entry::int_t)
1858 error = "invalid entry type in slot list";
1859 return check_no_fastresume(error);
1862 int index = int(e->int_value());
1863 if (index >= num_pieces || index < -2)
1865 error = "too high index number in slot map (index: "
1866 + boost::lexical_cast<std::string>(index) + " size: "
1867 + boost::lexical_cast<std::string>(num_pieces) + ")";
1868 return check_no_fastresume(error);
1870 if (index >= 0)
1872 m_slot_to_piece[i] = index;
1873 m_piece_to_slot[index] = i;
1874 if (i != index) out_of_place = true;
1876 else if (index == unassigned)
1878 if (m_storage_mode == storage_mode_compact)
1879 m_free_slots.push_back(i);
1881 else
1883 TORRENT_ASSERT(index == unallocated);
1884 if (m_storage_mode == storage_mode_compact)
1885 m_unallocated_slots.push_back(i);
1889 else
1891 for (int i = 0; i < slots->list_size(); ++i)
1893 lazy_entry const* e = slots->list_at(i);
1894 if (e->type() != lazy_entry::int_t)
1896 error = "invalid entry type in slot list";
1897 return check_no_fastresume(error);
1900 int index = int(e->int_value());
1901 if (index != i && index >= 0)
1903 error = "invalid slot index";
1904 return check_no_fastresume(error);
1909 // This will corrupt the storage
1910 // use while debugging to find
1911 // states that cannot be scanned
1912 // by check_pieces.
1913 // m_storage->shuffle();
1915 if (m_storage_mode == storage_mode_compact)
1917 if (m_unallocated_slots.empty()) switch_to_full_mode();
1919 else
1921 TORRENT_ASSERT(m_free_slots.empty());
1922 TORRENT_ASSERT(m_unallocated_slots.empty());
1924 if (out_of_place)
1926 // in this case we're in full allocation mode, but
1927 // we're resuming a compact allocated storage
1928 m_state = state_expand_pieces;
1929 m_current_slot = 0;
1930 error = "pieces needs to be reordered";
1931 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
1932 return need_full_check;
1937 else if (m_storage_mode == storage_mode_compact)
1939 // read piece map
1940 lazy_entry const* pieces = rd.dict_find("pieces");
1941 if (pieces == 0 || pieces->type() != lazy_entry::string_t)
1943 error = "missing pieces entry";
1944 return check_no_fastresume(error);
1947 if ((int)pieces->string_length() != m_files.num_pieces())
1949 error = "file has more slots than torrent (slots: "
1950 + boost::lexical_cast<std::string>(pieces->string_length()) + " size: "
1951 + boost::lexical_cast<std::string>(m_files.num_pieces()) + " )";
1952 return check_no_fastresume(error);
1955 int num_pieces = int(m_files.num_pieces());
1956 m_slot_to_piece.resize(num_pieces, unallocated);
1957 m_piece_to_slot.resize(num_pieces, has_no_slot);
1958 char const* have_pieces = pieces->string_ptr();
1959 for (int i = 0; i < num_pieces; ++i)
1961 if (have_pieces[i] & 1)
1963 m_slot_to_piece[i] = i;
1964 m_piece_to_slot[i] = i;
1966 else
1968 m_free_slots.push_back(i);
1971 if (m_unallocated_slots.empty()) switch_to_full_mode();
1974 return check_init_storage(error);
1978 state chart:
1980 check_fastresume() ----------+
1982 | | |
1983 | v v
1984 | +------------+ +---------------+
1985 | | full_check |-->| expand_pieses |
1986 | +------------+ +---------------+
1987 | | |
1988 | v |
1989 | +--------------+ |
1990 +->| finished | <------+
1991 +--------------+
1995 // performs the full check and full allocation
1996 // (if necessary). returns true if finished and
1997 // false if it should be called again
1998 // the second return value is the progress the
1999 // file check is at. 0 is nothing done, and 1
2000 // is finished
2001 int piece_manager::check_files(int& current_slot, int& have_piece, std::string& error)
2003 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2005 current_slot = m_current_slot;
2006 have_piece = -1;
2007 if (m_state == state_expand_pieces)
2009 INVARIANT_CHECK;
2011 if (m_scratch_piece >= 0)
2013 int piece = m_scratch_piece;
2014 int other_piece = m_slot_to_piece[piece];
2015 m_scratch_piece = -1;
2017 if (other_piece >= 0)
2019 if (m_scratch_buffer2.empty())
2020 m_scratch_buffer2.resize(m_files.piece_length());
2022 int piece_size = m_files.piece_size(other_piece);
2023 if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size)
2024 != piece_size)
2026 error = m_storage->error().message();
2027 TORRENT_ASSERT(!error.empty());
2028 return fatal_disk_error;
2030 m_scratch_piece = other_piece;
2031 m_piece_to_slot[other_piece] = unassigned;
2034 // the slot where this piece belongs is
2035 // free. Just move the piece there.
2036 int piece_size = m_files.piece_size(piece);
2037 if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
2039 error = m_storage->error().message();
2040 TORRENT_ASSERT(!error.empty());
2041 return fatal_disk_error;
2043 m_piece_to_slot[piece] = piece;
2044 m_slot_to_piece[piece] = piece;
2046 if (other_piece >= 0)
2047 m_scratch_buffer.swap(m_scratch_buffer2);
2049 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2050 return need_full_check;
2053 while (m_current_slot < m_files.num_pieces()
2054 && (m_slot_to_piece[m_current_slot] == m_current_slot
2055 || m_slot_to_piece[m_current_slot] < 0))
2057 ++m_current_slot;
2060 if (m_current_slot == m_files.num_pieces())
2062 return check_init_storage(error);
2065 TORRENT_ASSERT(m_current_slot < m_files.num_pieces());
2067 int piece = m_slot_to_piece[m_current_slot];
2068 TORRENT_ASSERT(piece >= 0);
2069 int other_piece = m_slot_to_piece[piece];
2070 if (other_piece >= 0)
2072 // there is another piece in the slot
2073 // where this one goes. Store it in the scratch
2074 // buffer until next iteration.
2075 if (m_scratch_buffer.empty())
2076 m_scratch_buffer.resize(m_files.piece_length());
2078 int piece_size = m_files.piece_size(other_piece);
2079 if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
2081 error = m_storage->error().message();
2082 TORRENT_ASSERT(!error.empty());
2083 return fatal_disk_error;
2085 m_scratch_piece = other_piece;
2086 m_piece_to_slot[other_piece] = unassigned;
2089 // the slot where this piece belongs is
2090 // free. Just move the piece there.
2091 m_storage->move_slot(m_current_slot, piece);
2092 m_piece_to_slot[piece] = piece;
2093 m_slot_to_piece[m_current_slot] = unassigned;
2094 m_slot_to_piece[piece] = piece;
2096 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2097 return need_full_check;
2100 TORRENT_ASSERT(m_state == state_full_check);
2102 int skip = check_one_piece(have_piece);
2103 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2105 if (skip == -1)
2107 error = m_storage->error().message();
2108 TORRENT_ASSERT(!error.empty());
2109 return fatal_disk_error;
2112 if (skip)
2114 clear_error();
2115 // skip means that the piece we checked failed to be read from disk
2116 // completely. We should skip all pieces belonging to that file.
2117 // find the file that failed, and skip all the pieces in that file
2118 size_type file_offset = 0;
2119 size_type current_offset = size_type(m_current_slot) * m_files.piece_length();
2120 for (file_storage::iterator i = m_files.begin()
2121 , end(m_files.end()); i != end; ++i)
2123 file_offset += i->size;
2124 if (file_offset > current_offset) break;
2127 TORRENT_ASSERT(file_offset > current_offset);
2128 int skip_blocks = static_cast<int>(
2129 (file_offset - current_offset + m_files.piece_length() - 1)
2130 / m_files.piece_length());
2132 if (m_storage_mode == storage_mode_compact)
2134 for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i)
2136 TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
2137 m_unallocated_slots.push_back(i);
2141 // current slot will increase by one at the end of the for-loop too
2142 m_current_slot += skip_blocks - 1;
2143 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2146 ++m_current_slot;
2147 current_slot = m_current_slot;
2149 if (m_current_slot >= m_files.num_pieces())
2151 TORRENT_ASSERT(m_current_slot == m_files.num_pieces());
2153 // clear the memory we've been using
2154 std::vector<char>().swap(m_piece_data);
2155 std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
2157 if (m_storage_mode != storage_mode_compact)
2159 if (!m_out_of_place)
2161 // if no piece is out of place
2162 // since we're in full allocation mode, we can
2163 // forget the piece allocation tables
2165 std::vector<int>().swap(m_piece_to_slot);
2166 std::vector<int>().swap(m_slot_to_piece);
2167 return check_init_storage(error);
2169 else
2171 // in this case we're in full allocation mode, but
2172 // we're resuming a compact allocated storage
2173 m_state = state_expand_pieces;
2174 m_current_slot = 0;
2175 current_slot = m_current_slot;
2176 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2177 return need_full_check;
2180 else if (m_unallocated_slots.empty())
2182 switch_to_full_mode();
2184 return check_init_storage(error);
2187 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2188 return need_full_check;
2191 // -1=error 0=ok 1=skip
2192 int piece_manager::check_one_piece(int& have_piece)
2194 // ------------------------
2195 // DO THE FULL CHECK
2196 // ------------------------
2198 TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces());
2199 TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_files.num_pieces());
2200 TORRENT_ASSERT(have_piece == -1);
2202 // initialization for the full check
2203 if (m_hash_to_piece.empty())
2205 for (int i = 0; i < m_files.num_pieces(); ++i)
2206 m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i));
2209 m_piece_data.resize(int(m_files.piece_length()));
2210 int piece_size = m_files.piece_size(m_current_slot);
2211 int num_read = m_storage->read(&m_piece_data[0]
2212 , m_current_slot, 0, piece_size);
2214 if (num_read < 0)
2216 if (m_storage->error()
2217 && m_storage->error() != error_code(ENOENT, get_posix_category()))
2219 std::cerr << m_storage->error().message() << std::endl;
2220 return -1;
2222 return 1;
2225 // if the file is incomplete, skip the rest of it
2226 if (num_read != piece_size)
2227 return 1;
2229 int piece_index = identify_data(m_piece_data, m_current_slot);
2231 if (piece_index >= 0) have_piece = piece_index;
2233 if (piece_index != m_current_slot
2234 && piece_index >= 0)
2235 m_out_of_place = true;
2237 TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0);
2239 const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
2240 const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot;
2242 // check if this piece should be swapped with any other slot
2243 // this section will ensure that the storage is correctly sorted
2244 // libtorrent will never leave the storage in a state that
2245 // requires this sorting, but other clients may.
2247 // example of worst case:
2248 // | m_current_slot = 5
2249 // V
2250 // +---+- - - +---+- - - +---+- -
2251 // | x | | 5 | | 3 | <- piece data in slots
2252 // +---+- - - +---+- - - +---+- -
2253 // 3 y 5 <- slot index
2255 // in this example, the data in the m_current_slot (5)
2256 // is piece 3. It has to be moved into slot 3. The data
2257 // in slot y (piece 5) should be moved into the m_current_slot.
2258 // and the data in slot 3 (piece x) should be moved to slot y.
2260 // there are three possible cases.
2261 // 1. There's another piece that should be placed into this slot
2262 // 2. This piece should be placed into another slot.
2263 // 3. There's another piece that should be placed into this slot
2264 // and this piece should be placed into another slot
2266 // swap piece_index with this slot
2268 // case 1
2269 if (this_should_move && !other_should_move)
2271 TORRENT_ASSERT(piece_index != m_current_slot);
2273 const int other_slot = piece_index;
2274 TORRENT_ASSERT(other_slot >= 0);
2275 int other_piece = m_slot_to_piece[other_slot];
2277 m_slot_to_piece[other_slot] = piece_index;
2278 m_slot_to_piece[m_current_slot] = other_piece;
2279 m_piece_to_slot[piece_index] = piece_index;
2280 if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot;
2282 if (other_piece == unassigned)
2284 std::vector<int>::iterator i =
2285 std::find(m_free_slots.begin(), m_free_slots.end(), other_slot);
2286 TORRENT_ASSERT(i != m_free_slots.end());
2287 if (m_storage_mode == storage_mode_compact)
2289 m_free_slots.erase(i);
2290 m_free_slots.push_back(m_current_slot);
2294 bool ret = false;
2295 if (other_piece >= 0)
2296 ret |= m_storage->swap_slots(other_slot, m_current_slot);
2297 else
2298 ret |= m_storage->move_slot(m_current_slot, other_slot);
2300 if (ret) return 1;
2302 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2303 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2305 // case 2
2306 else if (!this_should_move && other_should_move)
2308 TORRENT_ASSERT(piece_index != m_current_slot);
2310 const int other_piece = m_current_slot;
2311 const int other_slot = m_piece_to_slot[other_piece];
2312 TORRENT_ASSERT(other_slot >= 0);
2314 m_slot_to_piece[m_current_slot] = other_piece;
2315 m_slot_to_piece[other_slot] = piece_index;
2316 m_piece_to_slot[other_piece] = m_current_slot;
2318 if (piece_index == unassigned
2319 && m_storage_mode == storage_mode_compact)
2320 m_free_slots.push_back(other_slot);
2322 bool ret = false;
2323 if (piece_index >= 0)
2325 m_piece_to_slot[piece_index] = other_slot;
2326 ret |= m_storage->swap_slots(other_slot, m_current_slot);
2328 else
2330 ret |= m_storage->move_slot(other_slot, m_current_slot);
2333 if (ret) return 1;
2335 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2336 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2338 else if (this_should_move && other_should_move)
2340 TORRENT_ASSERT(piece_index != m_current_slot);
2341 TORRENT_ASSERT(piece_index >= 0);
2343 const int piece1 = m_slot_to_piece[piece_index];
2344 const int piece2 = m_current_slot;
2345 const int slot1 = piece_index;
2346 const int slot2 = m_piece_to_slot[piece2];
2348 TORRENT_ASSERT(slot1 >= 0);
2349 TORRENT_ASSERT(slot2 >= 0);
2350 TORRENT_ASSERT(piece2 >= 0);
2352 if (slot1 == slot2)
2354 // this means there are only two pieces involved in the swap
2355 TORRENT_ASSERT(piece1 >= 0);
2357 // movement diagram:
2358 // +-------------------------------+
2359 // | |
2360 // +--> slot1 --> m_current_slot --+
2362 m_slot_to_piece[slot1] = piece_index;
2363 m_slot_to_piece[m_current_slot] = piece1;
2365 m_piece_to_slot[piece_index] = slot1;
2366 m_piece_to_slot[piece1] = m_current_slot;
2368 TORRENT_ASSERT(piece1 == m_current_slot);
2369 TORRENT_ASSERT(piece_index == slot1);
2371 m_storage->swap_slots(m_current_slot, slot1);
2373 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2374 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2376 else
2378 TORRENT_ASSERT(slot1 != slot2);
2379 TORRENT_ASSERT(piece1 != piece2);
2381 // movement diagram:
2382 // +-----------------------------------------+
2383 // | |
2384 // +--> slot1 --> slot2 --> m_current_slot --+
2386 m_slot_to_piece[slot1] = piece_index;
2387 m_slot_to_piece[slot2] = piece1;
2388 m_slot_to_piece[m_current_slot] = piece2;
2390 m_piece_to_slot[piece_index] = slot1;
2391 m_piece_to_slot[m_current_slot] = piece2;
2393 if (piece1 == unassigned)
2395 std::vector<int>::iterator i =
2396 std::find(m_free_slots.begin(), m_free_slots.end(), slot1);
2397 TORRENT_ASSERT(i != m_free_slots.end());
2398 if (m_storage_mode == storage_mode_compact)
2400 m_free_slots.erase(i);
2401 m_free_slots.push_back(slot2);
2405 bool ret = false;
2406 if (piece1 >= 0)
2408 m_piece_to_slot[piece1] = slot2;
2409 ret |= m_storage->swap_slots3(m_current_slot, slot1, slot2);
2411 else
2413 ret |= m_storage->move_slot(m_current_slot, slot1);
2414 ret |= m_storage->move_slot(slot2, m_current_slot);
2417 if (ret) return 1;
2419 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2420 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2423 else
2425 TORRENT_ASSERT(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot);
2426 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unallocated);
2427 TORRENT_ASSERT(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot);
2429 // the slot was identified as piece 'piece_index'
2430 if (piece_index != unassigned)
2431 m_piece_to_slot[piece_index] = m_current_slot;
2432 else if (m_storage_mode == storage_mode_compact)
2433 m_free_slots.push_back(m_current_slot);
2435 m_slot_to_piece[m_current_slot] = piece_index;
2437 TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
2438 || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
2440 return 0;
2443 void piece_manager::switch_to_full_mode()
2445 TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
2446 TORRENT_ASSERT(m_unallocated_slots.empty());
2447 // we have allocated all slots, switch to
2448 // full allocation mode in order to free
2449 // some unnecessary memory.
2450 m_storage_mode = storage_mode_sparse;
2451 std::vector<int>().swap(m_unallocated_slots);
2452 std::vector<int>().swap(m_free_slots);
2453 std::vector<int>().swap(m_piece_to_slot);
2454 std::vector<int>().swap(m_slot_to_piece);
2457 int piece_manager::allocate_slot_for_piece(int piece_index)
2459 boost::recursive_mutex::scoped_lock lock(m_mutex);
2461 if (m_storage_mode != storage_mode_compact) return piece_index;
2463 INVARIANT_CHECK;
2465 TORRENT_ASSERT(piece_index >= 0);
2466 TORRENT_ASSERT(piece_index < (int)m_piece_to_slot.size());
2467 TORRENT_ASSERT(m_piece_to_slot.size() == m_slot_to_piece.size());
2469 int slot_index = m_piece_to_slot[piece_index];
2471 if (slot_index != has_no_slot)
2473 TORRENT_ASSERT(slot_index >= 0);
2474 TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
2475 return slot_index;
2478 if (m_free_slots.empty())
2480 allocate_slots(1);
2481 TORRENT_ASSERT(!m_free_slots.empty());
2484 std::vector<int>::iterator iter(
2485 std::find(
2486 m_free_slots.begin()
2487 , m_free_slots.end()
2488 , piece_index));
2490 if (iter == m_free_slots.end())
2492 TORRENT_ASSERT(m_slot_to_piece[piece_index] != unassigned);
2493 TORRENT_ASSERT(!m_free_slots.empty());
2494 iter = m_free_slots.end() - 1;
2496 // special case to make sure we don't use the last slot
2497 // when we shouldn't, since it's smaller than ordinary slots
2498 if (*iter == m_files.num_pieces() - 1 && piece_index != *iter)
2500 if (m_free_slots.size() == 1)
2501 allocate_slots(1);
2502 TORRENT_ASSERT(m_free_slots.size() > 1);
2503 // assumes that all allocated slots
2504 // are put at the end of the free_slots vector
2505 iter = m_free_slots.end() - 1;
2509 slot_index = *iter;
2510 m_free_slots.erase(iter);
2512 TORRENT_ASSERT(m_slot_to_piece[slot_index] == unassigned);
2514 m_slot_to_piece[slot_index] = piece_index;
2515 m_piece_to_slot[piece_index] = slot_index;
2517 // there is another piece already assigned to
2518 // the slot we are interested in, swap positions
2519 if (slot_index != piece_index
2520 && m_slot_to_piece[piece_index] >= 0)
2523 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2524 std::stringstream s;
2526 s << "there is another piece at our slot, swapping..";
2528 s << "\n piece_index: ";
2529 s << piece_index;
2530 s << "\n slot_index: ";
2531 s << slot_index;
2532 s << "\n piece at our slot: ";
2533 s << m_slot_to_piece[piece_index];
2534 s << "\n";
2536 print_to_log(s.str());
2537 debug_log();
2538 #endif
2540 int piece_at_our_slot = m_slot_to_piece[piece_index];
2541 TORRENT_ASSERT(m_piece_to_slot[piece_at_our_slot] == piece_index);
2543 std::swap(
2544 m_slot_to_piece[piece_index]
2545 , m_slot_to_piece[slot_index]);
2547 std::swap(
2548 m_piece_to_slot[piece_index]
2549 , m_piece_to_slot[piece_at_our_slot]);
2551 m_storage->move_slot(piece_index, slot_index);
2553 TORRENT_ASSERT(m_slot_to_piece[piece_index] == piece_index);
2554 TORRENT_ASSERT(m_piece_to_slot[piece_index] == piece_index);
2556 slot_index = piece_index;
2558 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2559 debug_log();
2560 #endif
2562 TORRENT_ASSERT(slot_index >= 0);
2563 TORRENT_ASSERT(slot_index < (int)m_slot_to_piece.size());
2565 if (m_free_slots.empty() && m_unallocated_slots.empty())
2566 switch_to_full_mode();
2568 return slot_index;
2571 bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk)
2573 boost::recursive_mutex::scoped_lock lock(m_mutex);
2574 TORRENT_ASSERT(num_slots > 0);
2576 INVARIANT_CHECK;
2578 TORRENT_ASSERT(!m_unallocated_slots.empty());
2579 TORRENT_ASSERT(m_storage_mode == storage_mode_compact);
2581 bool written = false;
2583 for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i)
2585 // INVARIANT_CHECK;
2587 int pos = m_unallocated_slots.front();
2588 TORRENT_ASSERT(m_slot_to_piece[pos] == unallocated);
2589 TORRENT_ASSERT(m_piece_to_slot[pos] != pos);
2591 int new_free_slot = pos;
2592 if (m_piece_to_slot[pos] != has_no_slot)
2594 new_free_slot = m_piece_to_slot[pos];
2595 m_storage->move_slot(new_free_slot, pos);
2596 m_slot_to_piece[pos] = pos;
2597 m_piece_to_slot[pos] = pos;
2598 written = true;
2600 m_unallocated_slots.erase(m_unallocated_slots.begin());
2601 m_slot_to_piece[new_free_slot] = unassigned;
2602 m_free_slots.push_back(new_free_slot);
2603 if (abort_on_disk && written) break;
2606 TORRENT_ASSERT(m_free_slots.size() > 0);
2607 return written;
2610 int piece_manager::slot_for(int piece) const
2612 if (m_storage_mode != storage_mode_compact) return piece;
2613 TORRENT_ASSERT(piece < int(m_piece_to_slot.size()));
2614 TORRENT_ASSERT(piece >= 0);
2615 return m_piece_to_slot[piece];
2618 int piece_manager::piece_for(int slot) const
2620 if (m_storage_mode != storage_mode_compact) return slot;
2621 TORRENT_ASSERT(slot < int(m_slot_to_piece.size()));
2622 TORRENT_ASSERT(slot >= 0);
2623 return m_slot_to_piece[slot];
2626 #ifndef NDEBUG
2627 void piece_manager::check_invariant() const
2629 boost::recursive_mutex::scoped_lock lock(m_mutex);
2631 TORRENT_ASSERT(m_current_slot <= m_files.num_pieces());
2633 if (m_unallocated_slots.empty()
2634 && m_free_slots.empty()
2635 && m_state == state_finished)
2637 TORRENT_ASSERT(m_storage_mode != storage_mode_compact
2638 || m_files.num_pieces() == 0);
2641 if (m_storage_mode != storage_mode_compact)
2643 TORRENT_ASSERT(m_unallocated_slots.empty());
2644 TORRENT_ASSERT(m_free_slots.empty());
2647 if (m_storage_mode != storage_mode_compact
2648 && m_state != state_expand_pieces
2649 && m_state != state_full_check)
2651 TORRENT_ASSERT(m_piece_to_slot.empty());
2652 TORRENT_ASSERT(m_slot_to_piece.empty());
2654 else
2656 if (m_piece_to_slot.empty()) return;
2658 TORRENT_ASSERT((int)m_piece_to_slot.size() == m_files.num_pieces());
2659 TORRENT_ASSERT((int)m_slot_to_piece.size() == m_files.num_pieces());
2661 for (std::vector<int>::const_iterator i = m_free_slots.begin();
2662 i != m_free_slots.end(); ++i)
2664 TORRENT_ASSERT(*i < (int)m_slot_to_piece.size());
2665 TORRENT_ASSERT(*i >= 0);
2666 TORRENT_ASSERT(m_slot_to_piece[*i] == unassigned);
2667 TORRENT_ASSERT(std::find(i+1, m_free_slots.end(), *i)
2668 == m_free_slots.end());
2671 for (std::vector<int>::const_iterator i = m_unallocated_slots.begin();
2672 i != m_unallocated_slots.end(); ++i)
2674 TORRENT_ASSERT(*i < (int)m_slot_to_piece.size());
2675 TORRENT_ASSERT(*i >= 0);
2676 TORRENT_ASSERT(m_slot_to_piece[*i] == unallocated);
2677 TORRENT_ASSERT(std::find(i+1, m_unallocated_slots.end(), *i)
2678 == m_unallocated_slots.end());
2681 for (int i = 0; i < m_files.num_pieces(); ++i)
2683 // Check domain of piece_to_slot's elements
2684 if (m_piece_to_slot[i] != has_no_slot)
2686 TORRENT_ASSERT(m_piece_to_slot[i] >= 0);
2687 TORRENT_ASSERT(m_piece_to_slot[i] < (int)m_slot_to_piece.size());
2690 // Check domain of slot_to_piece's elements
2691 if (m_slot_to_piece[i] != unallocated
2692 && m_slot_to_piece[i] != unassigned)
2694 TORRENT_ASSERT(m_slot_to_piece[i] >= 0);
2695 TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
2698 // do more detailed checks on piece_to_slot
2699 if (m_piece_to_slot[i] >= 0)
2701 TORRENT_ASSERT(m_slot_to_piece[m_piece_to_slot[i]] == i);
2702 if (m_piece_to_slot[i] != i)
2704 TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
2707 else
2709 TORRENT_ASSERT(m_piece_to_slot[i] == has_no_slot);
2712 // do more detailed checks on slot_to_piece
2714 if (m_slot_to_piece[i] >= 0)
2716 TORRENT_ASSERT(m_slot_to_piece[i] < (int)m_piece_to_slot.size());
2717 TORRENT_ASSERT(m_piece_to_slot[m_slot_to_piece[i]] == i);
2718 #ifdef TORRENT_STORAGE_DEBUG
2719 TORRENT_ASSERT(
2720 std::find(
2721 m_unallocated_slots.begin()
2722 , m_unallocated_slots.end()
2723 , i) == m_unallocated_slots.end()
2725 TORRENT_ASSERT(
2726 std::find(
2727 m_free_slots.begin()
2728 , m_free_slots.end()
2729 , i) == m_free_slots.end()
2731 #endif
2733 else if (m_slot_to_piece[i] == unallocated)
2735 #ifdef TORRENT_STORAGE_DEBUG
2736 TORRENT_ASSERT(m_unallocated_slots.empty()
2737 || (std::find(
2738 m_unallocated_slots.begin()
2739 , m_unallocated_slots.end()
2740 , i) != m_unallocated_slots.end())
2742 #endif
2744 else if (m_slot_to_piece[i] == unassigned)
2746 #ifdef TORRENT_STORAGE_DEBUG
2747 TORRENT_ASSERT(
2748 std::find(
2749 m_free_slots.begin()
2750 , m_free_slots.end()
2751 , i) != m_free_slots.end()
2753 #endif
2755 else
2757 TORRENT_ASSERT(false && "m_slot_to_piece[i] is invalid");
2763 #ifdef TORRENT_STORAGE_DEBUG
2764 void piece_manager::debug_log() const
2766 std::stringstream s;
2768 s << "index\tslot\tpiece\n";
2770 for (int i = 0; i < m_files.num_pieces(); ++i)
2772 s << i << "\t" << m_slot_to_piece[i] << "\t";
2773 s << m_piece_to_slot[i] << "\n";
2776 s << "---------------------------------\n";
2778 print_to_log(s.str());
2780 #endif
2781 #endif
2782 } // namespace libtorrent