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 #if BOOST_VERSION < 103600
269 recursive_copy(i
->path(), new_path
/ i
->leaf(), ec
);
271 recursive_copy(i
->path(), new_path
/ i
->filename(), ec
);
278 copy_file(old_path
, new_path
);
280 #ifndef BOOST_NO_EXCEPTIONS
281 } catch (std::exception
& e
) { ec
= error_code(errno
, get_posix_category()); }
285 template <class Path
>
286 void recursive_remove(Path
const& old_path
)
288 using boost::filesystem::basic_directory_iterator
;
289 #ifndef BOOST_NO_EXCEPTIONS
292 if (is_directory(old_path
))
294 for (basic_directory_iterator
<Path
> i(old_path
), end
; i
!= end
; ++i
)
295 recursive_remove(i
->path());
302 #ifndef BOOST_NO_EXCEPTIONS
303 } catch (std::exception
& e
) {}
306 std::vector
<std::pair
<size_type
, std::time_t> > get_filesizes(
307 file_storage
const& s
, fs::path p
)
310 std::vector
<std::pair
<size_type
, std::time_t> > sizes
;
311 for (file_storage::iterator i
= s
.begin()
312 , end(s
.end());i
!= end
; ++i
)
315 std::time_t time
= 0;
316 #if TORRENT_USE_WPATH
317 fs::wpath f
= safe_convert((p
/ i
->path
).string());
319 fs::path f
= p
/ i
->path
;
321 #ifndef BOOST_NO_EXCEPTIONS
328 time
= last_write_time(f
);
330 #ifndef BOOST_NO_EXCEPTIONS
331 catch (std::exception
&) {}
333 sizes
.push_back(std::make_pair(size
, time
));
338 // matches the sizes and timestamps of the files passed in
339 // in non-compact mode, actual file sizes and timestamps
340 // are allowed to be bigger and more recent than the fast
341 // resume data. This is because full allocation will not move
342 // pieces, so any older version of the resume data will
343 // still be a correct subset of the actual data on disk.
344 bool match_filesizes(
345 file_storage
const& fs
347 , std::vector
<std::pair
<size_type
, std::time_t> > const& sizes
349 , std::string
* error
)
351 if ((int)sizes
.size() != fs
.num_files())
353 if (error
) *error
= "mismatching number of files";
358 std::vector
<std::pair
<size_type
, std::time_t> >::const_iterator s
360 for (file_storage::iterator i
= fs
.begin()
361 , end(fs
.end());i
!= end
; ++i
, ++s
)
364 std::time_t time
= 0;
366 #if TORRENT_USE_WPATH
367 fs::wpath f
= safe_convert((p
/ i
->path
).string());
369 fs::path f
= p
/ i
->path
;
371 #ifndef BOOST_NO_EXCEPTIONS
378 time
= last_write_time(f
);
380 #ifndef BOOST_NO_EXCEPTIONS
381 catch (std::exception
&) {}
383 if ((compact_mode
&& size
!= s
->first
)
384 || (!compact_mode
&& size
< s
->first
))
386 if (error
) *error
= "filesize mismatch for file '"
387 + i
->path
.native_file_string()
388 + "', size: " + boost::lexical_cast
<std::string
>(size
)
389 + ", expected to be " + boost::lexical_cast
<std::string
>(s
->first
)
393 if ((compact_mode
&& time
!= s
->second
)
394 || (!compact_mode
&& time
< s
->second
))
396 if (error
) *error
= "timestamp mismatch for file '"
397 + i
->path
.native_file_string()
398 + "', modification date: " + boost::lexical_cast
<std::string
>(time
)
399 + ", expected to have modification date "
400 + boost::lexical_cast
<std::string
>(s
->second
);
407 class storage
: public storage_interface
, boost::noncopyable
410 storage(file_storage
const& fs
, fs::path
const& path
, file_pool
& fp
)
414 TORRENT_ASSERT(m_files
.begin() != m_files
.end());
415 m_save_path
= fs::complete(path
);
416 TORRENT_ASSERT(m_save_path
.is_complete());
419 bool rename_file(int index
, std::string
const& new_filename
);
420 bool release_files();
422 bool initialize(bool allocate_files
);
423 bool move_storage(fs::path save_path
);
424 int read(char* buf
, int slot
, int offset
, int size
);
425 int write(const char* buf
, int slot
, int offset
, int size
);
426 bool move_slot(int src_slot
, int dst_slot
);
427 bool swap_slots(int slot1
, int slot2
);
428 bool swap_slots3(int slot1
, int slot2
, int slot3
);
429 bool verify_resume_data(lazy_entry
const& rd
, std::string
& error
);
430 bool write_resume_data(entry
& rd
) const;
431 sha1_hash
hash_for_slot(int slot
, partial_hash
& ph
, int piece_size
);
433 int read_impl(char* buf
, int slot
, int offset
, int size
, bool fill_zero
);
436 { m_pool
.release(this); }
438 file_storage
const& files() const { return m_mapped_files
?*m_mapped_files
:m_files
; }
440 boost::scoped_ptr
<file_storage
> m_mapped_files
;
441 file_storage
const& m_files
;
443 std::vector
<boost::uint8_t> m_file_priority
;
444 fs::path m_save_path
;
445 // the file pool is typically stored in
446 // the session, to make all storage
447 // instances use the same pool
450 // temporary storage for moving pieces
451 buffer m_scratch_buffer
;
454 sha1_hash
storage::hash_for_slot(int slot
, partial_hash
& ph
, int piece_size
)
459 int slot_size1
= piece_size
;
460 m_scratch_buffer
.resize(slot_size1
);
461 read_impl(&m_scratch_buffer
[0], slot
, 0, slot_size1
, true);
463 partial
.update(&m_scratch_buffer
[0], ph
.offset
);
464 whole
.update(&m_scratch_buffer
[0], slot_size1
);
465 hasher partial_copy
= ph
.h
;
466 TORRENT_ASSERT(ph
.offset
== 0 || partial_copy
.final() == partial
.final());
468 int slot_size
= piece_size
- ph
.offset
;
471 m_scratch_buffer
.resize(slot_size
);
472 read_impl(&m_scratch_buffer
[0], slot
, ph
.offset
, slot_size
, true);
473 ph
.h
.update(&m_scratch_buffer
[0], slot_size
);
476 sha1_hash ret
= ph
.h
.final();
477 TORRENT_ASSERT(ret
== whole
.final());
484 bool storage::initialize(bool allocate_files
)
487 // first, create all missing directories
489 for (file_storage::iterator file_iter
= files().begin(),
490 end_iter
= files().end(); file_iter
!= end_iter
; ++file_iter
)
492 fs::path dir
= (m_save_path
/ file_iter
->path
).branch_path();
494 if (dir
!= last_path
)
497 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
499 if (!exists_win(last_path
))
500 create_directories_win(last_path
);
501 #elif TORRENT_USE_WPATH
503 fs::wpath wp
= safe_convert(last_path
.string());
505 create_directories(wp
);
508 if (!exists(last_path
))
509 create_directories(last_path
);
513 // if the file is empty, just create it. But also make sure
514 // the directory exists.
515 if (file_iter
->size
== 0)
517 file(m_save_path
/ file_iter
->path
, file::out
, ec
);
520 set_error(m_save_path
/ file_iter
->path
, ec
);
526 #ifndef BOOST_NO_EXCEPTIONS
529 // don't allocate files with priority 0
530 int file_index
= file_iter
- files().begin();
531 if (allocate_files
&& (int(m_file_priority
.size()) <= file_index
532 || m_file_priority
[file_index
] > 0))
535 boost::shared_ptr
<file
> f
= m_pool
.open_file(this
536 , m_save_path
/ file_iter
->path
, file::in
| file::out
, ec
);
537 if (ec
) set_error(m_save_path
/ file_iter
->path
, ec
);
540 f
->set_size(file_iter
->size
, ec
);
541 if (ec
) set_error(m_save_path
/ file_iter
->path
, ec
);
544 #ifndef BOOST_NO_EXCEPTIONS
546 catch (std::exception
& e
)
548 set_error(m_save_path
/ file_iter
->path
549 , error_code(errno
, get_posix_category()));
554 std::vector
<boost::uint8_t>().swap(m_file_priority
);
555 // close files that were opened in write mode
556 m_pool
.release(this);
560 bool storage::rename_file(int index
, std::string
const& new_filename
)
562 if (index
< 0 || index
>= m_files
.num_files()) return true;
563 fs::path old_name
= m_save_path
/ files().at(index
).path
;
564 m_pool
.release(old_name
);
566 #if TORRENT_USE_WPATH
567 fs::wpath old_path
= safe_convert(old_name
.string());
568 fs::wpath new_path
= safe_convert((m_save_path
/ new_filename
).string());
570 fs::path
const& old_path
= old_name
;
571 fs::path new_path
= m_save_path
/ new_filename
;
574 #ifndef BOOST_NO_EXCEPTIONS
578 rename(old_path
, new_path
);
581 rename(old_path, new_path, ec);
584 set_error(old_path, ec);
589 { m_mapped_files
.reset(new file_storage(m_files
)); }
590 m_mapped_files
->rename_file(index
, new_filename
);
591 #ifndef BOOST_NO_EXCEPTIONS
593 catch (std::exception
& e
)
595 set_error(old_name
, error_code(errno
, get_posix_category()));
602 bool storage::release_files()
604 m_pool
.release(this);
605 buffer().swap(m_scratch_buffer
);
609 bool storage::delete_files()
611 // make sure we don't have the files open
612 m_pool
.release(this);
613 buffer().swap(m_scratch_buffer
);
616 std::string error_file
;
618 // delete the files from disk
619 std::set
<std::string
> directories
;
620 typedef std::set
<std::string
>::iterator iter_t
;
621 for (file_storage::iterator i
= files().begin()
622 , end(files().end()); i
!= end
; ++i
)
624 std::string p
= (m_save_path
/ i
->path
).string();
625 fs::path bp
= i
->path
.branch_path();
626 std::pair
<iter_t
, bool> ret
;
628 while (ret
.second
&& !bp
.empty())
630 std::pair
<iter_t
, bool> ret
= directories
.insert((m_save_path
/ bp
).string());
631 bp
= bp
.branch_path();
633 #if TORRENT_USE_WPATH
635 { fs::remove(safe_convert(p
)); }
636 catch (std::exception
& e
)
642 if (std::remove(p
.c_str()) != 0 && errno
!= ENOENT
)
650 // remove the directories. Reverse order to delete
651 // subdirectories first
653 for (std::set
<std::string
>::reverse_iterator i
= directories
.rbegin()
654 , end(directories
.rend()); i
!= end
; ++i
)
656 #if TORRENT_USE_WPATH
658 { fs::remove(safe_convert(*i
)); }
659 catch (std::exception
& e
)
665 if (std::remove(i
->c_str()) != 0 && errno
!= ENOENT
)
675 m_error
= error_code(error
, get_posix_category());
676 m_error_file
.swap(error_file
);
682 bool storage::write_resume_data(entry
& rd
) const
684 TORRENT_ASSERT(rd
.type() == entry::dictionary_t
);
686 std::vector
<std::pair
<size_type
, std::time_t> > file_sizes
687 = get_filesizes(files(), m_save_path
);
689 entry::list_type
& fl
= rd
["file sizes"].list();
690 for (std::vector
<std::pair
<size_type
, std::time_t> >::iterator i
691 = file_sizes
.begin(), end(file_sizes
.end()); i
!= end
; ++i
)
694 p
.push_back(entry(i
->first
));
695 p
.push_back(entry(i
->second
));
696 fl
.push_back(entry(p
));
701 entry::list_type
& fl
= rd
["mapped_files"].list();
702 for (file_storage::iterator i
= m_mapped_files
->begin()
703 , end(m_mapped_files
->end()); i
!= end
; ++i
)
705 fl
.push_back(i
->path
.string());
712 bool storage::verify_resume_data(lazy_entry
const& rd
, std::string
& error
)
714 lazy_entry
const* file_priority
= rd
.dict_find_list("file_priority");
715 if (file_priority
&& file_priority
->list_size()
716 == files().num_files())
718 m_file_priority
.resize(file_priority
->list_size());
719 for (int i
= 0; i
< file_priority
->list_size(); ++i
)
720 m_file_priority
[i
] = file_priority
->list_int_value_at(i
, 1);
723 lazy_entry
const* mapped_files
= rd
.dict_find_list("mapped_files");
724 if (mapped_files
&& mapped_files
->list_size() == m_files
.num_files())
727 { m_mapped_files
.reset(new file_storage(m_files
)); }
728 for (int i
= 0; i
< m_files
.num_files(); ++i
)
729 m_mapped_files
->rename_file(i
, mapped_files
->list_string_value_at(i
));
732 std::vector
<std::pair
<size_type
, std::time_t> > file_sizes
;
733 lazy_entry
const* file_sizes_ent
= rd
.dict_find_list("file sizes");
734 if (file_sizes_ent
== 0)
736 error
= "missing or invalid 'file sizes' entry in resume data";
740 for (int i
= 0; i
< file_sizes_ent
->list_size(); ++i
)
742 lazy_entry
const* e
= file_sizes_ent
->list_at(i
);
743 if (e
->type() != lazy_entry::list_t
744 || e
->list_size() != 2
745 || e
->list_at(0)->type() != lazy_entry::int_t
746 || e
->list_at(1)->type() != lazy_entry::int_t
)
748 file_sizes
.push_back(std::pair
<size_type
, std::time_t>(
749 e
->list_int_value_at(0), std::time_t(e
->list_int_value_at(1))));
752 if (file_sizes
.empty())
754 error
= "the number of files in resume data is 0";
760 lazy_entry
const* slots
= rd
.dict_find_list("slots");
763 if (int(slots
->list_size()) == m_files
.num_pieces())
766 for (int i
= 0; i
< slots
->list_size(); ++i
)
768 lazy_entry
const* e
= slots
->list_at(i
);
769 if (e
->list_int_value_at(i
, -1) >= 0) continue;
775 else if (lazy_entry
const* pieces
= rd
.dict_find_string("pieces"))
777 if (int(pieces
->string_length()) == m_files
.num_pieces())
780 char const* p
= pieces
->string_ptr();
781 for (int i
= 0; i
< pieces
->string_length(); ++i
)
783 if ((p
[i
] & 1) == 1) continue;
791 error
= "missing 'slots' and 'pieces' entry in resume data";
795 bool full_allocation_mode
= false;
796 if (rd
.dict_find_string_value("allocation") != "compact")
797 full_allocation_mode
= true;
801 if (files().num_files() != (int)file_sizes
.size())
803 error
= "the number of files does not match the torrent (num: "
804 + boost::lexical_cast
<std::string
>(file_sizes
.size()) + " actual: "
805 + boost::lexical_cast
<std::string
>(files().num_files()) + ")";
809 std::vector
<std::pair
<size_type
, std::time_t> >::iterator
810 fs
= file_sizes
.begin();
811 // the resume data says we have the entire torrent
812 // make sure the file sizes are the right ones
813 for (file_storage::iterator i
= files().begin()
814 , end(files().end()); i
!= end
; ++i
, ++fs
)
816 if (i
->size
!= fs
->first
)
818 error
= "file size for '" + i
->path
.native_file_string()
819 + "' was expected to be "
820 + boost::lexical_cast
<std::string
>(i
->size
) + " bytes";
826 return match_filesizes(files(), m_save_path
, file_sizes
827 , !full_allocation_mode
, &error
);
830 // returns true on success
831 bool storage::move_storage(fs::path save_path
)
833 #if TORRENT_USE_WPATH
841 save_path
= complete(save_path
);
843 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
844 std::wstring
wsave_path(safe_convert(save_path
.native_file_string()));
845 if (!exists_win(save_path
))
846 CreateDirectory(wsave_path
.c_str(), 0);
847 else if ((GetFileAttributes(wsave_path
.c_str()) & FILE_ATTRIBUTE_DIRECTORY
) == 0)
849 #elif TORRENT_USE_WPATH
850 fs::wpath wp
= safe_convert(save_path
.string());
852 create_directory(wp
);
853 else if (!is_directory(wp
))
856 if (!exists(save_path
))
857 create_directory(save_path
);
858 else if (!is_directory(save_path
))
862 m_pool
.release(this);
864 #if TORRENT_USE_WPATH
865 old_path
= safe_convert((m_save_path
/ files().name()).string());
866 new_path
= safe_convert((save_path
/ files().name()).string());
868 old_path
= m_save_path
/ files().name();
869 new_path
= save_path
/ files().name();
872 #ifndef BOOST_NO_EXCEPTIONS
876 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
877 rename_win(old_path
, new_path
);
879 rename(old_path
, new_path
);
881 m_save_path
= save_path
;
883 #ifndef BOOST_NO_EXCEPTIONS
885 catch (std::exception
& e
)
888 recursive_copy(old_path
, new_path
, ec
);
891 set_error(m_save_path
/ files().name(), ec
);
894 m_save_path
= save_path
;
895 recursive_remove(old_path
);
903 void storage::shuffle()
905 int num_pieces = files().num_pieces();
907 std::vector<int> pieces(num_pieces);
908 for (std::vector<int>::iterator i = pieces.begin();
909 i != pieces.end(); ++i)
911 *i = static_cast<int>(i - pieces.begin());
913 std::srand((unsigned int)std::time(0));
914 std::vector<int> targets(pieces);
915 std::random_shuffle(pieces.begin(), pieces.end());
916 std::random_shuffle(targets.begin(), targets.end());
918 for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i)
920 const int slot_index = targets[i];
921 const int piece_index = pieces[i];
922 const int slot_size =static_cast<int>(m_files.piece_size(slot_index));
923 std::vector<char> buf(slot_size);
924 read(&buf[0], piece_index, 0, slot_size);
925 write(&buf[0], slot_index, 0, slot_size);
931 bool storage::move_slot(int src_slot
, int dst_slot
)
933 int piece_size
= m_files
.piece_size(dst_slot
);
934 m_scratch_buffer
.resize(piece_size
);
935 int ret1
= read_impl(&m_scratch_buffer
[0], src_slot
, 0, piece_size
, true);
936 int ret2
= write(&m_scratch_buffer
[0], dst_slot
, 0, piece_size
);
937 return ret1
!= piece_size
|| ret2
!= piece_size
;
940 bool storage::swap_slots(int slot1
, int slot2
)
942 // the size of the target slot is the size of the piece
943 int piece_size
= m_files
.piece_length();
944 int piece1_size
= m_files
.piece_size(slot2
);
945 int piece2_size
= m_files
.piece_size(slot1
);
946 m_scratch_buffer
.resize(piece_size
* 2);
947 int ret1
= read_impl(&m_scratch_buffer
[0], slot1
, 0, piece1_size
, true);
948 int ret2
= read_impl(&m_scratch_buffer
[piece_size
], slot2
, 0, piece2_size
, true);
949 int ret3
= write(&m_scratch_buffer
[0], slot2
, 0, piece1_size
);
950 int ret4
= write(&m_scratch_buffer
[piece_size
], slot1
, 0, piece2_size
);
951 return ret1
!= piece1_size
|| ret2
!= piece2_size
952 || ret3
!= piece1_size
|| ret4
!= piece2_size
;
955 bool storage::swap_slots3(int slot1
, int slot2
, int slot3
)
957 // the size of the target slot is the size of the piece
958 int piece_size
= m_files
.piece_length();
959 int piece1_size
= m_files
.piece_size(slot2
);
960 int piece2_size
= m_files
.piece_size(slot3
);
961 int piece3_size
= m_files
.piece_size(slot1
);
962 m_scratch_buffer
.resize(piece_size
* 2);
963 int ret1
= read_impl(&m_scratch_buffer
[0], slot1
, 0, piece1_size
, true);
964 int ret2
= read_impl(&m_scratch_buffer
[piece_size
], slot2
, 0, piece2_size
, true);
965 int ret3
= write(&m_scratch_buffer
[0], slot2
, 0, piece1_size
);
966 int ret4
= read_impl(&m_scratch_buffer
[0], slot3
, 0, piece3_size
, true);
967 int ret5
= write(&m_scratch_buffer
[piece_size
], slot3
, 0, piece2_size
);
968 int ret6
= write(&m_scratch_buffer
[0], slot1
, 0, piece3_size
);
969 return ret1
!= piece1_size
|| ret2
!= piece2_size
970 || ret3
!= piece1_size
|| ret4
!= piece3_size
971 || ret5
!= piece2_size
|| ret6
!= piece3_size
;
980 return read_impl(buf
, slot
, offset
, size
, false);
983 int storage::read_impl(
990 TORRENT_ASSERT(buf
!= 0);
991 TORRENT_ASSERT(slot
>= 0 && slot
< m_files
.num_pieces());
992 TORRENT_ASSERT(offset
>= 0);
993 TORRENT_ASSERT(offset
< m_files
.piece_size(slot
));
994 TORRENT_ASSERT(size
> 0);
997 std::vector
<file_slice
> slices
998 = files().map_block(slot
, offset
, size
);
999 TORRENT_ASSERT(!slices
.empty());
1002 size_type start
= slot
* (size_type
)m_files
.piece_length() + offset
;
1003 TORRENT_ASSERT(start
+ size
<= m_files
.total_size());
1005 // find the file iterator and file offset
1006 size_type file_offset
= start
;
1007 std::vector
<file_entry
>::const_iterator file_iter
;
1009 for (file_iter
= files().begin();;)
1011 if (file_offset
< file_iter
->size
)
1014 file_offset
-= file_iter
->size
;
1020 boost::shared_ptr
<file
> in(m_pool
.open_file(
1021 this, m_save_path
/ file_iter
->path
, file::in
, ec
));
1024 set_error(m_save_path
/ file_iter
->path
, ec
);
1027 TORRENT_ASSERT(file_offset
< file_iter
->size
);
1028 TORRENT_ASSERT(slices
[0].offset
== file_offset
+ file_iter
->file_base
);
1030 size_type new_pos
= in
->seek(file_offset
+ file_iter
->file_base
, file::begin
, ec
);
1031 if (new_pos
!= file_offset
+ file_iter
->file_base
|| ec
)
1033 // the file was not big enough
1036 set_error(m_save_path
/ file_iter
->path
, ec
);
1039 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
1044 size_type in_tell
= in
->tell(ec
);
1045 TORRENT_ASSERT(in_tell
== file_offset
+ file_iter
->file_base
&& !ec
);
1048 int left_to_read
= size
;
1049 int slot_size
= static_cast<int>(m_files
.piece_size(slot
));
1051 if (offset
+ left_to_read
> slot_size
)
1052 left_to_read
= slot_size
- offset
;
1054 TORRENT_ASSERT(left_to_read
>= 0);
1056 size_type result
= left_to_read
;
1062 while (left_to_read
> 0)
1064 int read_bytes
= left_to_read
;
1065 if (file_offset
+ read_bytes
> file_iter
->size
)
1066 read_bytes
= static_cast<int>(file_iter
->size
- file_offset
);
1071 TORRENT_ASSERT(int(slices
.size()) > counter
);
1072 size_type slice_size
= slices
[counter
].size
;
1073 TORRENT_ASSERT(slice_size
== read_bytes
);
1074 TORRENT_ASSERT(files().at(slices
[counter
].file_index
).path
1075 == file_iter
->path
);
1078 int actual_read
= int(in
->read(buf
+ buf_pos
, read_bytes
, ec
));
1080 if (read_bytes
!= actual_read
|| ec
)
1082 // the file was not big enough
1083 if (actual_read
> 0) buf_pos
+= actual_read
;
1086 set_error(m_save_path
/ file_iter
->path
, ec
);
1089 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
1093 left_to_read
-= read_bytes
;
1094 buf_pos
+= read_bytes
;
1095 TORRENT_ASSERT(buf_pos
>= 0);
1096 file_offset
+= read_bytes
;
1099 if (left_to_read
> 0)
1103 // empty files are not returned by map_block, so if
1104 // this file was empty, don't increment the slice counter
1105 if (read_bytes
> 0) ++counter
;
1107 fs::path path
= m_save_path
/ file_iter
->path
;
1111 in
= m_pool
.open_file( this, path
, file::in
, ec
);
1114 set_error(path
, ec
);
1117 size_type pos
= in
->seek(file_iter
->file_base
, file::begin
, ec
);
1118 if (pos
!= file_iter
->file_base
|| ec
)
1122 set_error(m_save_path
/ file_iter
->path
, ec
);
1125 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
1139 TORRENT_ASSERT(buf
!= 0);
1140 TORRENT_ASSERT(slot
>= 0);
1141 TORRENT_ASSERT(slot
< m_files
.num_pieces());
1142 TORRENT_ASSERT(offset
>= 0);
1143 TORRENT_ASSERT(size
> 0);
1146 std::vector
<file_slice
> slices
1147 = files().map_block(slot
, offset
, size
);
1148 TORRENT_ASSERT(!slices
.empty());
1151 size_type start
= slot
* (size_type
)m_files
.piece_length() + offset
;
1153 // find the file iterator and file offset
1154 size_type file_offset
= start
;
1155 std::vector
<file_entry
>::const_iterator file_iter
;
1157 for (file_iter
= files().begin();;)
1159 if (file_offset
< file_iter
->size
)
1162 file_offset
-= file_iter
->size
;
1164 TORRENT_ASSERT(file_iter
!= files().end());
1167 fs::path
p(m_save_path
/ file_iter
->path
);
1169 boost::shared_ptr
<file
> out
= m_pool
.open_file(
1170 this, p
, file::out
| file::in
, ec
);
1177 TORRENT_ASSERT(file_offset
< file_iter
->size
);
1178 TORRENT_ASSERT(slices
[0].offset
== file_offset
+ file_iter
->file_base
);
1180 size_type pos
= out
->seek(file_offset
+ file_iter
->file_base
, file::begin
, ec
);
1182 if (pos
!= file_offset
+ file_iter
->file_base
|| ec
)
1188 int left_to_write
= size
;
1189 int slot_size
= static_cast<int>(m_files
.piece_size(slot
));
1191 if (offset
+ left_to_write
> slot_size
)
1192 left_to_write
= slot_size
- offset
;
1194 TORRENT_ASSERT(left_to_write
>= 0);
1200 while (left_to_write
> 0)
1202 int write_bytes
= left_to_write
;
1203 if (file_offset
+ write_bytes
> file_iter
->size
)
1205 TORRENT_ASSERT(file_iter
->size
>= file_offset
);
1206 write_bytes
= static_cast<int>(file_iter
->size
- file_offset
);
1209 if (write_bytes
> 0)
1211 TORRENT_ASSERT(int(slices
.size()) > counter
);
1212 TORRENT_ASSERT(slices
[counter
].size
== write_bytes
);
1213 TORRENT_ASSERT(files().at(slices
[counter
].file_index
).path
1214 == file_iter
->path
);
1216 TORRENT_ASSERT(buf_pos
>= 0);
1217 TORRENT_ASSERT(write_bytes
>= 0);
1219 size_type written
= out
->write(buf
+ buf_pos
, write_bytes
, ec
);
1221 if (written
!= write_bytes
|| ec
)
1223 set_error(m_save_path
/ file_iter
->path
, ec
);
1227 left_to_write
-= write_bytes
;
1228 buf_pos
+= write_bytes
;
1229 TORRENT_ASSERT(buf_pos
>= 0);
1230 file_offset
+= write_bytes
;
1231 TORRENT_ASSERT(file_offset
<= file_iter
->size
);
1234 if (left_to_write
> 0)
1237 if (write_bytes
> 0) ++counter
;
1241 TORRENT_ASSERT(file_iter
!= files().end());
1242 fs::path p
= m_save_path
/ file_iter
->path
;
1245 out
= m_pool
.open_file(
1246 this, p
, file::out
| file::in
, ec
);
1254 size_type pos
= out
->seek(file_iter
->file_base
, file::begin
, ec
);
1256 if (pos
!= file_iter
->file_base
|| ec
)
1266 storage_interface
* default_storage_constructor(file_storage
const& fs
1267 , fs::path
const& path
, file_pool
& fp
)
1269 return new storage(fs
, path
, fp
);
1272 // -- piece_manager -----------------------------------------------------
1274 piece_manager::piece_manager(
1275 boost::shared_ptr
<void> const& torrent
1276 , boost::intrusive_ptr
<torrent_info
const> info
1277 , fs::path
const& save_path
1279 , disk_io_thread
& io
1280 , storage_constructor_type sc
1281 , storage_mode_t sm
)
1283 , m_files(m_info
->files())
1284 , m_storage(sc(m_files
, save_path
, fp
))
1285 , m_storage_mode(sm
)
1286 , m_save_path(complete(save_path
))
1287 , m_state(state_none
)
1289 , m_out_of_place(false)
1290 , m_scratch_piece(-1)
1291 , m_storage_constructor(sc
)
1293 , m_torrent(torrent
)
1297 piece_manager::~piece_manager()
1301 void piece_manager::async_save_resume_data(
1302 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1306 j
.action
= disk_io_job::save_resume_data
;
1307 m_io_thread
.add_job(j
, handler
);
1310 void piece_manager::async_clear_read_cache(
1311 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1315 j
.action
= disk_io_job::clear_read_cache
;
1316 m_io_thread
.add_job(j
, handler
);
1319 void piece_manager::async_release_files(
1320 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1324 j
.action
= disk_io_job::release_files
;
1325 m_io_thread
.add_job(j
, handler
);
1328 void piece_manager::async_delete_files(
1329 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1333 j
.action
= disk_io_job::delete_files
;
1334 m_io_thread
.add_job(j
, handler
);
1337 void piece_manager::async_move_storage(fs::path
const& p
1338 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1342 j
.action
= disk_io_job::move_storage
;
1344 m_io_thread
.add_job(j
, handler
);
1347 void piece_manager::async_check_fastresume(lazy_entry
const* resume_data
1348 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1350 TORRENT_ASSERT(resume_data
!= 0);
1353 j
.action
= disk_io_job::check_fastresume
;
1354 j
.buffer
= (char*)resume_data
;
1355 m_io_thread
.add_job(j
, handler
);
1358 void piece_manager::async_rename_file(int index
, std::string
const& name
1359 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1365 j
.action
= disk_io_job::rename_file
;
1366 m_io_thread
.add_job(j
, handler
);
1369 void piece_manager::async_check_files(
1370 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1374 j
.action
= disk_io_job::check_files
;
1375 m_io_thread
.add_job(j
, handler
);
1378 void piece_manager::async_read(
1379 peer_request
const& r
1380 , boost::function
<void(int, disk_io_job
const&)> const& handler
1385 j
.action
= disk_io_job::read
;
1388 j
.buffer_size
= r
.length
;
1390 j
.priority
= priority
;
1391 // if a buffer is not specified, only one block can be read
1392 // since that is the size of the pool allocator's buffers
1393 TORRENT_ASSERT(r
.length
<= 16 * 1024);
1394 m_io_thread
.add_job(j
, handler
);
1396 boost::recursive_mutex::scoped_lock
l(m_mutex
);
1397 // if this assert is hit, it suggests
1398 // that check_files was not successful
1399 TORRENT_ASSERT(slot_for(r
.piece
) >= 0);
1403 void piece_manager::async_write(
1404 peer_request
const& r
1405 , disk_buffer_holder
& buffer
1406 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1408 TORRENT_ASSERT(r
.length
<= 16 * 1024);
1409 // the buffer needs to be allocated through the io_thread
1410 TORRENT_ASSERT(m_io_thread
.is_disk_buffer(buffer
.get()));
1414 j
.action
= disk_io_job::write
;
1417 j
.buffer_size
= r
.length
;
1418 j
.buffer
= buffer
.get();
1419 m_io_thread
.add_job(j
, handler
);
1423 void piece_manager::async_hash(int piece
1424 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1428 j
.action
= disk_io_job::hash
;
1431 m_io_thread
.add_job(j
, handler
);
1434 fs::path
piece_manager::save_path() const
1436 boost::recursive_mutex::scoped_lock
l(m_mutex
);
1440 sha1_hash
piece_manager::hash_for_piece_impl(int piece
)
1444 std::map
<int, partial_hash
>::iterator i
= m_piece_hasher
.find(piece
);
1445 if (i
!= m_piece_hasher
.end())
1448 m_piece_hasher
.erase(i
);
1451 int slot
= slot_for(piece
);
1452 TORRENT_ASSERT(slot
!= has_no_slot
);
1453 return m_storage
->hash_for_slot(slot
, ph
, m_files
.piece_size(piece
));
1456 int piece_manager::move_storage_impl(fs::path
const& save_path
)
1458 if (m_storage
->move_storage(save_path
))
1460 m_save_path
= fs::complete(save_path
);
1466 void piece_manager::write_resume_data(entry
& rd
) const
1468 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
1472 m_storage
->write_resume_data(rd
);
1474 if (m_storage_mode
== storage_mode_compact
)
1476 entry::list_type
& slots
= rd
["slots"].list();
1478 std::vector
<int>::const_reverse_iterator last
;
1479 for (last
= m_slot_to_piece
.rbegin();
1480 last
!= m_slot_to_piece
.rend(); ++last
)
1482 if (*last
!= unallocated
) break;
1485 for (std::vector
<int>::const_iterator i
=
1486 m_slot_to_piece
.begin();
1487 i
!= last
.base(); ++i
)
1489 slots
.push_back((*i
>= 0) ? *i
: unassigned
);
1493 rd
["allocation"] = m_storage_mode
== storage_mode_sparse
?"sparse"
1494 :m_storage_mode
== storage_mode_allocate
?"full":"compact";
1497 void piece_manager::mark_failed(int piece_index
)
1501 if (m_storage_mode
!= storage_mode_compact
) return;
1503 TORRENT_ASSERT(piece_index
>= 0 && piece_index
< (int)m_piece_to_slot
.size());
1504 int slot_index
= m_piece_to_slot
[piece_index
];
1505 TORRENT_ASSERT(slot_index
>= 0);
1507 m_slot_to_piece
[slot_index
] = unassigned
;
1508 m_piece_to_slot
[piece_index
] = has_no_slot
;
1509 m_free_slots
.push_back(slot_index
);
1512 int piece_manager::read_impl(
1518 TORRENT_ASSERT(buf
);
1519 TORRENT_ASSERT(offset
>= 0);
1520 TORRENT_ASSERT(size
> 0);
1521 int slot
= slot_for(piece_index
);
1522 return m_storage
->read(buf
, slot
, offset
, size
);
1525 int piece_manager::write_impl(
1531 TORRENT_ASSERT(buf
);
1532 TORRENT_ASSERT(offset
>= 0);
1533 TORRENT_ASSERT(size
> 0);
1534 TORRENT_ASSERT(piece_index
>= 0 && piece_index
< m_files
.num_pieces());
1536 int slot
= allocate_slot_for_piece(piece_index
);
1537 int ret
= m_storage
->write(buf
, slot
, offset
, size
);
1538 // only save the partial hash if the write succeeds
1539 if (ret
!= size
) return ret
;
1541 // std::ofstream out("partial_hash.log", std::ios::app);
1545 partial_hash
& ph
= m_piece_hasher
[piece_index
];
1546 TORRENT_ASSERT(ph
.offset
== 0);
1548 ph
.h
.update(buf
, size
);
1550 out << time_now_string() << " NEW ["
1552 << " p: " << piece_index
1553 << " off: " << offset
1554 << " size: " << size
1555 << " entries: " << m_piece_hasher.size()
1556 << " ]" << std::endl;
1561 std::map
<int, partial_hash
>::iterator i
= m_piece_hasher
.find(piece_index
);
1562 if (i
!= m_piece_hasher
.end())
1565 TORRENT_ASSERT(i
->second
.offset
> 0);
1566 int hash_offset
= i
->second
.offset
;
1567 TORRENT_ASSERT(offset
>= hash_offset
);
1569 if (offset
== i
->second
.offset
)
1572 out << time_now_string() << " UPDATING ["
1574 << " p: " << piece_index
1575 << " off: " << offset
1576 << " size: " << size
1577 << " entries: " << m_piece_hasher.size()
1578 << " ]" << std::endl;
1580 i
->second
.offset
+= size
;
1581 i
->second
.h
.update(buf
, size
);
1585 out << time_now_string() << " SKIPPING (out of order) ["
1587 << " p: " << piece_index
1588 << " off: " << offset
1589 << " size: " << size
1590 << " entries: " << m_piece_hasher.size()
1591 << " ]" << std::endl;
1596 out << time_now_string() << " SKIPPING (no entry) ["
1598 << " p: " << piece_index
1599 << " off: " << offset
1600 << " size: " << size
1601 << " entries: " << m_piece_hasher.size()
1602 << " ]" << std::endl;
1610 int piece_manager::identify_data(
1611 const std::vector
<char>& piece_data
1616 const int piece_size
= static_cast<int>(m_files
.piece_length());
1617 const int last_piece_size
= static_cast<int>(m_files
.piece_size(
1618 m_files
.num_pieces() - 1));
1620 TORRENT_ASSERT((int)piece_data
.size() >= last_piece_size
);
1622 // calculate a small digest, with the same
1623 // size as the last piece. And a large digest
1624 // which has the same size as a normal piece
1625 hasher small_digest
;
1626 small_digest
.update(&piece_data
[0], last_piece_size
);
1627 hasher
large_digest(small_digest
);
1628 TORRENT_ASSERT(piece_size
- last_piece_size
>= 0);
1629 if (piece_size
- last_piece_size
> 0)
1631 large_digest
.update(
1632 &piece_data
[last_piece_size
]
1633 , piece_size
- last_piece_size
);
1635 sha1_hash large_hash
= large_digest
.final();
1636 sha1_hash small_hash
= small_digest
.final();
1638 typedef std::multimap
<sha1_hash
, int>::const_iterator map_iter
;
1644 // makes the lookups for the small digest and the large digest
1645 boost::tie(begin1
, end1
) = m_hash_to_piece
.equal_range(small_hash
);
1646 boost::tie(begin2
, end2
) = m_hash_to_piece
.equal_range(large_hash
);
1648 // copy all potential piece indices into this vector
1649 std::vector
<int> matching_pieces
;
1650 for (map_iter i
= begin1
; i
!= end1
; ++i
)
1651 matching_pieces
.push_back(i
->second
);
1652 for (map_iter i
= begin2
; i
!= end2
; ++i
)
1653 matching_pieces
.push_back(i
->second
);
1655 // no piece matched the data in the slot
1656 if (matching_pieces
.empty())
1659 // ------------------------------------------
1660 // CHECK IF THE PIECE IS IN ITS CORRECT PLACE
1661 // ------------------------------------------
1664 matching_pieces
.begin()
1665 , matching_pieces
.end()
1666 , current_slot
) != matching_pieces
.end())
1668 // the current slot is among the matching pieces, so
1669 // we will assume that the piece is in the right place
1670 const int piece_index
= current_slot
;
1672 int other_slot
= m_piece_to_slot
[piece_index
];
1673 if (other_slot
>= 0)
1675 // we have already found a piece with
1678 // take one of the other matching pieces
1679 // that hasn't already been assigned
1680 int other_piece
= -1;
1681 for (std::vector
<int>::iterator i
= matching_pieces
.begin();
1682 i
!= matching_pieces
.end(); ++i
)
1684 if (m_piece_to_slot
[*i
] >= 0 || *i
== piece_index
) continue;
1688 if (other_piece
>= 0)
1690 // replace the old slot with 'other_piece'
1691 m_slot_to_piece
[other_slot
] = other_piece
;
1692 m_piece_to_slot
[other_piece
] = other_slot
;
1696 // this index is the only piece with this
1697 // hash. The previous slot we found with
1698 // this hash must be the same piece. Mark
1699 // that piece as unassigned, since this slot
1700 // is the correct place for the piece.
1701 m_slot_to_piece
[other_slot
] = unassigned
;
1702 if (m_storage_mode
== storage_mode_compact
)
1703 m_free_slots
.push_back(other_slot
);
1705 TORRENT_ASSERT(m_piece_to_slot
[piece_index
] != current_slot
);
1706 TORRENT_ASSERT(m_piece_to_slot
[piece_index
] >= 0);
1707 m_piece_to_slot
[piece_index
] = has_no_slot
;
1710 TORRENT_ASSERT(m_piece_to_slot
[piece_index
] == has_no_slot
);
1715 // find a matching piece that hasn't
1716 // already been assigned
1717 int free_piece
= unassigned
;
1718 for (std::vector
<int>::iterator i
= matching_pieces
.begin();
1719 i
!= matching_pieces
.end(); ++i
)
1721 if (m_piece_to_slot
[*i
] >= 0) continue;
1726 if (free_piece
>= 0)
1728 TORRENT_ASSERT(m_piece_to_slot
[free_piece
] == has_no_slot
);
1733 TORRENT_ASSERT(free_piece
== unassigned
);
1738 int piece_manager::check_no_fastresume(std::string
& error
)
1740 file_storage::iterator i
= m_files
.begin();
1741 file_storage::iterator end
= m_files
.end();
1743 for (; i
!= end
; ++i
)
1745 bool file_exists
= false;
1746 fs::path f
= m_save_path
/ i
->path
;
1747 #ifndef BOOST_NO_EXCEPTIONS
1751 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
1752 file_exists
= exists_win(f
);
1753 #elif TORRENT_USE_WPATH
1754 fs::wpath wf
= safe_convert(f
.string());
1755 file_exists
= exists(wf
);
1757 file_exists
= exists(f
);
1759 #ifndef BOOST_NO_EXCEPTIONS
1761 catch (std::exception
& e
)
1766 TORRENT_ASSERT(!error
.empty());
1767 return fatal_disk_error
;
1770 if (file_exists
&& i
->size
> 0)
1772 m_state
= state_full_check
;
1773 m_piece_to_slot
.clear();
1774 m_piece_to_slot
.resize(m_files
.num_pieces(), has_no_slot
);
1775 m_slot_to_piece
.clear();
1776 m_slot_to_piece
.resize(m_files
.num_pieces(), unallocated
);
1777 if (m_storage_mode
== storage_mode_compact
)
1779 m_unallocated_slots
.clear();
1780 m_free_slots
.clear();
1782 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
1783 return need_full_check
;
1787 if (m_storage_mode
== storage_mode_compact
)
1789 // in compact mode without checking, we need to
1790 // populate the unallocated list
1791 TORRENT_ASSERT(m_unallocated_slots
.empty());
1792 for (int i
= 0, end(m_files
.num_pieces()); i
< end
; ++i
)
1793 m_unallocated_slots
.push_back(i
);
1794 m_piece_to_slot
.clear();
1795 m_piece_to_slot
.resize(m_files
.num_pieces(), has_no_slot
);
1796 m_slot_to_piece
.clear();
1797 m_slot_to_piece
.resize(m_files
.num_pieces(), unallocated
);
1800 return check_init_storage(error
);
1803 int piece_manager::check_init_storage(std::string
& error
)
1805 if (m_storage
->initialize(m_storage_mode
== storage_mode_allocate
))
1807 error
= m_storage
->error().message();
1808 TORRENT_ASSERT(!error
.empty());
1809 return fatal_disk_error
;
1811 m_state
= state_finished
;
1812 buffer().swap(m_scratch_buffer
);
1813 buffer().swap(m_scratch_buffer2
);
1814 if (m_storage_mode
!= storage_mode_compact
)
1816 // if no piece is out of place
1817 // since we're in full allocation mode, we can
1818 // forget the piece allocation tables
1819 std::vector
<int>().swap(m_piece_to_slot
);
1820 std::vector
<int>().swap(m_slot_to_piece
);
1821 std::vector
<int>().swap(m_free_slots
);
1822 std::vector
<int>().swap(m_unallocated_slots
);
1827 // check if the fastresume data is up to date
1828 // if it is, use it and return true. If it
1829 // isn't return false and the full check
1831 int piece_manager::check_fastresume(
1832 lazy_entry
const& rd
, std::string
& error
)
1834 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
1838 TORRENT_ASSERT(m_files
.piece_length() > 0);
1840 // if we don't have any resume data, return
1841 if (rd
.type() == lazy_entry::none_t
) return check_no_fastresume(error
);
1843 if (rd
.type() != lazy_entry::dict_t
)
1845 error
= "invalid fastresume data (not a dictionary)";
1846 return check_no_fastresume(error
);
1849 int block_size
= (std::min
)(16 * 1024, m_files
.piece_length());
1850 int blocks_per_piece
= rd
.dict_find_int_value("blocks per piece", -1);
1851 if (blocks_per_piece
!= -1
1852 && blocks_per_piece
!= m_files
.piece_length() / block_size
)
1854 error
= "invalid 'blocks per piece' entry";
1855 return check_no_fastresume(error
);
1858 storage_mode_t storage_mode
= storage_mode_compact
;
1859 if (rd
.dict_find_string_value("allocation") != "compact")
1860 storage_mode
= storage_mode_sparse
;
1862 if (!m_storage
->verify_resume_data(rd
, error
))
1863 return check_no_fastresume(error
);
1865 // assume no piece is out of place (i.e. in a slot
1866 // other than the one it should be in)
1867 bool out_of_place
= false;
1869 // if we don't have a piece map, we need the slots
1870 // if we're in compact mode, we also need the slots map
1871 if (storage_mode
== storage_mode_compact
|| rd
.dict_find("pieces") == 0)
1874 lazy_entry
const* slots
= rd
.dict_find_list("slots");
1877 error
= "missing slot list";
1878 return check_no_fastresume(error
);
1881 if ((int)slots
->list_size() > m_files
.num_pieces())
1883 error
= "file has more slots than torrent (slots: "
1884 + boost::lexical_cast
<std::string
>(slots
->list_size()) + " size: "
1885 + boost::lexical_cast
<std::string
>(m_files
.num_pieces()) + " )";
1886 return check_no_fastresume(error
);
1889 if (m_storage_mode
== storage_mode_compact
)
1891 int num_pieces
= int(m_files
.num_pieces());
1892 m_slot_to_piece
.resize(num_pieces
, unallocated
);
1893 m_piece_to_slot
.resize(num_pieces
, has_no_slot
);
1894 for (int i
= 0; i
< slots
->list_size(); ++i
)
1896 lazy_entry
const* e
= slots
->list_at(i
);
1897 if (e
->type() != lazy_entry::int_t
)
1899 error
= "invalid entry type in slot list";
1900 return check_no_fastresume(error
);
1903 int index
= int(e
->int_value());
1904 if (index
>= num_pieces
|| index
< -2)
1906 error
= "too high index number in slot map (index: "
1907 + boost::lexical_cast
<std::string
>(index
) + " size: "
1908 + boost::lexical_cast
<std::string
>(num_pieces
) + ")";
1909 return check_no_fastresume(error
);
1913 m_slot_to_piece
[i
] = index
;
1914 m_piece_to_slot
[index
] = i
;
1915 if (i
!= index
) out_of_place
= true;
1917 else if (index
== unassigned
)
1919 if (m_storage_mode
== storage_mode_compact
)
1920 m_free_slots
.push_back(i
);
1924 TORRENT_ASSERT(index
== unallocated
);
1925 if (m_storage_mode
== storage_mode_compact
)
1926 m_unallocated_slots
.push_back(i
);
1932 for (int i
= 0; i
< slots
->list_size(); ++i
)
1934 lazy_entry
const* e
= slots
->list_at(i
);
1935 if (e
->type() != lazy_entry::int_t
)
1937 error
= "invalid entry type in slot list";
1938 return check_no_fastresume(error
);
1941 int index
= int(e
->int_value());
1942 if (index
!= i
&& index
>= 0)
1944 error
= "invalid slot index";
1945 return check_no_fastresume(error
);
1950 // This will corrupt the storage
1951 // use while debugging to find
1952 // states that cannot be scanned
1954 // m_storage->shuffle();
1956 if (m_storage_mode
== storage_mode_compact
)
1958 if (m_unallocated_slots
.empty()) switch_to_full_mode();
1962 TORRENT_ASSERT(m_free_slots
.empty());
1963 TORRENT_ASSERT(m_unallocated_slots
.empty());
1967 // in this case we're in full allocation mode, but
1968 // we're resuming a compact allocated storage
1969 m_state
= state_expand_pieces
;
1971 error
= "pieces needs to be reordered";
1972 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
1973 return need_full_check
;
1978 else if (m_storage_mode
== storage_mode_compact
)
1981 lazy_entry
const* pieces
= rd
.dict_find("pieces");
1982 if (pieces
== 0 || pieces
->type() != lazy_entry::string_t
)
1984 error
= "missing pieces entry";
1985 return check_no_fastresume(error
);
1988 if ((int)pieces
->string_length() != m_files
.num_pieces())
1990 error
= "file has more slots than torrent (slots: "
1991 + boost::lexical_cast
<std::string
>(pieces
->string_length()) + " size: "
1992 + boost::lexical_cast
<std::string
>(m_files
.num_pieces()) + " )";
1993 return check_no_fastresume(error
);
1996 int num_pieces
= int(m_files
.num_pieces());
1997 m_slot_to_piece
.resize(num_pieces
, unallocated
);
1998 m_piece_to_slot
.resize(num_pieces
, has_no_slot
);
1999 char const* have_pieces
= pieces
->string_ptr();
2000 for (int i
= 0; i
< num_pieces
; ++i
)
2002 if (have_pieces
[i
] & 1)
2004 m_slot_to_piece
[i
] = i
;
2005 m_piece_to_slot
[i
] = i
;
2009 m_free_slots
.push_back(i
);
2012 if (m_unallocated_slots
.empty()) switch_to_full_mode();
2015 return check_init_storage(error
);
2021 check_fastresume() ----------+
2025 | +------------+ +---------------+
2026 | | full_check |-->| expand_pieses |
2027 | +------------+ +---------------+
2030 | +--------------+ |
2031 +->| finished | <------+
2036 // performs the full check and full allocation
2037 // (if necessary). returns true if finished and
2038 // false if it should be called again
2039 // the second return value is the progress the
2040 // file check is at. 0 is nothing done, and 1
2042 int piece_manager::check_files(int& current_slot
, int& have_piece
, std::string
& error
)
2044 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2046 current_slot
= m_current_slot
;
2048 if (m_state
== state_expand_pieces
)
2052 if (m_scratch_piece
>= 0)
2054 int piece
= m_scratch_piece
;
2055 int other_piece
= m_slot_to_piece
[piece
];
2056 m_scratch_piece
= -1;
2058 if (other_piece
>= 0)
2060 if (m_scratch_buffer2
.empty())
2061 m_scratch_buffer2
.resize(m_files
.piece_length());
2063 int piece_size
= m_files
.piece_size(other_piece
);
2064 if (m_storage
->read(&m_scratch_buffer2
[0], piece
, 0, piece_size
)
2067 error
= m_storage
->error().message();
2068 TORRENT_ASSERT(!error
.empty());
2069 return fatal_disk_error
;
2071 m_scratch_piece
= other_piece
;
2072 m_piece_to_slot
[other_piece
] = unassigned
;
2075 // the slot where this piece belongs is
2076 // free. Just move the piece there.
2077 int piece_size
= m_files
.piece_size(piece
);
2078 if (m_storage
->write(&m_scratch_buffer
[0], piece
, 0, piece_size
) != piece_size
)
2080 error
= m_storage
->error().message();
2081 TORRENT_ASSERT(!error
.empty());
2082 return fatal_disk_error
;
2084 m_piece_to_slot
[piece
] = piece
;
2085 m_slot_to_piece
[piece
] = piece
;
2087 if (other_piece
>= 0)
2088 m_scratch_buffer
.swap(m_scratch_buffer2
);
2090 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2091 return need_full_check
;
2094 while (m_current_slot
< m_files
.num_pieces()
2095 && (m_slot_to_piece
[m_current_slot
] == m_current_slot
2096 || m_slot_to_piece
[m_current_slot
] < 0))
2101 if (m_current_slot
== m_files
.num_pieces())
2103 return check_init_storage(error
);
2106 TORRENT_ASSERT(m_current_slot
< m_files
.num_pieces());
2108 int piece
= m_slot_to_piece
[m_current_slot
];
2109 TORRENT_ASSERT(piece
>= 0);
2110 int other_piece
= m_slot_to_piece
[piece
];
2111 if (other_piece
>= 0)
2113 // there is another piece in the slot
2114 // where this one goes. Store it in the scratch
2115 // buffer until next iteration.
2116 if (m_scratch_buffer
.empty())
2117 m_scratch_buffer
.resize(m_files
.piece_length());
2119 int piece_size
= m_files
.piece_size(other_piece
);
2120 if (m_storage
->read(&m_scratch_buffer
[0], piece
, 0, piece_size
) != piece_size
)
2122 error
= m_storage
->error().message();
2123 TORRENT_ASSERT(!error
.empty());
2124 return fatal_disk_error
;
2126 m_scratch_piece
= other_piece
;
2127 m_piece_to_slot
[other_piece
] = unassigned
;
2130 // the slot where this piece belongs is
2131 // free. Just move the piece there.
2132 m_storage
->move_slot(m_current_slot
, piece
);
2133 m_piece_to_slot
[piece
] = piece
;
2134 m_slot_to_piece
[m_current_slot
] = unassigned
;
2135 m_slot_to_piece
[piece
] = piece
;
2137 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2138 return need_full_check
;
2141 TORRENT_ASSERT(m_state
== state_full_check
);
2143 int skip
= check_one_piece(have_piece
);
2144 TORRENT_ASSERT(m_current_slot
<= m_files
.num_pieces());
2148 error
= m_storage
->error().message();
2149 TORRENT_ASSERT(!error
.empty());
2150 return fatal_disk_error
;
2156 // skip means that the piece we checked failed to be read from disk
2157 // completely. We should skip all pieces belonging to that file.
2158 // find the file that failed, and skip all the pieces in that file
2159 size_type file_offset
= 0;
2160 size_type current_offset
= size_type(m_current_slot
) * m_files
.piece_length();
2161 for (file_storage::iterator i
= m_files
.begin()
2162 , end(m_files
.end()); i
!= end
; ++i
)
2164 file_offset
+= i
->size
;
2165 if (file_offset
> current_offset
) break;
2168 TORRENT_ASSERT(file_offset
> current_offset
);
2169 int skip_blocks
= static_cast<int>(
2170 (file_offset
- current_offset
+ m_files
.piece_length() - 1)
2171 / m_files
.piece_length());
2172 TORRENT_ASSERT(skip_blocks
>= 1);
2174 if (m_storage_mode
== storage_mode_compact
)
2176 for (int i
= m_current_slot
; i
< m_current_slot
+ skip_blocks
; ++i
)
2178 TORRENT_ASSERT(m_slot_to_piece
[i
] == unallocated
);
2179 m_unallocated_slots
.push_back(i
);
2183 // current slot will increase by one at the end of the for-loop too
2184 m_current_slot
+= skip_blocks
- 1;
2185 TORRENT_ASSERT(m_current_slot
<= m_files
.num_pieces());
2189 current_slot
= m_current_slot
;
2191 if (m_current_slot
>= m_files
.num_pieces())
2193 TORRENT_ASSERT(m_current_slot
== m_files
.num_pieces());
2195 // clear the memory we've been using
2196 std::vector
<char>().swap(m_piece_data
);
2197 std::multimap
<sha1_hash
, int>().swap(m_hash_to_piece
);
2199 if (m_storage_mode
!= storage_mode_compact
)
2201 if (!m_out_of_place
)
2203 // if no piece is out of place
2204 // since we're in full allocation mode, we can
2205 // forget the piece allocation tables
2207 std::vector
<int>().swap(m_piece_to_slot
);
2208 std::vector
<int>().swap(m_slot_to_piece
);
2209 return check_init_storage(error
);
2213 // in this case we're in full allocation mode, but
2214 // we're resuming a compact allocated storage
2215 m_state
= state_expand_pieces
;
2217 current_slot
= m_current_slot
;
2218 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2219 return need_full_check
;
2222 else if (m_unallocated_slots
.empty())
2224 switch_to_full_mode();
2226 return check_init_storage(error
);
2229 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2230 return need_full_check
;
2233 // -1=error 0=ok 1=skip
2234 int piece_manager::check_one_piece(int& have_piece
)
2236 // ------------------------
2237 // DO THE FULL CHECK
2238 // ------------------------
2240 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2241 TORRENT_ASSERT(int(m_slot_to_piece
.size()) == m_files
.num_pieces());
2242 TORRENT_ASSERT(have_piece
== -1);
2244 // initialization for the full check
2245 if (m_hash_to_piece
.empty())
2247 for (int i
= 0; i
< m_files
.num_pieces(); ++i
)
2248 m_hash_to_piece
.insert(std::make_pair(m_info
->hash_for_piece(i
), i
));
2251 m_piece_data
.resize(int(m_files
.piece_length()));
2252 int piece_size
= m_files
.piece_size(m_current_slot
);
2253 int num_read
= m_storage
->read(&m_piece_data
[0]
2254 , m_current_slot
, 0, piece_size
);
2258 if (m_storage
->error()
2259 && m_storage
->error() != error_code(ENOENT
, get_posix_category()))
2261 std::cerr
<< m_storage
->error().message() << std::endl
;
2267 // if the file is incomplete, skip the rest of it
2268 if (num_read
!= piece_size
)
2271 int piece_index
= identify_data(m_piece_data
, m_current_slot
);
2273 if (piece_index
>= 0) have_piece
= piece_index
;
2275 if (piece_index
!= m_current_slot
2276 && piece_index
>= 0)
2277 m_out_of_place
= true;
2279 TORRENT_ASSERT(piece_index
== unassigned
|| piece_index
>= 0);
2281 const bool this_should_move
= piece_index
>= 0 && m_slot_to_piece
[piece_index
] != unallocated
;
2282 const bool other_should_move
= m_piece_to_slot
[m_current_slot
] != has_no_slot
;
2284 // check if this piece should be swapped with any other slot
2285 // this section will ensure that the storage is correctly sorted
2286 // libtorrent will never leave the storage in a state that
2287 // requires this sorting, but other clients may.
2289 // example of worst case:
2290 // | m_current_slot = 5
2292 // +---+- - - +---+- - - +---+- -
2293 // | x | | 5 | | 3 | <- piece data in slots
2294 // +---+- - - +---+- - - +---+- -
2295 // 3 y 5 <- slot index
2297 // in this example, the data in the m_current_slot (5)
2298 // is piece 3. It has to be moved into slot 3. The data
2299 // in slot y (piece 5) should be moved into the m_current_slot.
2300 // and the data in slot 3 (piece x) should be moved to slot y.
2302 // there are three possible cases.
2303 // 1. There's another piece that should be placed into this slot
2304 // 2. This piece should be placed into another slot.
2305 // 3. There's another piece that should be placed into this slot
2306 // and this piece should be placed into another slot
2308 // swap piece_index with this slot
2311 if (this_should_move
&& !other_should_move
)
2313 TORRENT_ASSERT(piece_index
!= m_current_slot
);
2315 const int other_slot
= piece_index
;
2316 TORRENT_ASSERT(other_slot
>= 0);
2317 int other_piece
= m_slot_to_piece
[other_slot
];
2319 m_slot_to_piece
[other_slot
] = piece_index
;
2320 m_slot_to_piece
[m_current_slot
] = other_piece
;
2321 m_piece_to_slot
[piece_index
] = piece_index
;
2322 if (other_piece
>= 0) m_piece_to_slot
[other_piece
] = m_current_slot
;
2324 if (other_piece
== unassigned
)
2326 std::vector
<int>::iterator i
=
2327 std::find(m_free_slots
.begin(), m_free_slots
.end(), other_slot
);
2328 TORRENT_ASSERT(i
!= m_free_slots
.end());
2329 if (m_storage_mode
== storage_mode_compact
)
2331 m_free_slots
.erase(i
);
2332 m_free_slots
.push_back(m_current_slot
);
2337 if (other_piece
>= 0)
2338 ret
|= m_storage
->swap_slots(other_slot
, m_current_slot
);
2340 ret
|= m_storage
->move_slot(m_current_slot
, other_slot
);
2344 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2345 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2348 else if (!this_should_move
&& other_should_move
)
2350 TORRENT_ASSERT(piece_index
!= m_current_slot
);
2352 const int other_piece
= m_current_slot
;
2353 const int other_slot
= m_piece_to_slot
[other_piece
];
2354 TORRENT_ASSERT(other_slot
>= 0);
2356 m_slot_to_piece
[m_current_slot
] = other_piece
;
2357 m_slot_to_piece
[other_slot
] = piece_index
;
2358 m_piece_to_slot
[other_piece
] = m_current_slot
;
2360 if (piece_index
== unassigned
2361 && m_storage_mode
== storage_mode_compact
)
2362 m_free_slots
.push_back(other_slot
);
2365 if (piece_index
>= 0)
2367 m_piece_to_slot
[piece_index
] = other_slot
;
2368 ret
|= m_storage
->swap_slots(other_slot
, m_current_slot
);
2372 ret
|= m_storage
->move_slot(other_slot
, m_current_slot
);
2377 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2378 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2380 else if (this_should_move
&& other_should_move
)
2382 TORRENT_ASSERT(piece_index
!= m_current_slot
);
2383 TORRENT_ASSERT(piece_index
>= 0);
2385 const int piece1
= m_slot_to_piece
[piece_index
];
2386 const int piece2
= m_current_slot
;
2387 const int slot1
= piece_index
;
2388 const int slot2
= m_piece_to_slot
[piece2
];
2390 TORRENT_ASSERT(slot1
>= 0);
2391 TORRENT_ASSERT(slot2
>= 0);
2392 TORRENT_ASSERT(piece2
>= 0);
2396 // this means there are only two pieces involved in the swap
2397 TORRENT_ASSERT(piece1
>= 0);
2399 // movement diagram:
2400 // +-------------------------------+
2402 // +--> slot1 --> m_current_slot --+
2404 m_slot_to_piece
[slot1
] = piece_index
;
2405 m_slot_to_piece
[m_current_slot
] = piece1
;
2407 m_piece_to_slot
[piece_index
] = slot1
;
2408 m_piece_to_slot
[piece1
] = m_current_slot
;
2410 TORRENT_ASSERT(piece1
== m_current_slot
);
2411 TORRENT_ASSERT(piece_index
== slot1
);
2413 m_storage
->swap_slots(m_current_slot
, slot1
);
2415 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2416 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2420 TORRENT_ASSERT(slot1
!= slot2
);
2421 TORRENT_ASSERT(piece1
!= piece2
);
2423 // movement diagram:
2424 // +-----------------------------------------+
2426 // +--> slot1 --> slot2 --> m_current_slot --+
2428 m_slot_to_piece
[slot1
] = piece_index
;
2429 m_slot_to_piece
[slot2
] = piece1
;
2430 m_slot_to_piece
[m_current_slot
] = piece2
;
2432 m_piece_to_slot
[piece_index
] = slot1
;
2433 m_piece_to_slot
[m_current_slot
] = piece2
;
2435 if (piece1
== unassigned
)
2437 std::vector
<int>::iterator i
=
2438 std::find(m_free_slots
.begin(), m_free_slots
.end(), slot1
);
2439 TORRENT_ASSERT(i
!= m_free_slots
.end());
2440 if (m_storage_mode
== storage_mode_compact
)
2442 m_free_slots
.erase(i
);
2443 m_free_slots
.push_back(slot2
);
2450 m_piece_to_slot
[piece1
] = slot2
;
2451 ret
|= m_storage
->swap_slots3(m_current_slot
, slot1
, slot2
);
2455 ret
|= m_storage
->move_slot(m_current_slot
, slot1
);
2456 ret
|= m_storage
->move_slot(slot2
, m_current_slot
);
2461 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2462 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2467 TORRENT_ASSERT(m_piece_to_slot
[m_current_slot
] == has_no_slot
|| piece_index
!= m_current_slot
);
2468 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unallocated
);
2469 TORRENT_ASSERT(piece_index
== unassigned
|| m_piece_to_slot
[piece_index
] == has_no_slot
);
2471 // the slot was identified as piece 'piece_index'
2472 if (piece_index
!= unassigned
)
2473 m_piece_to_slot
[piece_index
] = m_current_slot
;
2474 else if (m_storage_mode
== storage_mode_compact
)
2475 m_free_slots
.push_back(m_current_slot
);
2477 m_slot_to_piece
[m_current_slot
] = piece_index
;
2479 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2480 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2485 void piece_manager::switch_to_full_mode()
2487 TORRENT_ASSERT(m_storage_mode
== storage_mode_compact
);
2488 TORRENT_ASSERT(m_unallocated_slots
.empty());
2489 // we have allocated all slots, switch to
2490 // full allocation mode in order to free
2491 // some unnecessary memory.
2492 m_storage_mode
= storage_mode_sparse
;
2493 std::vector
<int>().swap(m_unallocated_slots
);
2494 std::vector
<int>().swap(m_free_slots
);
2495 std::vector
<int>().swap(m_piece_to_slot
);
2496 std::vector
<int>().swap(m_slot_to_piece
);
2499 int piece_manager::allocate_slot_for_piece(int piece_index
)
2501 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
2503 if (m_storage_mode
!= storage_mode_compact
) return piece_index
;
2507 TORRENT_ASSERT(piece_index
>= 0);
2508 TORRENT_ASSERT(piece_index
< (int)m_piece_to_slot
.size());
2509 TORRENT_ASSERT(m_piece_to_slot
.size() == m_slot_to_piece
.size());
2511 int slot_index
= m_piece_to_slot
[piece_index
];
2513 if (slot_index
!= has_no_slot
)
2515 TORRENT_ASSERT(slot_index
>= 0);
2516 TORRENT_ASSERT(slot_index
< (int)m_slot_to_piece
.size());
2520 if (m_free_slots
.empty())
2523 TORRENT_ASSERT(!m_free_slots
.empty());
2526 std::vector
<int>::iterator
iter(
2528 m_free_slots
.begin()
2529 , m_free_slots
.end()
2532 if (iter
== m_free_slots
.end())
2534 TORRENT_ASSERT(m_slot_to_piece
[piece_index
] != unassigned
);
2535 TORRENT_ASSERT(!m_free_slots
.empty());
2536 iter
= m_free_slots
.end() - 1;
2538 // special case to make sure we don't use the last slot
2539 // when we shouldn't, since it's smaller than ordinary slots
2540 if (*iter
== m_files
.num_pieces() - 1 && piece_index
!= *iter
)
2542 if (m_free_slots
.size() == 1)
2544 TORRENT_ASSERT(m_free_slots
.size() > 1);
2545 // assumes that all allocated slots
2546 // are put at the end of the free_slots vector
2547 iter
= m_free_slots
.end() - 1;
2552 m_free_slots
.erase(iter
);
2554 TORRENT_ASSERT(m_slot_to_piece
[slot_index
] == unassigned
);
2556 m_slot_to_piece
[slot_index
] = piece_index
;
2557 m_piece_to_slot
[piece_index
] = slot_index
;
2559 // there is another piece already assigned to
2560 // the slot we are interested in, swap positions
2561 if (slot_index
!= piece_index
2562 && m_slot_to_piece
[piece_index
] >= 0)
2565 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2566 std::stringstream s
;
2568 s
<< "there is another piece at our slot, swapping..";
2570 s
<< "\n piece_index: ";
2572 s
<< "\n slot_index: ";
2574 s
<< "\n piece at our slot: ";
2575 s
<< m_slot_to_piece
[piece_index
];
2578 print_to_log(s
.str());
2582 int piece_at_our_slot
= m_slot_to_piece
[piece_index
];
2583 TORRENT_ASSERT(m_piece_to_slot
[piece_at_our_slot
] == piece_index
);
2586 m_slot_to_piece
[piece_index
]
2587 , m_slot_to_piece
[slot_index
]);
2590 m_piece_to_slot
[piece_index
]
2591 , m_piece_to_slot
[piece_at_our_slot
]);
2593 m_storage
->move_slot(piece_index
, slot_index
);
2595 TORRENT_ASSERT(m_slot_to_piece
[piece_index
] == piece_index
);
2596 TORRENT_ASSERT(m_piece_to_slot
[piece_index
] == piece_index
);
2598 slot_index
= piece_index
;
2600 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2604 TORRENT_ASSERT(slot_index
>= 0);
2605 TORRENT_ASSERT(slot_index
< (int)m_slot_to_piece
.size());
2607 if (m_free_slots
.empty() && m_unallocated_slots
.empty())
2608 switch_to_full_mode();
2613 bool piece_manager::allocate_slots(int num_slots
, bool abort_on_disk
)
2615 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
2616 TORRENT_ASSERT(num_slots
> 0);
2620 TORRENT_ASSERT(!m_unallocated_slots
.empty());
2621 TORRENT_ASSERT(m_storage_mode
== storage_mode_compact
);
2623 bool written
= false;
2625 for (int i
= 0; i
< num_slots
&& !m_unallocated_slots
.empty(); ++i
)
2629 int pos
= m_unallocated_slots
.front();
2630 TORRENT_ASSERT(m_slot_to_piece
[pos
] == unallocated
);
2631 TORRENT_ASSERT(m_piece_to_slot
[pos
] != pos
);
2633 int new_free_slot
= pos
;
2634 if (m_piece_to_slot
[pos
] != has_no_slot
)
2636 new_free_slot
= m_piece_to_slot
[pos
];
2637 m_storage
->move_slot(new_free_slot
, pos
);
2638 m_slot_to_piece
[pos
] = pos
;
2639 m_piece_to_slot
[pos
] = pos
;
2642 m_unallocated_slots
.erase(m_unallocated_slots
.begin());
2643 m_slot_to_piece
[new_free_slot
] = unassigned
;
2644 m_free_slots
.push_back(new_free_slot
);
2645 if (abort_on_disk
&& written
) break;
2648 TORRENT_ASSERT(m_free_slots
.size() > 0);
2652 int piece_manager::slot_for(int piece
) const
2654 if (m_storage_mode
!= storage_mode_compact
) return piece
;
2655 TORRENT_ASSERT(piece
< int(m_piece_to_slot
.size()));
2656 TORRENT_ASSERT(piece
>= 0);
2657 return m_piece_to_slot
[piece
];
2660 int piece_manager::piece_for(int slot
) const
2662 if (m_storage_mode
!= storage_mode_compact
) return slot
;
2663 TORRENT_ASSERT(slot
< int(m_slot_to_piece
.size()));
2664 TORRENT_ASSERT(slot
>= 0);
2665 return m_slot_to_piece
[slot
];
2669 void piece_manager::check_invariant() const
2671 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
2673 TORRENT_ASSERT(m_current_slot
<= m_files
.num_pieces());
2675 if (m_unallocated_slots
.empty()
2676 && m_free_slots
.empty()
2677 && m_state
== state_finished
)
2679 TORRENT_ASSERT(m_storage_mode
!= storage_mode_compact
2680 || m_files
.num_pieces() == 0);
2683 if (m_storage_mode
!= storage_mode_compact
)
2685 TORRENT_ASSERT(m_unallocated_slots
.empty());
2686 TORRENT_ASSERT(m_free_slots
.empty());
2689 if (m_storage_mode
!= storage_mode_compact
2690 && m_state
!= state_expand_pieces
2691 && m_state
!= state_full_check
)
2693 TORRENT_ASSERT(m_piece_to_slot
.empty());
2694 TORRENT_ASSERT(m_slot_to_piece
.empty());
2698 if (m_piece_to_slot
.empty()) return;
2700 TORRENT_ASSERT((int)m_piece_to_slot
.size() == m_files
.num_pieces());
2701 TORRENT_ASSERT((int)m_slot_to_piece
.size() == m_files
.num_pieces());
2703 for (std::vector
<int>::const_iterator i
= m_free_slots
.begin();
2704 i
!= m_free_slots
.end(); ++i
)
2706 TORRENT_ASSERT(*i
< (int)m_slot_to_piece
.size());
2707 TORRENT_ASSERT(*i
>= 0);
2708 TORRENT_ASSERT(m_slot_to_piece
[*i
] == unassigned
);
2709 TORRENT_ASSERT(std::find(i
+1, m_free_slots
.end(), *i
)
2710 == m_free_slots
.end());
2713 for (std::vector
<int>::const_iterator i
= m_unallocated_slots
.begin();
2714 i
!= m_unallocated_slots
.end(); ++i
)
2716 TORRENT_ASSERT(*i
< (int)m_slot_to_piece
.size());
2717 TORRENT_ASSERT(*i
>= 0);
2718 TORRENT_ASSERT(m_slot_to_piece
[*i
] == unallocated
);
2719 TORRENT_ASSERT(std::find(i
+1, m_unallocated_slots
.end(), *i
)
2720 == m_unallocated_slots
.end());
2723 for (int i
= 0; i
< m_files
.num_pieces(); ++i
)
2725 // Check domain of piece_to_slot's elements
2726 if (m_piece_to_slot
[i
] != has_no_slot
)
2728 TORRENT_ASSERT(m_piece_to_slot
[i
] >= 0);
2729 TORRENT_ASSERT(m_piece_to_slot
[i
] < (int)m_slot_to_piece
.size());
2732 // Check domain of slot_to_piece's elements
2733 if (m_slot_to_piece
[i
] != unallocated
2734 && m_slot_to_piece
[i
] != unassigned
)
2736 TORRENT_ASSERT(m_slot_to_piece
[i
] >= 0);
2737 TORRENT_ASSERT(m_slot_to_piece
[i
] < (int)m_piece_to_slot
.size());
2740 // do more detailed checks on piece_to_slot
2741 if (m_piece_to_slot
[i
] >= 0)
2743 TORRENT_ASSERT(m_slot_to_piece
[m_piece_to_slot
[i
]] == i
);
2744 if (m_piece_to_slot
[i
] != i
)
2746 TORRENT_ASSERT(m_slot_to_piece
[i
] == unallocated
);
2751 TORRENT_ASSERT(m_piece_to_slot
[i
] == has_no_slot
);
2754 // do more detailed checks on slot_to_piece
2756 if (m_slot_to_piece
[i
] >= 0)
2758 TORRENT_ASSERT(m_slot_to_piece
[i
] < (int)m_piece_to_slot
.size());
2759 TORRENT_ASSERT(m_piece_to_slot
[m_slot_to_piece
[i
]] == i
);
2760 #ifdef TORRENT_STORAGE_DEBUG
2763 m_unallocated_slots
.begin()
2764 , m_unallocated_slots
.end()
2765 , i
) == m_unallocated_slots
.end()
2769 m_free_slots
.begin()
2770 , m_free_slots
.end()
2771 , i
) == m_free_slots
.end()
2775 else if (m_slot_to_piece
[i
] == unallocated
)
2777 #ifdef TORRENT_STORAGE_DEBUG
2778 TORRENT_ASSERT(m_unallocated_slots
.empty()
2780 m_unallocated_slots
.begin()
2781 , m_unallocated_slots
.end()
2782 , i
) != m_unallocated_slots
.end())
2786 else if (m_slot_to_piece
[i
] == unassigned
)
2788 #ifdef TORRENT_STORAGE_DEBUG
2791 m_free_slots
.begin()
2792 , m_free_slots
.end()
2793 , i
) != m_free_slots
.end()
2799 TORRENT_ASSERT(false && "m_slot_to_piece[i] is invalid");
2805 #ifdef TORRENT_STORAGE_DEBUG
2806 void piece_manager::debug_log() const
2808 std::stringstream s
;
2810 s
<< "index\tslot\tpiece\n";
2812 for (int i
= 0; i
< m_files
.num_pieces(); ++i
)
2814 s
<< i
<< "\t" << m_slot_to_piece
[i
] << "\t";
2815 s
<< m_piece_to_slot
[i
] << "\n";
2818 s
<< "---------------------------------\n";
2820 print_to_log(s
.str());
2824 } // namespace libtorrent