3 Copyright (c) 2003, Arvid Norberg, Daniel Wallin
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
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"
42 #pragma warning(push, 1)
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>
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"
79 #if defined(__APPLE__)
84 #include <sys/param.h>
85 #include <sys/mount.h>
88 #if defined(__linux__)
89 #include <sys/statfs.h>
92 #if defined(__FreeBSD__)
94 #include <sys/param.h>
95 #include <sys/mount.h>
104 #include <boost/filesystem/exception.hpp>
105 #include "libtorrent/utf8.hpp"
106 #include "libtorrent/buffer.hpp"
110 std::wstring
safe_convert(std::string
const& s
)
114 return libtorrent::utf8_wchar(s
);
116 catch (std::exception
)
119 const char* end
= &s
[0] + s
.size();
120 for (const char* i
= &s
[0]; i
< end
;)
123 int result
= std::mbtowc(&c
, i
, end
- i
);
124 if (result
> 0) i
+= result
;
134 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
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
) );
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);
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)
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))
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
227 #if BOOST_VERSION < 103200
228 bool operator<(fs::path
const& lhs
, fs::path
const& rhs
)
230 return lhs
.string() < rhs
.string();
234 namespace fs
= boost::filesystem
;
236 using namespace ::boost::multi_index
;
237 using boost::multi_index::multi_index_container
;
239 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
242 using namespace libtorrent
;
244 void print_to_log(const std::string
& s
)
246 static std::ofstream
log("log.txt");
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
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
);
274 copy_file(old_path
, new_path
);
276 #ifndef BOOST_NO_EXCEPTIONS
277 } catch (std::exception
& e
) { ec
= error_code(errno
, get_posix_category()); }
281 template <class Path
>
282 void recursive_remove(Path
const& old_path
)
284 using boost::filesystem::basic_directory_iterator
;
285 #ifndef BOOST_NO_EXCEPTIONS
288 if (is_directory(old_path
))
290 for (basic_directory_iterator
<Path
> i(old_path
), end
; i
!= end
; ++i
)
291 recursive_remove(i
->path());
298 #ifndef BOOST_NO_EXCEPTIONS
299 } catch (std::exception
& e
) {}
302 std::vector
<std::pair
<size_type
, std::time_t> > get_filesizes(
303 file_storage
const& s
, fs::path 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
)
311 std::time_t time
= 0;
312 #if TORRENT_USE_WPATH
313 fs::wpath f
= safe_convert((p
/ i
->path
).string());
315 fs::path f
= p
/ i
->path
;
317 #ifndef BOOST_NO_EXCEPTIONS
324 time
= last_write_time(f
);
326 #ifndef BOOST_NO_EXCEPTIONS
327 catch (std::exception
&) {}
329 sizes
.push_back(std::make_pair(size
, time
));
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
343 , std::vector
<std::pair
<size_type
, std::time_t> > const& sizes
345 , std::string
* error
)
347 if ((int)sizes
.size() != fs
.num_files())
349 if (error
) *error
= "mismatching number of files";
354 std::vector
<std::pair
<size_type
, std::time_t> >::const_iterator s
356 for (file_storage::iterator i
= fs
.begin()
357 , end(fs
.end());i
!= end
; ++i
, ++s
)
360 std::time_t time
= 0;
362 #if TORRENT_USE_WPATH
363 fs::wpath f
= safe_convert((p
/ i
->path
).string());
365 fs::path f
= p
/ i
->path
;
367 #ifndef BOOST_NO_EXCEPTIONS
374 time
= last_write_time(f
);
376 #ifndef BOOST_NO_EXCEPTIONS
377 catch (std::exception
&) {}
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
)
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
);
403 class storage
: public storage_interface
, boost::noncopyable
406 storage(file_storage
const& fs
, fs::path
const& path
, file_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();
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
);
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
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
)
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);
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());
463 int slot_size
= piece_size
- ph
.offset
;
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
);
471 sha1_hash ret
= ph
.h
.final();
472 TORRENT_ASSERT(ret
== whole
.final());
479 bool storage::initialize(bool allocate_files
)
482 // first, create all missing directories
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
494 if (!exists_win(last_path
))
495 create_directories_win(last_path
);
496 #elif TORRENT_USE_WPATH
498 fs::wpath wp
= safe_convert(last_path
.string());
500 create_directories(wp
);
503 if (!exists(last_path
))
504 create_directories(last_path
);
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
);
515 set_error(m_save_path
/ file_iter
->path
, ec
);
521 #ifndef BOOST_NO_EXCEPTIONS
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
);
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()));
546 // close files that were opened in write mode
547 m_pool
.release(this);
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());
561 fs::path
const& old_path
= old_name
;
562 fs::path new_path
= m_save_path
/ new_filename
;
565 #ifndef BOOST_NO_EXCEPTIONS
569 rename(old_path
, new_path
);
572 rename(old_path, new_path, ec);
575 set_error(old_path, ec);
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()));
593 bool storage::release_files()
595 m_pool
.release(this);
596 buffer().swap(m_scratch_buffer
);
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
);
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
;
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
)
633 if (std::remove(p
.c_str()) != 0 && errno
!= ENOENT
)
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
)
656 if (std::remove(i
->c_str()) != 0 && errno
!= ENOENT
)
666 m_error
= error_code(error
, get_posix_category());
667 m_error_file
.swap(error_file
);
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
)
685 p
.push_back(entry(i
->first
));
686 p
.push_back(entry(i
->second
));
687 fl
.push_back(entry(p
));
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)";
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";
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
)
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";
728 lazy_entry
const* slots
= rd
.dict_find_list("slots");
731 if (int(slots
->list_size()) == m_files
.num_pieces())
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;
743 else if (lazy_entry
const* pieces
= rd
.dict_find_string("pieces"))
745 if (int(pieces
->string_length()) == m_files
.num_pieces())
748 char const* p
= pieces
->string_ptr();
749 for (int i
= 0; i
< pieces
->string_length(); ++i
)
751 if ((p
[i
] & 1) == 1) continue;
759 error
= "missing 'slots' and 'pieces' entry in resume data";
763 bool full_allocation_mode
= false;
764 if (rd
.dict_find_string_value("allocation") != "compact")
765 full_allocation_mode
= true;
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()) + ")";
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";
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
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)
817 #elif TORRENT_USE_WPATH
818 fs::wpath wp
= safe_convert(save_path
.string());
820 create_directory(wp
);
821 else if (!is_directory(wp
))
824 if (!exists(save_path
))
825 create_directory(save_path
);
826 else if (!is_directory(save_path
))
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());
836 old_path
= m_save_path
/ files().name();
837 new_path
= save_path
/ files().name();
840 #ifndef BOOST_NO_EXCEPTIONS
844 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
845 rename_win(old_path
, new_path
);
847 rename(old_path
, new_path
);
849 m_save_path
= save_path
;
851 #ifndef BOOST_NO_EXCEPTIONS
853 catch (std::exception
& e
)
856 recursive_copy(old_path
, new_path
, ec
);
859 set_error(m_save_path
/ files().name(), ec
);
862 m_save_path
= save_path
;
863 recursive_remove(old_path
);
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);
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
;
948 return read_impl(buf
, slot
, offset
, size
, false);
951 int storage::read_impl(
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);
965 std::vector
<file_slice
> slices
966 = files().map_block(slot
, offset
, size
);
967 TORRENT_ASSERT(!slices
.empty());
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
)
982 file_offset
-= file_iter
->size
;
988 boost::shared_ptr
<file
> in(m_pool
.open_file(
989 this, m_save_path
/ file_iter
->path
, file::in
, ec
));
992 set_error(m_save_path
/ file_iter
->path
, ec
);
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
1004 set_error(m_save_path
/ file_iter
->path
, ec
);
1007 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
1012 size_type in_tell
= in
->tell(ec
);
1013 TORRENT_ASSERT(in_tell
== file_offset
+ file_iter
->file_base
&& !ec
);
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
;
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
);
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
);
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
;
1054 set_error(m_save_path
/ file_iter
->path
, ec
);
1057 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
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)
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
;
1075 fs::path path
= m_save_path
/ file_iter
->path
;
1079 in
= m_pool
.open_file( this, path
, file::in
, ec
);
1082 set_error(path
, ec
);
1085 size_type pos
= in
->seek(file_iter
->file_base
, file::begin
, ec
);
1086 if (pos
!= file_iter
->file_base
|| ec
)
1090 set_error(m_save_path
/ file_iter
->path
, ec
);
1093 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
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);
1114 std::vector
<file_slice
> slices
1115 = files().map_block(slot
, offset
, size
);
1116 TORRENT_ASSERT(!slices
.empty());
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
)
1130 file_offset
-= file_iter
->size
;
1132 TORRENT_ASSERT(file_iter
!= files().end());
1135 fs::path
p(m_save_path
/ file_iter
->path
);
1137 boost::shared_ptr
<file
> out
= m_pool
.open_file(
1138 this, p
, file::out
| file::in
, ec
);
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
)
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);
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);
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
);
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)
1205 if (write_bytes
> 0) ++counter
;
1209 TORRENT_ASSERT(file_iter
!= files().end());
1210 fs::path p
= m_save_path
/ file_iter
->path
;
1213 out
= m_pool
.open_file(
1214 this, p
, file::out
| file::in
, ec
);
1222 size_type pos
= out
->seek(file_iter
->file_base
, file::begin
, ec
);
1224 if (pos
!= file_iter
->file_base
|| ec
)
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
1247 , disk_io_thread
& io
1248 , storage_constructor_type sc
1249 , storage_mode_t sm
)
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
)
1257 , m_out_of_place(false)
1258 , m_scratch_piece(-1)
1259 , m_storage_constructor(sc
)
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
)
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
)
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
)
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
)
1301 j
.action
= disk_io_job::move_storage
;
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);
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
)
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
)
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
1344 j
.action
= disk_io_job::read
;
1347 j
.buffer_size
= r
.length
;
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
);
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);
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()));
1373 j
.action
= disk_io_job::write
;
1376 j
.buffer_size
= r
.length
;
1377 j
.buffer
= buffer
.get();
1378 m_io_thread
.add_job(j
, handler
);
1382 void piece_manager::async_hash(int piece
1383 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1387 j
.action
= disk_io_job::hash
;
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
);
1399 sha1_hash
piece_manager::hash_for_piece_impl(int piece
)
1403 std::map
<int, partial_hash
>::iterator i
= m_piece_hasher
.find(piece
);
1404 if (i
!= m_piece_hasher
.end())
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
);
1425 void piece_manager::write_resume_data(entry
& rd
) const
1427 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
1431 m_storage
->write_resume_data(rd
);
1433 if (m_storage_mode
== storage_mode_compact
)
1435 entry::list_type
& slots
= rd
["slots"].list();
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
)
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(
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(
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);
1504 partial_hash
& ph
= m_piece_hasher
[piece_index
];
1505 TORRENT_ASSERT(ph
.offset
== 0);
1507 ph
.h
.update(buf
, size
);
1509 out << time_now_string() << " NEW ["
1511 << " p: " << piece_index
1512 << " off: " << offset
1513 << " size: " << size
1514 << " entries: " << m_piece_hasher.size()
1515 << " ]" << std::endl;
1520 std::map
<int, partial_hash
>::iterator i
= m_piece_hasher
.find(piece_index
);
1521 if (i
!= m_piece_hasher
.end())
1524 TORRENT_ASSERT(i
->second
.offset
> 0);
1525 int hash_offset
= i
->second
.offset
;
1526 TORRENT_ASSERT(offset
>= hash_offset
);
1528 if (offset
== i
->second
.offset
)
1531 out << time_now_string() << " UPDATING ["
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
);
1544 out << time_now_string() << " SKIPPING (out of order) ["
1546 << " p: " << piece_index
1547 << " off: " << offset
1548 << " size: " << size
1549 << " entries: " << m_piece_hasher.size()
1550 << " ]" << std::endl;
1555 out << time_now_string() << " SKIPPING (no entry) ["
1557 << " p: " << piece_index
1558 << " off: " << offset
1559 << " size: " << size
1560 << " entries: " << m_piece_hasher.size()
1561 << " ]" << std::endl;
1569 int piece_manager::identify_data(
1570 const std::vector
<char>& piece_data
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
;
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())
1618 // ------------------------------------------
1619 // CHECK IF THE PIECE IS IN ITS CORRECT PLACE
1620 // ------------------------------------------
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
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;
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
;
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
);
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;
1685 if (free_piece
>= 0)
1687 TORRENT_ASSERT(m_piece_to_slot
[free_piece
] == has_no_slot
);
1692 TORRENT_ASSERT(free_piece
== 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
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
);
1716 file_exists
= exists(f
);
1718 #ifndef BOOST_NO_EXCEPTIONS
1720 catch (std::exception
& e
)
1725 TORRENT_ASSERT(!error
.empty());
1726 return fatal_disk_error
;
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
);
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
1790 int piece_manager::check_fastresume(
1791 lazy_entry
const& rd
, std::string
& error
)
1793 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
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)
1833 lazy_entry
const* slots
= rd
.dict_find_list("slots");
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
);
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
);
1883 TORRENT_ASSERT(index
== unallocated
);
1884 if (m_storage_mode
== storage_mode_compact
)
1885 m_unallocated_slots
.push_back(i
);
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
1913 // m_storage->shuffle();
1915 if (m_storage_mode
== storage_mode_compact
)
1917 if (m_unallocated_slots
.empty()) switch_to_full_mode();
1921 TORRENT_ASSERT(m_free_slots
.empty());
1922 TORRENT_ASSERT(m_unallocated_slots
.empty());
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
;
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
)
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
;
1968 m_free_slots
.push_back(i
);
1971 if (m_unallocated_slots
.empty()) switch_to_full_mode();
1974 return check_init_storage(error
);
1980 check_fastresume() ----------+
1984 | +------------+ +---------------+
1985 | | full_check |-->| expand_pieses |
1986 | +------------+ +---------------+
1989 | +--------------+ |
1990 +->| finished | <------+
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
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
;
2007 if (m_state
== state_expand_pieces
)
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
)
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))
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());
2107 error
= m_storage
->error().message();
2108 TORRENT_ASSERT(!error
.empty());
2109 return fatal_disk_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());
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
);
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
;
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
);
2216 if (m_storage
->error()
2217 && m_storage
->error() != error_code(ENOENT
, get_posix_category()))
2219 std::cerr
<< m_storage
->error().message() << std::endl
;
2225 // if the file is incomplete, skip the rest of it
2226 if (num_read
!= piece_size
)
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
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
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
);
2295 if (other_piece
>= 0)
2296 ret
|= m_storage
->swap_slots(other_slot
, m_current_slot
);
2298 ret
|= m_storage
->move_slot(m_current_slot
, other_slot
);
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
);
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
);
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
);
2330 ret
|= m_storage
->move_slot(other_slot
, m_current_slot
);
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);
2354 // this means there are only two pieces involved in the swap
2355 TORRENT_ASSERT(piece1
>= 0);
2357 // movement diagram:
2358 // +-------------------------------+
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
);
2378 TORRENT_ASSERT(slot1
!= slot2
);
2379 TORRENT_ASSERT(piece1
!= piece2
);
2381 // movement diagram:
2382 // +-----------------------------------------+
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
);
2408 m_piece_to_slot
[piece1
] = slot2
;
2409 ret
|= m_storage
->swap_slots3(m_current_slot
, slot1
, slot2
);
2413 ret
|= m_storage
->move_slot(m_current_slot
, slot1
);
2414 ret
|= m_storage
->move_slot(slot2
, m_current_slot
);
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
);
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
);
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
;
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());
2478 if (m_free_slots
.empty())
2481 TORRENT_ASSERT(!m_free_slots
.empty());
2484 std::vector
<int>::iterator
iter(
2486 m_free_slots
.begin()
2487 , m_free_slots
.end()
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)
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;
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: ";
2530 s
<< "\n slot_index: ";
2532 s
<< "\n piece at our slot: ";
2533 s
<< m_slot_to_piece
[piece_index
];
2536 print_to_log(s
.str());
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
);
2544 m_slot_to_piece
[piece_index
]
2545 , m_slot_to_piece
[slot_index
]);
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)
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();
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);
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
)
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
;
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);
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
];
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());
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
);
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
2721 m_unallocated_slots
.begin()
2722 , m_unallocated_slots
.end()
2723 , i
) == m_unallocated_slots
.end()
2727 m_free_slots
.begin()
2728 , m_free_slots
.end()
2729 , i
) == m_free_slots
.end()
2733 else if (m_slot_to_piece
[i
] == unallocated
)
2735 #ifdef TORRENT_STORAGE_DEBUG
2736 TORRENT_ASSERT(m_unallocated_slots
.empty()
2738 m_unallocated_slots
.begin()
2739 , m_unallocated_slots
.end()
2740 , i
) != m_unallocated_slots
.end())
2744 else if (m_slot_to_piece
[i
] == unassigned
)
2746 #ifdef TORRENT_STORAGE_DEBUG
2749 m_free_slots
.begin()
2750 , m_free_slots
.end()
2751 , i
) != m_free_slots
.end()
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());
2782 } // namespace libtorrent