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 std::vector
<boost::uint8_t> m_file_priority
;
440 fs::path m_save_path
;
441 // the file pool is typically stored in
442 // the session, to make all storage
443 // instances use the same pool
446 // temporary storage for moving pieces
447 buffer m_scratch_buffer
;
450 sha1_hash
storage::hash_for_slot(int slot
, partial_hash
& ph
, int piece_size
)
455 int slot_size1
= piece_size
;
456 m_scratch_buffer
.resize(slot_size1
);
457 read_impl(&m_scratch_buffer
[0], slot
, 0, slot_size1
, true);
459 partial
.update(&m_scratch_buffer
[0], ph
.offset
);
460 whole
.update(&m_scratch_buffer
[0], slot_size1
);
461 hasher partial_copy
= ph
.h
;
462 TORRENT_ASSERT(ph
.offset
== 0 || partial_copy
.final() == partial
.final());
464 int slot_size
= piece_size
- ph
.offset
;
467 m_scratch_buffer
.resize(slot_size
);
468 read_impl(&m_scratch_buffer
[0], slot
, ph
.offset
, slot_size
, true);
469 ph
.h
.update(&m_scratch_buffer
[0], slot_size
);
472 sha1_hash ret
= ph
.h
.final();
473 TORRENT_ASSERT(ret
== whole
.final());
480 bool storage::initialize(bool allocate_files
)
483 // first, create all missing directories
485 for (file_storage::iterator file_iter
= files().begin(),
486 end_iter
= files().end(); file_iter
!= end_iter
; ++file_iter
)
488 fs::path dir
= (m_save_path
/ file_iter
->path
).branch_path();
490 if (dir
!= last_path
)
493 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
495 if (!exists_win(last_path
))
496 create_directories_win(last_path
);
497 #elif TORRENT_USE_WPATH
499 fs::wpath wp
= safe_convert(last_path
.string());
501 create_directories(wp
);
504 if (!exists(last_path
))
505 create_directories(last_path
);
509 // if the file is empty, just create it. But also make sure
510 // the directory exists.
511 if (file_iter
->size
== 0)
513 file(m_save_path
/ file_iter
->path
, file::out
, ec
);
516 set_error(m_save_path
/ file_iter
->path
, ec
);
522 #ifndef BOOST_NO_EXCEPTIONS
525 // don't allocate files with priority 0
526 int file_index
= file_iter
- files().begin();
527 if (allocate_files
&& (m_file_priority
.size() <= file_index
528 || m_file_priority
[file_index
] > 0))
531 boost::shared_ptr
<file
> f
= m_pool
.open_file(this
532 , m_save_path
/ file_iter
->path
, file::in
| file::out
, ec
);
533 if (ec
) set_error(m_save_path
/ file_iter
->path
, ec
);
536 f
->set_size(file_iter
->size
, ec
);
537 if (ec
) set_error(m_save_path
/ file_iter
->path
, ec
);
540 #ifndef BOOST_NO_EXCEPTIONS
542 catch (std::exception
& e
)
544 set_error(m_save_path
/ file_iter
->path
545 , error_code(errno
, get_posix_category()));
550 std::vector
<boost::uint8_t>().swap(m_file_priority
);
551 // close files that were opened in write mode
552 m_pool
.release(this);
556 bool storage::rename_file(int index
, std::string
const& new_filename
)
558 if (index
< 0 || index
>= m_files
.num_files()) return true;
559 fs::path old_name
= m_save_path
/ files().at(index
).path
;
560 m_pool
.release(old_name
);
562 #if TORRENT_USE_WPATH
563 fs::wpath old_path
= safe_convert(old_name
.string());
564 fs::wpath new_path
= safe_convert((m_save_path
/ new_filename
).string());
566 fs::path
const& old_path
= old_name
;
567 fs::path new_path
= m_save_path
/ new_filename
;
570 #ifndef BOOST_NO_EXCEPTIONS
574 rename(old_path
, new_path
);
577 rename(old_path, new_path, ec);
580 set_error(old_path, ec);
585 { m_mapped_files
.reset(new file_storage(m_files
)); }
586 m_mapped_files
->rename_file(index
, new_filename
);
587 #ifndef BOOST_NO_EXCEPTIONS
589 catch (std::exception
& e
)
591 set_error(old_name
, error_code(errno
, get_posix_category()));
598 bool storage::release_files()
600 m_pool
.release(this);
601 buffer().swap(m_scratch_buffer
);
605 bool storage::delete_files()
607 // make sure we don't have the files open
608 m_pool
.release(this);
609 buffer().swap(m_scratch_buffer
);
612 std::string error_file
;
614 // delete the files from disk
615 std::set
<std::string
> directories
;
616 typedef std::set
<std::string
>::iterator iter_t
;
617 for (file_storage::iterator i
= files().begin()
618 , end(files().end()); i
!= end
; ++i
)
620 std::string p
= (m_save_path
/ i
->path
).string();
621 fs::path bp
= i
->path
.branch_path();
622 std::pair
<iter_t
, bool> ret
;
624 while (ret
.second
&& !bp
.empty())
626 std::pair
<iter_t
, bool> ret
= directories
.insert((m_save_path
/ bp
).string());
627 bp
= bp
.branch_path();
629 #if TORRENT_USE_WPATH
631 { fs::remove(safe_convert(p
)); }
632 catch (std::exception
& e
)
638 if (std::remove(p
.c_str()) != 0 && errno
!= ENOENT
)
646 // remove the directories. Reverse order to delete
647 // subdirectories first
649 for (std::set
<std::string
>::reverse_iterator i
= directories
.rbegin()
650 , end(directories
.rend()); i
!= end
; ++i
)
652 #if TORRENT_USE_WPATH
654 { fs::remove(safe_convert(*i
)); }
655 catch (std::exception
& e
)
661 if (std::remove(i
->c_str()) != 0 && errno
!= ENOENT
)
671 m_error
= error_code(error
, get_posix_category());
672 m_error_file
.swap(error_file
);
678 bool storage::write_resume_data(entry
& rd
) const
680 TORRENT_ASSERT(rd
.type() == entry::dictionary_t
);
682 std::vector
<std::pair
<size_type
, std::time_t> > file_sizes
683 = get_filesizes(files(), m_save_path
);
685 entry::list_type
& fl
= rd
["file sizes"].list();
686 for (std::vector
<std::pair
<size_type
, std::time_t> >::iterator i
687 = file_sizes
.begin(), end(file_sizes
.end()); i
!= end
; ++i
)
690 p
.push_back(entry(i
->first
));
691 p
.push_back(entry(i
->second
));
692 fl
.push_back(entry(p
));
697 entry::list_type
& fl
= rd
["mapped_files"].list();
698 for (file_storage::iterator i
= m_mapped_files
->begin()
699 , end(m_mapped_files
->end()); i
!= end
; ++i
)
701 fl
.push_back(i
->path
.string());
708 bool storage::verify_resume_data(lazy_entry
const& rd
, std::string
& error
)
710 lazy_entry
const* file_priority
= rd
.dict_find_list("file_priority");
711 if (file_priority
&& file_priority
->list_size()
712 == files().num_files())
714 m_file_priority
.resize(file_priority
->list_size());
715 for (int i
= 0; i
< file_priority
->list_size(); ++i
)
716 m_file_priority
[i
] = file_priority
->list_int_value_at(i
, 1);
719 lazy_entry
const* mapped_files
= rd
.dict_find_list("mapped_files");
720 if (mapped_files
&& mapped_files
->list_size() == m_files
.num_files())
723 { m_mapped_files
.reset(new file_storage(m_files
)); }
724 for (int i
= 0; i
< m_files
.num_files(); ++i
)
725 m_mapped_files
->rename_file(i
, mapped_files
->list_string_value_at(i
));
728 std::vector
<std::pair
<size_type
, std::time_t> > file_sizes
;
729 lazy_entry
const* file_sizes_ent
= rd
.dict_find_list("file sizes");
730 if (file_sizes_ent
== 0)
732 error
= "missing or invalid 'file sizes' entry in resume data";
736 for (int i
= 0; i
< file_sizes_ent
->list_size(); ++i
)
738 lazy_entry
const* e
= file_sizes_ent
->list_at(i
);
739 if (e
->type() != lazy_entry::list_t
740 || e
->list_size() != 2
741 || e
->list_at(0)->type() != lazy_entry::int_t
742 || e
->list_at(1)->type() != lazy_entry::int_t
)
744 file_sizes
.push_back(std::pair
<size_type
, std::time_t>(
745 e
->list_int_value_at(0), std::time_t(e
->list_int_value_at(1))));
748 if (file_sizes
.empty())
750 error
= "the number of files in resume data is 0";
756 lazy_entry
const* slots
= rd
.dict_find_list("slots");
759 if (int(slots
->list_size()) == m_files
.num_pieces())
762 for (int i
= 0; i
< slots
->list_size(); ++i
)
764 lazy_entry
const* e
= slots
->list_at(i
);
765 if (e
->list_int_value_at(i
, -1) >= 0) continue;
771 else if (lazy_entry
const* pieces
= rd
.dict_find_string("pieces"))
773 if (int(pieces
->string_length()) == m_files
.num_pieces())
776 char const* p
= pieces
->string_ptr();
777 for (int i
= 0; i
< pieces
->string_length(); ++i
)
779 if ((p
[i
] & 1) == 1) continue;
787 error
= "missing 'slots' and 'pieces' entry in resume data";
791 bool full_allocation_mode
= false;
792 if (rd
.dict_find_string_value("allocation") != "compact")
793 full_allocation_mode
= true;
797 if (files().num_files() != (int)file_sizes
.size())
799 error
= "the number of files does not match the torrent (num: "
800 + boost::lexical_cast
<std::string
>(file_sizes
.size()) + " actual: "
801 + boost::lexical_cast
<std::string
>(files().num_files()) + ")";
805 std::vector
<std::pair
<size_type
, std::time_t> >::iterator
806 fs
= file_sizes
.begin();
807 // the resume data says we have the entire torrent
808 // make sure the file sizes are the right ones
809 for (file_storage::iterator i
= files().begin()
810 , end(files().end()); i
!= end
; ++i
, ++fs
)
812 if (i
->size
!= fs
->first
)
814 error
= "file size for '" + i
->path
.native_file_string()
815 + "' was expected to be "
816 + boost::lexical_cast
<std::string
>(i
->size
) + " bytes";
822 return match_filesizes(files(), m_save_path
, file_sizes
823 , !full_allocation_mode
, &error
);
826 // returns true on success
827 bool storage::move_storage(fs::path save_path
)
829 #if TORRENT_USE_WPATH
837 save_path
= complete(save_path
);
839 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
840 std::wstring
wsave_path(safe_convert(save_path
.native_file_string()));
841 if (!exists_win(save_path
))
842 CreateDirectory(wsave_path
.c_str(), 0);
843 else if ((GetFileAttributes(wsave_path
.c_str()) & FILE_ATTRIBUTE_DIRECTORY
) == 0)
845 #elif TORRENT_USE_WPATH
846 fs::wpath wp
= safe_convert(save_path
.string());
848 create_directory(wp
);
849 else if (!is_directory(wp
))
852 if (!exists(save_path
))
853 create_directory(save_path
);
854 else if (!is_directory(save_path
))
858 m_pool
.release(this);
860 #if TORRENT_USE_WPATH
861 old_path
= safe_convert((m_save_path
/ files().name()).string());
862 new_path
= safe_convert((save_path
/ files().name()).string());
864 old_path
= m_save_path
/ files().name();
865 new_path
= save_path
/ files().name();
868 #ifndef BOOST_NO_EXCEPTIONS
872 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
873 rename_win(old_path
, new_path
);
875 rename(old_path
, new_path
);
877 m_save_path
= save_path
;
879 #ifndef BOOST_NO_EXCEPTIONS
881 catch (std::exception
& e
)
884 recursive_copy(old_path
, new_path
, ec
);
887 set_error(m_save_path
/ files().name(), ec
);
890 m_save_path
= save_path
;
891 recursive_remove(old_path
);
899 void storage::shuffle()
901 int num_pieces = files().num_pieces();
903 std::vector<int> pieces(num_pieces);
904 for (std::vector<int>::iterator i = pieces.begin();
905 i != pieces.end(); ++i)
907 *i = static_cast<int>(i - pieces.begin());
909 std::srand((unsigned int)std::time(0));
910 std::vector<int> targets(pieces);
911 std::random_shuffle(pieces.begin(), pieces.end());
912 std::random_shuffle(targets.begin(), targets.end());
914 for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i)
916 const int slot_index = targets[i];
917 const int piece_index = pieces[i];
918 const int slot_size =static_cast<int>(m_files.piece_size(slot_index));
919 std::vector<char> buf(slot_size);
920 read(&buf[0], piece_index, 0, slot_size);
921 write(&buf[0], slot_index, 0, slot_size);
927 bool storage::move_slot(int src_slot
, int dst_slot
)
929 int piece_size
= m_files
.piece_size(dst_slot
);
930 m_scratch_buffer
.resize(piece_size
);
931 int ret1
= read_impl(&m_scratch_buffer
[0], src_slot
, 0, piece_size
, true);
932 int ret2
= write(&m_scratch_buffer
[0], dst_slot
, 0, piece_size
);
933 return ret1
!= piece_size
|| ret2
!= piece_size
;
936 bool storage::swap_slots(int slot1
, int slot2
)
938 // the size of the target slot is the size of the piece
939 int piece_size
= m_files
.piece_length();
940 int piece1_size
= m_files
.piece_size(slot2
);
941 int piece2_size
= m_files
.piece_size(slot1
);
942 m_scratch_buffer
.resize(piece_size
* 2);
943 int ret1
= read_impl(&m_scratch_buffer
[0], slot1
, 0, piece1_size
, true);
944 int ret2
= read_impl(&m_scratch_buffer
[piece_size
], slot2
, 0, piece2_size
, true);
945 int ret3
= write(&m_scratch_buffer
[0], slot2
, 0, piece1_size
);
946 int ret4
= write(&m_scratch_buffer
[piece_size
], slot1
, 0, piece2_size
);
947 return ret1
!= piece1_size
|| ret2
!= piece2_size
948 || ret3
!= piece1_size
|| ret4
!= piece2_size
;
951 bool storage::swap_slots3(int slot1
, int slot2
, int slot3
)
953 // the size of the target slot is the size of the piece
954 int piece_size
= m_files
.piece_length();
955 int piece1_size
= m_files
.piece_size(slot2
);
956 int piece2_size
= m_files
.piece_size(slot3
);
957 int piece3_size
= m_files
.piece_size(slot1
);
958 m_scratch_buffer
.resize(piece_size
* 2);
959 int ret1
= read_impl(&m_scratch_buffer
[0], slot1
, 0, piece1_size
, true);
960 int ret2
= read_impl(&m_scratch_buffer
[piece_size
], slot2
, 0, piece2_size
, true);
961 int ret3
= write(&m_scratch_buffer
[0], slot2
, 0, piece1_size
);
962 int ret4
= read_impl(&m_scratch_buffer
[0], slot3
, 0, piece3_size
, true);
963 int ret5
= write(&m_scratch_buffer
[piece_size
], slot3
, 0, piece2_size
);
964 int ret6
= write(&m_scratch_buffer
[0], slot1
, 0, piece3_size
);
965 return ret1
!= piece1_size
|| ret2
!= piece2_size
966 || ret3
!= piece1_size
|| ret4
!= piece3_size
967 || ret5
!= piece2_size
|| ret6
!= piece3_size
;
976 return read_impl(buf
, slot
, offset
, size
, false);
979 int storage::read_impl(
986 TORRENT_ASSERT(buf
!= 0);
987 TORRENT_ASSERT(slot
>= 0 && slot
< m_files
.num_pieces());
988 TORRENT_ASSERT(offset
>= 0);
989 TORRENT_ASSERT(offset
< m_files
.piece_size(slot
));
990 TORRENT_ASSERT(size
> 0);
993 std::vector
<file_slice
> slices
994 = files().map_block(slot
, offset
, size
);
995 TORRENT_ASSERT(!slices
.empty());
998 size_type start
= slot
* (size_type
)m_files
.piece_length() + offset
;
999 TORRENT_ASSERT(start
+ size
<= m_files
.total_size());
1001 // find the file iterator and file offset
1002 size_type file_offset
= start
;
1003 std::vector
<file_entry
>::const_iterator file_iter
;
1005 for (file_iter
= files().begin();;)
1007 if (file_offset
< file_iter
->size
)
1010 file_offset
-= file_iter
->size
;
1016 boost::shared_ptr
<file
> in(m_pool
.open_file(
1017 this, m_save_path
/ file_iter
->path
, file::in
, ec
));
1020 set_error(m_save_path
/ file_iter
->path
, ec
);
1023 TORRENT_ASSERT(file_offset
< file_iter
->size
);
1024 TORRENT_ASSERT(slices
[0].offset
== file_offset
+ file_iter
->file_base
);
1026 size_type new_pos
= in
->seek(file_offset
+ file_iter
->file_base
, file::begin
, ec
);
1027 if (new_pos
!= file_offset
+ file_iter
->file_base
|| ec
)
1029 // the file was not big enough
1032 set_error(m_save_path
/ file_iter
->path
, ec
);
1035 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
1040 size_type in_tell
= in
->tell(ec
);
1041 TORRENT_ASSERT(in_tell
== file_offset
+ file_iter
->file_base
&& !ec
);
1044 int left_to_read
= size
;
1045 int slot_size
= static_cast<int>(m_files
.piece_size(slot
));
1047 if (offset
+ left_to_read
> slot_size
)
1048 left_to_read
= slot_size
- offset
;
1050 TORRENT_ASSERT(left_to_read
>= 0);
1052 size_type result
= left_to_read
;
1058 while (left_to_read
> 0)
1060 int read_bytes
= left_to_read
;
1061 if (file_offset
+ read_bytes
> file_iter
->size
)
1062 read_bytes
= static_cast<int>(file_iter
->size
- file_offset
);
1067 TORRENT_ASSERT(int(slices
.size()) > counter
);
1068 size_type slice_size
= slices
[counter
].size
;
1069 TORRENT_ASSERT(slice_size
== read_bytes
);
1070 TORRENT_ASSERT(files().at(slices
[counter
].file_index
).path
1071 == file_iter
->path
);
1074 int actual_read
= int(in
->read(buf
+ buf_pos
, read_bytes
, ec
));
1076 if (read_bytes
!= actual_read
|| ec
)
1078 // the file was not big enough
1079 if (actual_read
> 0) buf_pos
+= actual_read
;
1082 set_error(m_save_path
/ file_iter
->path
, ec
);
1085 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
1089 left_to_read
-= read_bytes
;
1090 buf_pos
+= read_bytes
;
1091 TORRENT_ASSERT(buf_pos
>= 0);
1092 file_offset
+= read_bytes
;
1095 if (left_to_read
> 0)
1099 // empty files are not returned by map_block, so if
1100 // this file was empty, don't increment the slice counter
1101 if (read_bytes
> 0) ++counter
;
1103 fs::path path
= m_save_path
/ file_iter
->path
;
1107 in
= m_pool
.open_file( this, path
, file::in
, ec
);
1110 set_error(path
, ec
);
1113 size_type pos
= in
->seek(file_iter
->file_base
, file::begin
, ec
);
1114 if (pos
!= file_iter
->file_base
|| ec
)
1118 set_error(m_save_path
/ file_iter
->path
, ec
);
1121 std::memset(buf
+ buf_pos
, 0, size
- buf_pos
);
1135 TORRENT_ASSERT(buf
!= 0);
1136 TORRENT_ASSERT(slot
>= 0);
1137 TORRENT_ASSERT(slot
< m_files
.num_pieces());
1138 TORRENT_ASSERT(offset
>= 0);
1139 TORRENT_ASSERT(size
> 0);
1142 std::vector
<file_slice
> slices
1143 = files().map_block(slot
, offset
, size
);
1144 TORRENT_ASSERT(!slices
.empty());
1147 size_type start
= slot
* (size_type
)m_files
.piece_length() + offset
;
1149 // find the file iterator and file offset
1150 size_type file_offset
= start
;
1151 std::vector
<file_entry
>::const_iterator file_iter
;
1153 for (file_iter
= files().begin();;)
1155 if (file_offset
< file_iter
->size
)
1158 file_offset
-= file_iter
->size
;
1160 TORRENT_ASSERT(file_iter
!= files().end());
1163 fs::path
p(m_save_path
/ file_iter
->path
);
1165 boost::shared_ptr
<file
> out
= m_pool
.open_file(
1166 this, p
, file::out
| file::in
, ec
);
1173 TORRENT_ASSERT(file_offset
< file_iter
->size
);
1174 TORRENT_ASSERT(slices
[0].offset
== file_offset
+ file_iter
->file_base
);
1176 size_type pos
= out
->seek(file_offset
+ file_iter
->file_base
, file::begin
, ec
);
1178 if (pos
!= file_offset
+ file_iter
->file_base
|| ec
)
1184 int left_to_write
= size
;
1185 int slot_size
= static_cast<int>(m_files
.piece_size(slot
));
1187 if (offset
+ left_to_write
> slot_size
)
1188 left_to_write
= slot_size
- offset
;
1190 TORRENT_ASSERT(left_to_write
>= 0);
1196 while (left_to_write
> 0)
1198 int write_bytes
= left_to_write
;
1199 if (file_offset
+ write_bytes
> file_iter
->size
)
1201 TORRENT_ASSERT(file_iter
->size
>= file_offset
);
1202 write_bytes
= static_cast<int>(file_iter
->size
- file_offset
);
1205 if (write_bytes
> 0)
1207 TORRENT_ASSERT(int(slices
.size()) > counter
);
1208 TORRENT_ASSERT(slices
[counter
].size
== write_bytes
);
1209 TORRENT_ASSERT(files().at(slices
[counter
].file_index
).path
1210 == file_iter
->path
);
1212 TORRENT_ASSERT(buf_pos
>= 0);
1213 TORRENT_ASSERT(write_bytes
>= 0);
1215 size_type written
= out
->write(buf
+ buf_pos
, write_bytes
, ec
);
1217 if (written
!= write_bytes
|| ec
)
1219 set_error(m_save_path
/ file_iter
->path
, ec
);
1223 left_to_write
-= write_bytes
;
1224 buf_pos
+= write_bytes
;
1225 TORRENT_ASSERT(buf_pos
>= 0);
1226 file_offset
+= write_bytes
;
1227 TORRENT_ASSERT(file_offset
<= file_iter
->size
);
1230 if (left_to_write
> 0)
1233 if (write_bytes
> 0) ++counter
;
1237 TORRENT_ASSERT(file_iter
!= files().end());
1238 fs::path p
= m_save_path
/ file_iter
->path
;
1241 out
= m_pool
.open_file(
1242 this, p
, file::out
| file::in
, ec
);
1250 size_type pos
= out
->seek(file_iter
->file_base
, file::begin
, ec
);
1252 if (pos
!= file_iter
->file_base
|| ec
)
1262 storage_interface
* default_storage_constructor(file_storage
const& fs
1263 , fs::path
const& path
, file_pool
& fp
)
1265 return new storage(fs
, path
, fp
);
1268 // -- piece_manager -----------------------------------------------------
1270 piece_manager::piece_manager(
1271 boost::shared_ptr
<void> const& torrent
1272 , boost::intrusive_ptr
<torrent_info
const> info
1273 , fs::path
const& save_path
1275 , disk_io_thread
& io
1276 , storage_constructor_type sc
1277 , storage_mode_t sm
)
1279 , m_files(m_info
->files())
1280 , m_storage(sc(m_files
, save_path
, fp
))
1281 , m_storage_mode(sm
)
1282 , m_save_path(complete(save_path
))
1283 , m_state(state_none
)
1285 , m_out_of_place(false)
1286 , m_scratch_piece(-1)
1287 , m_storage_constructor(sc
)
1289 , m_torrent(torrent
)
1293 piece_manager::~piece_manager()
1297 void piece_manager::async_save_resume_data(
1298 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1302 j
.action
= disk_io_job::save_resume_data
;
1303 m_io_thread
.add_job(j
, handler
);
1306 void piece_manager::async_clear_read_cache(
1307 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1311 j
.action
= disk_io_job::clear_read_cache
;
1312 m_io_thread
.add_job(j
, handler
);
1315 void piece_manager::async_release_files(
1316 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1320 j
.action
= disk_io_job::release_files
;
1321 m_io_thread
.add_job(j
, handler
);
1324 void piece_manager::async_delete_files(
1325 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1329 j
.action
= disk_io_job::delete_files
;
1330 m_io_thread
.add_job(j
, handler
);
1333 void piece_manager::async_move_storage(fs::path
const& p
1334 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1338 j
.action
= disk_io_job::move_storage
;
1340 m_io_thread
.add_job(j
, handler
);
1343 void piece_manager::async_check_fastresume(lazy_entry
const* resume_data
1344 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1346 TORRENT_ASSERT(resume_data
!= 0);
1349 j
.action
= disk_io_job::check_fastresume
;
1350 j
.buffer
= (char*)resume_data
;
1351 m_io_thread
.add_job(j
, handler
);
1354 void piece_manager::async_rename_file(int index
, std::string
const& name
1355 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1361 j
.action
= disk_io_job::rename_file
;
1362 m_io_thread
.add_job(j
, handler
);
1365 void piece_manager::async_check_files(
1366 boost::function
<void(int, disk_io_job
const&)> const& handler
)
1370 j
.action
= disk_io_job::check_files
;
1371 m_io_thread
.add_job(j
, handler
);
1374 void piece_manager::async_read(
1375 peer_request
const& r
1376 , boost::function
<void(int, disk_io_job
const&)> const& handler
1381 j
.action
= disk_io_job::read
;
1384 j
.buffer_size
= r
.length
;
1386 j
.priority
= priority
;
1387 // if a buffer is not specified, only one block can be read
1388 // since that is the size of the pool allocator's buffers
1389 TORRENT_ASSERT(r
.length
<= 16 * 1024);
1390 m_io_thread
.add_job(j
, handler
);
1392 boost::recursive_mutex::scoped_lock
l(m_mutex
);
1393 // if this assert is hit, it suggests
1394 // that check_files was not successful
1395 TORRENT_ASSERT(slot_for(r
.piece
) >= 0);
1399 void piece_manager::async_write(
1400 peer_request
const& r
1401 , disk_buffer_holder
& buffer
1402 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1404 TORRENT_ASSERT(r
.length
<= 16 * 1024);
1405 // the buffer needs to be allocated through the io_thread
1406 TORRENT_ASSERT(m_io_thread
.is_disk_buffer(buffer
.get()));
1410 j
.action
= disk_io_job::write
;
1413 j
.buffer_size
= r
.length
;
1414 j
.buffer
= buffer
.get();
1415 m_io_thread
.add_job(j
, handler
);
1419 void piece_manager::async_hash(int piece
1420 , boost::function
<void(int, disk_io_job
const&)> const& handler
)
1424 j
.action
= disk_io_job::hash
;
1427 m_io_thread
.add_job(j
, handler
);
1430 fs::path
piece_manager::save_path() const
1432 boost::recursive_mutex::scoped_lock
l(m_mutex
);
1436 sha1_hash
piece_manager::hash_for_piece_impl(int piece
)
1440 std::map
<int, partial_hash
>::iterator i
= m_piece_hasher
.find(piece
);
1441 if (i
!= m_piece_hasher
.end())
1444 m_piece_hasher
.erase(i
);
1447 int slot
= slot_for(piece
);
1448 TORRENT_ASSERT(slot
!= has_no_slot
);
1449 return m_storage
->hash_for_slot(slot
, ph
, m_files
.piece_size(piece
));
1452 int piece_manager::move_storage_impl(fs::path
const& save_path
)
1454 if (m_storage
->move_storage(save_path
))
1456 m_save_path
= fs::complete(save_path
);
1462 void piece_manager::write_resume_data(entry
& rd
) const
1464 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
1468 m_storage
->write_resume_data(rd
);
1470 if (m_storage_mode
== storage_mode_compact
)
1472 entry::list_type
& slots
= rd
["slots"].list();
1474 std::vector
<int>::const_reverse_iterator last
;
1475 for (last
= m_slot_to_piece
.rbegin();
1476 last
!= m_slot_to_piece
.rend(); ++last
)
1478 if (*last
!= unallocated
) break;
1481 for (std::vector
<int>::const_iterator i
=
1482 m_slot_to_piece
.begin();
1483 i
!= last
.base(); ++i
)
1485 slots
.push_back((*i
>= 0) ? *i
: unassigned
);
1489 rd
["allocation"] = m_storage_mode
== storage_mode_sparse
?"sparse"
1490 :m_storage_mode
== storage_mode_allocate
?"full":"compact";
1493 void piece_manager::mark_failed(int piece_index
)
1497 if (m_storage_mode
!= storage_mode_compact
) return;
1499 TORRENT_ASSERT(piece_index
>= 0 && piece_index
< (int)m_piece_to_slot
.size());
1500 int slot_index
= m_piece_to_slot
[piece_index
];
1501 TORRENT_ASSERT(slot_index
>= 0);
1503 m_slot_to_piece
[slot_index
] = unassigned
;
1504 m_piece_to_slot
[piece_index
] = has_no_slot
;
1505 m_free_slots
.push_back(slot_index
);
1508 int piece_manager::read_impl(
1514 TORRENT_ASSERT(buf
);
1515 TORRENT_ASSERT(offset
>= 0);
1516 TORRENT_ASSERT(size
> 0);
1517 int slot
= slot_for(piece_index
);
1518 return m_storage
->read(buf
, slot
, offset
, size
);
1521 int piece_manager::write_impl(
1527 TORRENT_ASSERT(buf
);
1528 TORRENT_ASSERT(offset
>= 0);
1529 TORRENT_ASSERT(size
> 0);
1530 TORRENT_ASSERT(piece_index
>= 0 && piece_index
< m_files
.num_pieces());
1532 int slot
= allocate_slot_for_piece(piece_index
);
1533 int ret
= m_storage
->write(buf
, slot
, offset
, size
);
1534 // only save the partial hash if the write succeeds
1535 if (ret
!= size
) return ret
;
1537 // std::ofstream out("partial_hash.log", std::ios::app);
1541 partial_hash
& ph
= m_piece_hasher
[piece_index
];
1542 TORRENT_ASSERT(ph
.offset
== 0);
1544 ph
.h
.update(buf
, size
);
1546 out << time_now_string() << " NEW ["
1548 << " p: " << piece_index
1549 << " off: " << offset
1550 << " size: " << size
1551 << " entries: " << m_piece_hasher.size()
1552 << " ]" << std::endl;
1557 std::map
<int, partial_hash
>::iterator i
= m_piece_hasher
.find(piece_index
);
1558 if (i
!= m_piece_hasher
.end())
1561 TORRENT_ASSERT(i
->second
.offset
> 0);
1562 int hash_offset
= i
->second
.offset
;
1563 TORRENT_ASSERT(offset
>= hash_offset
);
1565 if (offset
== i
->second
.offset
)
1568 out << time_now_string() << " UPDATING ["
1570 << " p: " << piece_index
1571 << " off: " << offset
1572 << " size: " << size
1573 << " entries: " << m_piece_hasher.size()
1574 << " ]" << std::endl;
1576 i
->second
.offset
+= size
;
1577 i
->second
.h
.update(buf
, size
);
1581 out << time_now_string() << " SKIPPING (out of order) ["
1583 << " p: " << piece_index
1584 << " off: " << offset
1585 << " size: " << size
1586 << " entries: " << m_piece_hasher.size()
1587 << " ]" << std::endl;
1592 out << time_now_string() << " SKIPPING (no entry) ["
1594 << " p: " << piece_index
1595 << " off: " << offset
1596 << " size: " << size
1597 << " entries: " << m_piece_hasher.size()
1598 << " ]" << std::endl;
1606 int piece_manager::identify_data(
1607 const std::vector
<char>& piece_data
1612 const int piece_size
= static_cast<int>(m_files
.piece_length());
1613 const int last_piece_size
= static_cast<int>(m_files
.piece_size(
1614 m_files
.num_pieces() - 1));
1616 TORRENT_ASSERT((int)piece_data
.size() >= last_piece_size
);
1618 // calculate a small digest, with the same
1619 // size as the last piece. And a large digest
1620 // which has the same size as a normal piece
1621 hasher small_digest
;
1622 small_digest
.update(&piece_data
[0], last_piece_size
);
1623 hasher
large_digest(small_digest
);
1624 TORRENT_ASSERT(piece_size
- last_piece_size
>= 0);
1625 if (piece_size
- last_piece_size
> 0)
1627 large_digest
.update(
1628 &piece_data
[last_piece_size
]
1629 , piece_size
- last_piece_size
);
1631 sha1_hash large_hash
= large_digest
.final();
1632 sha1_hash small_hash
= small_digest
.final();
1634 typedef std::multimap
<sha1_hash
, int>::const_iterator map_iter
;
1640 // makes the lookups for the small digest and the large digest
1641 boost::tie(begin1
, end1
) = m_hash_to_piece
.equal_range(small_hash
);
1642 boost::tie(begin2
, end2
) = m_hash_to_piece
.equal_range(large_hash
);
1644 // copy all potential piece indices into this vector
1645 std::vector
<int> matching_pieces
;
1646 for (map_iter i
= begin1
; i
!= end1
; ++i
)
1647 matching_pieces
.push_back(i
->second
);
1648 for (map_iter i
= begin2
; i
!= end2
; ++i
)
1649 matching_pieces
.push_back(i
->second
);
1651 // no piece matched the data in the slot
1652 if (matching_pieces
.empty())
1655 // ------------------------------------------
1656 // CHECK IF THE PIECE IS IN ITS CORRECT PLACE
1657 // ------------------------------------------
1660 matching_pieces
.begin()
1661 , matching_pieces
.end()
1662 , current_slot
) != matching_pieces
.end())
1664 // the current slot is among the matching pieces, so
1665 // we will assume that the piece is in the right place
1666 const int piece_index
= current_slot
;
1668 int other_slot
= m_piece_to_slot
[piece_index
];
1669 if (other_slot
>= 0)
1671 // we have already found a piece with
1674 // take one of the other matching pieces
1675 // that hasn't already been assigned
1676 int other_piece
= -1;
1677 for (std::vector
<int>::iterator i
= matching_pieces
.begin();
1678 i
!= matching_pieces
.end(); ++i
)
1680 if (m_piece_to_slot
[*i
] >= 0 || *i
== piece_index
) continue;
1684 if (other_piece
>= 0)
1686 // replace the old slot with 'other_piece'
1687 m_slot_to_piece
[other_slot
] = other_piece
;
1688 m_piece_to_slot
[other_piece
] = other_slot
;
1692 // this index is the only piece with this
1693 // hash. The previous slot we found with
1694 // this hash must be the same piece. Mark
1695 // that piece as unassigned, since this slot
1696 // is the correct place for the piece.
1697 m_slot_to_piece
[other_slot
] = unassigned
;
1698 if (m_storage_mode
== storage_mode_compact
)
1699 m_free_slots
.push_back(other_slot
);
1701 TORRENT_ASSERT(m_piece_to_slot
[piece_index
] != current_slot
);
1702 TORRENT_ASSERT(m_piece_to_slot
[piece_index
] >= 0);
1703 m_piece_to_slot
[piece_index
] = has_no_slot
;
1706 TORRENT_ASSERT(m_piece_to_slot
[piece_index
] == has_no_slot
);
1711 // find a matching piece that hasn't
1712 // already been assigned
1713 int free_piece
= unassigned
;
1714 for (std::vector
<int>::iterator i
= matching_pieces
.begin();
1715 i
!= matching_pieces
.end(); ++i
)
1717 if (m_piece_to_slot
[*i
] >= 0) continue;
1722 if (free_piece
>= 0)
1724 TORRENT_ASSERT(m_piece_to_slot
[free_piece
] == has_no_slot
);
1729 TORRENT_ASSERT(free_piece
== unassigned
);
1734 int piece_manager::check_no_fastresume(std::string
& error
)
1736 file_storage::iterator i
= m_files
.begin();
1737 file_storage::iterator end
= m_files
.end();
1739 for (; i
!= end
; ++i
)
1741 bool file_exists
= false;
1742 fs::path f
= m_save_path
/ i
->path
;
1743 #ifndef BOOST_NO_EXCEPTIONS
1747 #if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
1748 file_exists
= exists_win(f
);
1749 #elif TORRENT_USE_WPATH
1750 fs::wpath wf
= safe_convert(f
.string());
1751 file_exists
= exists(wf
);
1753 file_exists
= exists(f
);
1755 #ifndef BOOST_NO_EXCEPTIONS
1757 catch (std::exception
& e
)
1762 TORRENT_ASSERT(!error
.empty());
1763 return fatal_disk_error
;
1766 if (file_exists
&& i
->size
> 0)
1768 m_state
= state_full_check
;
1769 m_piece_to_slot
.clear();
1770 m_piece_to_slot
.resize(m_files
.num_pieces(), has_no_slot
);
1771 m_slot_to_piece
.clear();
1772 m_slot_to_piece
.resize(m_files
.num_pieces(), unallocated
);
1773 if (m_storage_mode
== storage_mode_compact
)
1775 m_unallocated_slots
.clear();
1776 m_free_slots
.clear();
1778 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
1779 return need_full_check
;
1783 if (m_storage_mode
== storage_mode_compact
)
1785 // in compact mode without checking, we need to
1786 // populate the unallocated list
1787 TORRENT_ASSERT(m_unallocated_slots
.empty());
1788 for (int i
= 0, end(m_files
.num_pieces()); i
< end
; ++i
)
1789 m_unallocated_slots
.push_back(i
);
1790 m_piece_to_slot
.clear();
1791 m_piece_to_slot
.resize(m_files
.num_pieces(), has_no_slot
);
1792 m_slot_to_piece
.clear();
1793 m_slot_to_piece
.resize(m_files
.num_pieces(), unallocated
);
1796 return check_init_storage(error
);
1799 int piece_manager::check_init_storage(std::string
& error
)
1801 if (m_storage
->initialize(m_storage_mode
== storage_mode_allocate
))
1803 error
= m_storage
->error().message();
1804 TORRENT_ASSERT(!error
.empty());
1805 return fatal_disk_error
;
1807 m_state
= state_finished
;
1808 buffer().swap(m_scratch_buffer
);
1809 buffer().swap(m_scratch_buffer2
);
1810 if (m_storage_mode
!= storage_mode_compact
)
1812 // if no piece is out of place
1813 // since we're in full allocation mode, we can
1814 // forget the piece allocation tables
1815 std::vector
<int>().swap(m_piece_to_slot
);
1816 std::vector
<int>().swap(m_slot_to_piece
);
1817 std::vector
<int>().swap(m_free_slots
);
1818 std::vector
<int>().swap(m_unallocated_slots
);
1823 // check if the fastresume data is up to date
1824 // if it is, use it and return true. If it
1825 // isn't return false and the full check
1827 int piece_manager::check_fastresume(
1828 lazy_entry
const& rd
, std::string
& error
)
1830 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
1834 TORRENT_ASSERT(m_files
.piece_length() > 0);
1836 // if we don't have any resume data, return
1837 if (rd
.type() == lazy_entry::none_t
) return check_no_fastresume(error
);
1839 if (rd
.type() != lazy_entry::dict_t
)
1841 error
= "invalid fastresume data (not a dictionary)";
1842 return check_no_fastresume(error
);
1845 int block_size
= (std::min
)(16 * 1024, m_files
.piece_length());
1846 int blocks_per_piece
= rd
.dict_find_int_value("blocks per piece", -1);
1847 if (blocks_per_piece
!= -1
1848 && blocks_per_piece
!= m_files
.piece_length() / block_size
)
1850 error
= "invalid 'blocks per piece' entry";
1851 return check_no_fastresume(error
);
1854 storage_mode_t storage_mode
= storage_mode_compact
;
1855 if (rd
.dict_find_string_value("allocation") != "compact")
1856 storage_mode
= storage_mode_sparse
;
1858 if (!m_storage
->verify_resume_data(rd
, error
))
1859 return check_no_fastresume(error
);
1861 // assume no piece is out of place (i.e. in a slot
1862 // other than the one it should be in)
1863 bool out_of_place
= false;
1865 // if we don't have a piece map, we need the slots
1866 // if we're in compact mode, we also need the slots map
1867 if (storage_mode
== storage_mode_compact
|| rd
.dict_find("pieces") == 0)
1870 lazy_entry
const* slots
= rd
.dict_find_list("slots");
1873 error
= "missing slot list";
1874 return check_no_fastresume(error
);
1877 if ((int)slots
->list_size() > m_files
.num_pieces())
1879 error
= "file has more slots than torrent (slots: "
1880 + boost::lexical_cast
<std::string
>(slots
->list_size()) + " size: "
1881 + boost::lexical_cast
<std::string
>(m_files
.num_pieces()) + " )";
1882 return check_no_fastresume(error
);
1885 if (m_storage_mode
== storage_mode_compact
)
1887 int num_pieces
= int(m_files
.num_pieces());
1888 m_slot_to_piece
.resize(num_pieces
, unallocated
);
1889 m_piece_to_slot
.resize(num_pieces
, has_no_slot
);
1890 for (int i
= 0; i
< slots
->list_size(); ++i
)
1892 lazy_entry
const* e
= slots
->list_at(i
);
1893 if (e
->type() != lazy_entry::int_t
)
1895 error
= "invalid entry type in slot list";
1896 return check_no_fastresume(error
);
1899 int index
= int(e
->int_value());
1900 if (index
>= num_pieces
|| index
< -2)
1902 error
= "too high index number in slot map (index: "
1903 + boost::lexical_cast
<std::string
>(index
) + " size: "
1904 + boost::lexical_cast
<std::string
>(num_pieces
) + ")";
1905 return check_no_fastresume(error
);
1909 m_slot_to_piece
[i
] = index
;
1910 m_piece_to_slot
[index
] = i
;
1911 if (i
!= index
) out_of_place
= true;
1913 else if (index
== unassigned
)
1915 if (m_storage_mode
== storage_mode_compact
)
1916 m_free_slots
.push_back(i
);
1920 TORRENT_ASSERT(index
== unallocated
);
1921 if (m_storage_mode
== storage_mode_compact
)
1922 m_unallocated_slots
.push_back(i
);
1928 for (int i
= 0; i
< slots
->list_size(); ++i
)
1930 lazy_entry
const* e
= slots
->list_at(i
);
1931 if (e
->type() != lazy_entry::int_t
)
1933 error
= "invalid entry type in slot list";
1934 return check_no_fastresume(error
);
1937 int index
= int(e
->int_value());
1938 if (index
!= i
&& index
>= 0)
1940 error
= "invalid slot index";
1941 return check_no_fastresume(error
);
1946 // This will corrupt the storage
1947 // use while debugging to find
1948 // states that cannot be scanned
1950 // m_storage->shuffle();
1952 if (m_storage_mode
== storage_mode_compact
)
1954 if (m_unallocated_slots
.empty()) switch_to_full_mode();
1958 TORRENT_ASSERT(m_free_slots
.empty());
1959 TORRENT_ASSERT(m_unallocated_slots
.empty());
1963 // in this case we're in full allocation mode, but
1964 // we're resuming a compact allocated storage
1965 m_state
= state_expand_pieces
;
1967 error
= "pieces needs to be reordered";
1968 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
1969 return need_full_check
;
1974 else if (m_storage_mode
== storage_mode_compact
)
1977 lazy_entry
const* pieces
= rd
.dict_find("pieces");
1978 if (pieces
== 0 || pieces
->type() != lazy_entry::string_t
)
1980 error
= "missing pieces entry";
1981 return check_no_fastresume(error
);
1984 if ((int)pieces
->string_length() != m_files
.num_pieces())
1986 error
= "file has more slots than torrent (slots: "
1987 + boost::lexical_cast
<std::string
>(pieces
->string_length()) + " size: "
1988 + boost::lexical_cast
<std::string
>(m_files
.num_pieces()) + " )";
1989 return check_no_fastresume(error
);
1992 int num_pieces
= int(m_files
.num_pieces());
1993 m_slot_to_piece
.resize(num_pieces
, unallocated
);
1994 m_piece_to_slot
.resize(num_pieces
, has_no_slot
);
1995 char const* have_pieces
= pieces
->string_ptr();
1996 for (int i
= 0; i
< num_pieces
; ++i
)
1998 if (have_pieces
[i
] & 1)
2000 m_slot_to_piece
[i
] = i
;
2001 m_piece_to_slot
[i
] = i
;
2005 m_free_slots
.push_back(i
);
2008 if (m_unallocated_slots
.empty()) switch_to_full_mode();
2011 return check_init_storage(error
);
2017 check_fastresume() ----------+
2021 | +------------+ +---------------+
2022 | | full_check |-->| expand_pieses |
2023 | +------------+ +---------------+
2026 | +--------------+ |
2027 +->| finished | <------+
2032 // performs the full check and full allocation
2033 // (if necessary). returns true if finished and
2034 // false if it should be called again
2035 // the second return value is the progress the
2036 // file check is at. 0 is nothing done, and 1
2038 int piece_manager::check_files(int& current_slot
, int& have_piece
, std::string
& error
)
2040 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2042 current_slot
= m_current_slot
;
2044 if (m_state
== state_expand_pieces
)
2048 if (m_scratch_piece
>= 0)
2050 int piece
= m_scratch_piece
;
2051 int other_piece
= m_slot_to_piece
[piece
];
2052 m_scratch_piece
= -1;
2054 if (other_piece
>= 0)
2056 if (m_scratch_buffer2
.empty())
2057 m_scratch_buffer2
.resize(m_files
.piece_length());
2059 int piece_size
= m_files
.piece_size(other_piece
);
2060 if (m_storage
->read(&m_scratch_buffer2
[0], piece
, 0, piece_size
)
2063 error
= m_storage
->error().message();
2064 TORRENT_ASSERT(!error
.empty());
2065 return fatal_disk_error
;
2067 m_scratch_piece
= other_piece
;
2068 m_piece_to_slot
[other_piece
] = unassigned
;
2071 // the slot where this piece belongs is
2072 // free. Just move the piece there.
2073 int piece_size
= m_files
.piece_size(piece
);
2074 if (m_storage
->write(&m_scratch_buffer
[0], piece
, 0, piece_size
) != piece_size
)
2076 error
= m_storage
->error().message();
2077 TORRENT_ASSERT(!error
.empty());
2078 return fatal_disk_error
;
2080 m_piece_to_slot
[piece
] = piece
;
2081 m_slot_to_piece
[piece
] = piece
;
2083 if (other_piece
>= 0)
2084 m_scratch_buffer
.swap(m_scratch_buffer2
);
2086 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2087 return need_full_check
;
2090 while (m_current_slot
< m_files
.num_pieces()
2091 && (m_slot_to_piece
[m_current_slot
] == m_current_slot
2092 || m_slot_to_piece
[m_current_slot
] < 0))
2097 if (m_current_slot
== m_files
.num_pieces())
2099 return check_init_storage(error
);
2102 TORRENT_ASSERT(m_current_slot
< m_files
.num_pieces());
2104 int piece
= m_slot_to_piece
[m_current_slot
];
2105 TORRENT_ASSERT(piece
>= 0);
2106 int other_piece
= m_slot_to_piece
[piece
];
2107 if (other_piece
>= 0)
2109 // there is another piece in the slot
2110 // where this one goes. Store it in the scratch
2111 // buffer until next iteration.
2112 if (m_scratch_buffer
.empty())
2113 m_scratch_buffer
.resize(m_files
.piece_length());
2115 int piece_size
= m_files
.piece_size(other_piece
);
2116 if (m_storage
->read(&m_scratch_buffer
[0], piece
, 0, piece_size
) != piece_size
)
2118 error
= m_storage
->error().message();
2119 TORRENT_ASSERT(!error
.empty());
2120 return fatal_disk_error
;
2122 m_scratch_piece
= other_piece
;
2123 m_piece_to_slot
[other_piece
] = unassigned
;
2126 // the slot where this piece belongs is
2127 // free. Just move the piece there.
2128 m_storage
->move_slot(m_current_slot
, piece
);
2129 m_piece_to_slot
[piece
] = piece
;
2130 m_slot_to_piece
[m_current_slot
] = unassigned
;
2131 m_slot_to_piece
[piece
] = piece
;
2133 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2134 return need_full_check
;
2137 TORRENT_ASSERT(m_state
== state_full_check
);
2139 int skip
= check_one_piece(have_piece
);
2140 TORRENT_ASSERT(m_current_slot
<= m_files
.num_pieces());
2144 error
= m_storage
->error().message();
2145 TORRENT_ASSERT(!error
.empty());
2146 return fatal_disk_error
;
2152 // skip means that the piece we checked failed to be read from disk
2153 // completely. We should skip all pieces belonging to that file.
2154 // find the file that failed, and skip all the pieces in that file
2155 size_type file_offset
= 0;
2156 size_type current_offset
= size_type(m_current_slot
) * m_files
.piece_length();
2157 for (file_storage::iterator i
= m_files
.begin()
2158 , end(m_files
.end()); i
!= end
; ++i
)
2160 file_offset
+= i
->size
;
2161 if (file_offset
> current_offset
) break;
2164 TORRENT_ASSERT(file_offset
> current_offset
);
2165 int skip_blocks
= static_cast<int>(
2166 (file_offset
- current_offset
+ m_files
.piece_length() - 1)
2167 / m_files
.piece_length());
2169 if (m_storage_mode
== storage_mode_compact
)
2171 for (int i
= m_current_slot
; i
< m_current_slot
+ skip_blocks
; ++i
)
2173 TORRENT_ASSERT(m_slot_to_piece
[i
] == unallocated
);
2174 m_unallocated_slots
.push_back(i
);
2178 // current slot will increase by one at the end of the for-loop too
2179 m_current_slot
+= skip_blocks
- 1;
2180 TORRENT_ASSERT(m_current_slot
<= m_files
.num_pieces());
2184 current_slot
= m_current_slot
;
2186 if (m_current_slot
>= m_files
.num_pieces())
2188 TORRENT_ASSERT(m_current_slot
== m_files
.num_pieces());
2190 // clear the memory we've been using
2191 std::vector
<char>().swap(m_piece_data
);
2192 std::multimap
<sha1_hash
, int>().swap(m_hash_to_piece
);
2194 if (m_storage_mode
!= storage_mode_compact
)
2196 if (!m_out_of_place
)
2198 // if no piece is out of place
2199 // since we're in full allocation mode, we can
2200 // forget the piece allocation tables
2202 std::vector
<int>().swap(m_piece_to_slot
);
2203 std::vector
<int>().swap(m_slot_to_piece
);
2204 return check_init_storage(error
);
2208 // in this case we're in full allocation mode, but
2209 // we're resuming a compact allocated storage
2210 m_state
= state_expand_pieces
;
2212 current_slot
= m_current_slot
;
2213 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2214 return need_full_check
;
2217 else if (m_unallocated_slots
.empty())
2219 switch_to_full_mode();
2221 return check_init_storage(error
);
2224 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2225 return need_full_check
;
2228 // -1=error 0=ok 1=skip
2229 int piece_manager::check_one_piece(int& have_piece
)
2231 // ------------------------
2232 // DO THE FULL CHECK
2233 // ------------------------
2235 TORRENT_ASSERT(int(m_piece_to_slot
.size()) == m_files
.num_pieces());
2236 TORRENT_ASSERT(int(m_slot_to_piece
.size()) == m_files
.num_pieces());
2237 TORRENT_ASSERT(have_piece
== -1);
2239 // initialization for the full check
2240 if (m_hash_to_piece
.empty())
2242 for (int i
= 0; i
< m_files
.num_pieces(); ++i
)
2243 m_hash_to_piece
.insert(std::make_pair(m_info
->hash_for_piece(i
), i
));
2246 m_piece_data
.resize(int(m_files
.piece_length()));
2247 int piece_size
= m_files
.piece_size(m_current_slot
);
2248 int num_read
= m_storage
->read(&m_piece_data
[0]
2249 , m_current_slot
, 0, piece_size
);
2253 if (m_storage
->error()
2254 && m_storage
->error() != error_code(ENOENT
, get_posix_category()))
2256 std::cerr
<< m_storage
->error().message() << std::endl
;
2262 // if the file is incomplete, skip the rest of it
2263 if (num_read
!= piece_size
)
2266 int piece_index
= identify_data(m_piece_data
, m_current_slot
);
2268 if (piece_index
>= 0) have_piece
= piece_index
;
2270 if (piece_index
!= m_current_slot
2271 && piece_index
>= 0)
2272 m_out_of_place
= true;
2274 TORRENT_ASSERT(piece_index
== unassigned
|| piece_index
>= 0);
2276 const bool this_should_move
= piece_index
>= 0 && m_slot_to_piece
[piece_index
] != unallocated
;
2277 const bool other_should_move
= m_piece_to_slot
[m_current_slot
] != has_no_slot
;
2279 // check if this piece should be swapped with any other slot
2280 // this section will ensure that the storage is correctly sorted
2281 // libtorrent will never leave the storage in a state that
2282 // requires this sorting, but other clients may.
2284 // example of worst case:
2285 // | m_current_slot = 5
2287 // +---+- - - +---+- - - +---+- -
2288 // | x | | 5 | | 3 | <- piece data in slots
2289 // +---+- - - +---+- - - +---+- -
2290 // 3 y 5 <- slot index
2292 // in this example, the data in the m_current_slot (5)
2293 // is piece 3. It has to be moved into slot 3. The data
2294 // in slot y (piece 5) should be moved into the m_current_slot.
2295 // and the data in slot 3 (piece x) should be moved to slot y.
2297 // there are three possible cases.
2298 // 1. There's another piece that should be placed into this slot
2299 // 2. This piece should be placed into another slot.
2300 // 3. There's another piece that should be placed into this slot
2301 // and this piece should be placed into another slot
2303 // swap piece_index with this slot
2306 if (this_should_move
&& !other_should_move
)
2308 TORRENT_ASSERT(piece_index
!= m_current_slot
);
2310 const int other_slot
= piece_index
;
2311 TORRENT_ASSERT(other_slot
>= 0);
2312 int other_piece
= m_slot_to_piece
[other_slot
];
2314 m_slot_to_piece
[other_slot
] = piece_index
;
2315 m_slot_to_piece
[m_current_slot
] = other_piece
;
2316 m_piece_to_slot
[piece_index
] = piece_index
;
2317 if (other_piece
>= 0) m_piece_to_slot
[other_piece
] = m_current_slot
;
2319 if (other_piece
== unassigned
)
2321 std::vector
<int>::iterator i
=
2322 std::find(m_free_slots
.begin(), m_free_slots
.end(), other_slot
);
2323 TORRENT_ASSERT(i
!= m_free_slots
.end());
2324 if (m_storage_mode
== storage_mode_compact
)
2326 m_free_slots
.erase(i
);
2327 m_free_slots
.push_back(m_current_slot
);
2332 if (other_piece
>= 0)
2333 ret
|= m_storage
->swap_slots(other_slot
, m_current_slot
);
2335 ret
|= m_storage
->move_slot(m_current_slot
, other_slot
);
2339 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2340 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2343 else if (!this_should_move
&& other_should_move
)
2345 TORRENT_ASSERT(piece_index
!= m_current_slot
);
2347 const int other_piece
= m_current_slot
;
2348 const int other_slot
= m_piece_to_slot
[other_piece
];
2349 TORRENT_ASSERT(other_slot
>= 0);
2351 m_slot_to_piece
[m_current_slot
] = other_piece
;
2352 m_slot_to_piece
[other_slot
] = piece_index
;
2353 m_piece_to_slot
[other_piece
] = m_current_slot
;
2355 if (piece_index
== unassigned
2356 && m_storage_mode
== storage_mode_compact
)
2357 m_free_slots
.push_back(other_slot
);
2360 if (piece_index
>= 0)
2362 m_piece_to_slot
[piece_index
] = other_slot
;
2363 ret
|= m_storage
->swap_slots(other_slot
, m_current_slot
);
2367 ret
|= m_storage
->move_slot(other_slot
, m_current_slot
);
2372 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2373 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2375 else if (this_should_move
&& other_should_move
)
2377 TORRENT_ASSERT(piece_index
!= m_current_slot
);
2378 TORRENT_ASSERT(piece_index
>= 0);
2380 const int piece1
= m_slot_to_piece
[piece_index
];
2381 const int piece2
= m_current_slot
;
2382 const int slot1
= piece_index
;
2383 const int slot2
= m_piece_to_slot
[piece2
];
2385 TORRENT_ASSERT(slot1
>= 0);
2386 TORRENT_ASSERT(slot2
>= 0);
2387 TORRENT_ASSERT(piece2
>= 0);
2391 // this means there are only two pieces involved in the swap
2392 TORRENT_ASSERT(piece1
>= 0);
2394 // movement diagram:
2395 // +-------------------------------+
2397 // +--> slot1 --> m_current_slot --+
2399 m_slot_to_piece
[slot1
] = piece_index
;
2400 m_slot_to_piece
[m_current_slot
] = piece1
;
2402 m_piece_to_slot
[piece_index
] = slot1
;
2403 m_piece_to_slot
[piece1
] = m_current_slot
;
2405 TORRENT_ASSERT(piece1
== m_current_slot
);
2406 TORRENT_ASSERT(piece_index
== slot1
);
2408 m_storage
->swap_slots(m_current_slot
, slot1
);
2410 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2411 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2415 TORRENT_ASSERT(slot1
!= slot2
);
2416 TORRENT_ASSERT(piece1
!= piece2
);
2418 // movement diagram:
2419 // +-----------------------------------------+
2421 // +--> slot1 --> slot2 --> m_current_slot --+
2423 m_slot_to_piece
[slot1
] = piece_index
;
2424 m_slot_to_piece
[slot2
] = piece1
;
2425 m_slot_to_piece
[m_current_slot
] = piece2
;
2427 m_piece_to_slot
[piece_index
] = slot1
;
2428 m_piece_to_slot
[m_current_slot
] = piece2
;
2430 if (piece1
== unassigned
)
2432 std::vector
<int>::iterator i
=
2433 std::find(m_free_slots
.begin(), m_free_slots
.end(), slot1
);
2434 TORRENT_ASSERT(i
!= m_free_slots
.end());
2435 if (m_storage_mode
== storage_mode_compact
)
2437 m_free_slots
.erase(i
);
2438 m_free_slots
.push_back(slot2
);
2445 m_piece_to_slot
[piece1
] = slot2
;
2446 ret
|= m_storage
->swap_slots3(m_current_slot
, slot1
, slot2
);
2450 ret
|= m_storage
->move_slot(m_current_slot
, slot1
);
2451 ret
|= m_storage
->move_slot(slot2
, m_current_slot
);
2456 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2457 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2462 TORRENT_ASSERT(m_piece_to_slot
[m_current_slot
] == has_no_slot
|| piece_index
!= m_current_slot
);
2463 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unallocated
);
2464 TORRENT_ASSERT(piece_index
== unassigned
|| m_piece_to_slot
[piece_index
] == has_no_slot
);
2466 // the slot was identified as piece 'piece_index'
2467 if (piece_index
!= unassigned
)
2468 m_piece_to_slot
[piece_index
] = m_current_slot
;
2469 else if (m_storage_mode
== storage_mode_compact
)
2470 m_free_slots
.push_back(m_current_slot
);
2472 m_slot_to_piece
[m_current_slot
] = piece_index
;
2474 TORRENT_ASSERT(m_slot_to_piece
[m_current_slot
] == unassigned
2475 || m_piece_to_slot
[m_slot_to_piece
[m_current_slot
]] == m_current_slot
);
2480 void piece_manager::switch_to_full_mode()
2482 TORRENT_ASSERT(m_storage_mode
== storage_mode_compact
);
2483 TORRENT_ASSERT(m_unallocated_slots
.empty());
2484 // we have allocated all slots, switch to
2485 // full allocation mode in order to free
2486 // some unnecessary memory.
2487 m_storage_mode
= storage_mode_sparse
;
2488 std::vector
<int>().swap(m_unallocated_slots
);
2489 std::vector
<int>().swap(m_free_slots
);
2490 std::vector
<int>().swap(m_piece_to_slot
);
2491 std::vector
<int>().swap(m_slot_to_piece
);
2494 int piece_manager::allocate_slot_for_piece(int piece_index
)
2496 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
2498 if (m_storage_mode
!= storage_mode_compact
) return piece_index
;
2502 TORRENT_ASSERT(piece_index
>= 0);
2503 TORRENT_ASSERT(piece_index
< (int)m_piece_to_slot
.size());
2504 TORRENT_ASSERT(m_piece_to_slot
.size() == m_slot_to_piece
.size());
2506 int slot_index
= m_piece_to_slot
[piece_index
];
2508 if (slot_index
!= has_no_slot
)
2510 TORRENT_ASSERT(slot_index
>= 0);
2511 TORRENT_ASSERT(slot_index
< (int)m_slot_to_piece
.size());
2515 if (m_free_slots
.empty())
2518 TORRENT_ASSERT(!m_free_slots
.empty());
2521 std::vector
<int>::iterator
iter(
2523 m_free_slots
.begin()
2524 , m_free_slots
.end()
2527 if (iter
== m_free_slots
.end())
2529 TORRENT_ASSERT(m_slot_to_piece
[piece_index
] != unassigned
);
2530 TORRENT_ASSERT(!m_free_slots
.empty());
2531 iter
= m_free_slots
.end() - 1;
2533 // special case to make sure we don't use the last slot
2534 // when we shouldn't, since it's smaller than ordinary slots
2535 if (*iter
== m_files
.num_pieces() - 1 && piece_index
!= *iter
)
2537 if (m_free_slots
.size() == 1)
2539 TORRENT_ASSERT(m_free_slots
.size() > 1);
2540 // assumes that all allocated slots
2541 // are put at the end of the free_slots vector
2542 iter
= m_free_slots
.end() - 1;
2547 m_free_slots
.erase(iter
);
2549 TORRENT_ASSERT(m_slot_to_piece
[slot_index
] == unassigned
);
2551 m_slot_to_piece
[slot_index
] = piece_index
;
2552 m_piece_to_slot
[piece_index
] = slot_index
;
2554 // there is another piece already assigned to
2555 // the slot we are interested in, swap positions
2556 if (slot_index
!= piece_index
2557 && m_slot_to_piece
[piece_index
] >= 0)
2560 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2561 std::stringstream s
;
2563 s
<< "there is another piece at our slot, swapping..";
2565 s
<< "\n piece_index: ";
2567 s
<< "\n slot_index: ";
2569 s
<< "\n piece at our slot: ";
2570 s
<< m_slot_to_piece
[piece_index
];
2573 print_to_log(s
.str());
2577 int piece_at_our_slot
= m_slot_to_piece
[piece_index
];
2578 TORRENT_ASSERT(m_piece_to_slot
[piece_at_our_slot
] == piece_index
);
2581 m_slot_to_piece
[piece_index
]
2582 , m_slot_to_piece
[slot_index
]);
2585 m_piece_to_slot
[piece_index
]
2586 , m_piece_to_slot
[piece_at_our_slot
]);
2588 m_storage
->move_slot(piece_index
, slot_index
);
2590 TORRENT_ASSERT(m_slot_to_piece
[piece_index
] == piece_index
);
2591 TORRENT_ASSERT(m_piece_to_slot
[piece_index
] == piece_index
);
2593 slot_index
= piece_index
;
2595 #if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
2599 TORRENT_ASSERT(slot_index
>= 0);
2600 TORRENT_ASSERT(slot_index
< (int)m_slot_to_piece
.size());
2602 if (m_free_slots
.empty() && m_unallocated_slots
.empty())
2603 switch_to_full_mode();
2608 bool piece_manager::allocate_slots(int num_slots
, bool abort_on_disk
)
2610 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
2611 TORRENT_ASSERT(num_slots
> 0);
2615 TORRENT_ASSERT(!m_unallocated_slots
.empty());
2616 TORRENT_ASSERT(m_storage_mode
== storage_mode_compact
);
2618 bool written
= false;
2620 for (int i
= 0; i
< num_slots
&& !m_unallocated_slots
.empty(); ++i
)
2624 int pos
= m_unallocated_slots
.front();
2625 TORRENT_ASSERT(m_slot_to_piece
[pos
] == unallocated
);
2626 TORRENT_ASSERT(m_piece_to_slot
[pos
] != pos
);
2628 int new_free_slot
= pos
;
2629 if (m_piece_to_slot
[pos
] != has_no_slot
)
2631 new_free_slot
= m_piece_to_slot
[pos
];
2632 m_storage
->move_slot(new_free_slot
, pos
);
2633 m_slot_to_piece
[pos
] = pos
;
2634 m_piece_to_slot
[pos
] = pos
;
2637 m_unallocated_slots
.erase(m_unallocated_slots
.begin());
2638 m_slot_to_piece
[new_free_slot
] = unassigned
;
2639 m_free_slots
.push_back(new_free_slot
);
2640 if (abort_on_disk
&& written
) break;
2643 TORRENT_ASSERT(m_free_slots
.size() > 0);
2647 int piece_manager::slot_for(int piece
) const
2649 if (m_storage_mode
!= storage_mode_compact
) return piece
;
2650 TORRENT_ASSERT(piece
< int(m_piece_to_slot
.size()));
2651 TORRENT_ASSERT(piece
>= 0);
2652 return m_piece_to_slot
[piece
];
2655 int piece_manager::piece_for(int slot
) const
2657 if (m_storage_mode
!= storage_mode_compact
) return slot
;
2658 TORRENT_ASSERT(slot
< int(m_slot_to_piece
.size()));
2659 TORRENT_ASSERT(slot
>= 0);
2660 return m_slot_to_piece
[slot
];
2664 void piece_manager::check_invariant() const
2666 boost::recursive_mutex::scoped_lock
lock(m_mutex
);
2668 TORRENT_ASSERT(m_current_slot
<= m_files
.num_pieces());
2670 if (m_unallocated_slots
.empty()
2671 && m_free_slots
.empty()
2672 && m_state
== state_finished
)
2674 TORRENT_ASSERT(m_storage_mode
!= storage_mode_compact
2675 || m_files
.num_pieces() == 0);
2678 if (m_storage_mode
!= storage_mode_compact
)
2680 TORRENT_ASSERT(m_unallocated_slots
.empty());
2681 TORRENT_ASSERT(m_free_slots
.empty());
2684 if (m_storage_mode
!= storage_mode_compact
2685 && m_state
!= state_expand_pieces
2686 && m_state
!= state_full_check
)
2688 TORRENT_ASSERT(m_piece_to_slot
.empty());
2689 TORRENT_ASSERT(m_slot_to_piece
.empty());
2693 if (m_piece_to_slot
.empty()) return;
2695 TORRENT_ASSERT((int)m_piece_to_slot
.size() == m_files
.num_pieces());
2696 TORRENT_ASSERT((int)m_slot_to_piece
.size() == m_files
.num_pieces());
2698 for (std::vector
<int>::const_iterator i
= m_free_slots
.begin();
2699 i
!= m_free_slots
.end(); ++i
)
2701 TORRENT_ASSERT(*i
< (int)m_slot_to_piece
.size());
2702 TORRENT_ASSERT(*i
>= 0);
2703 TORRENT_ASSERT(m_slot_to_piece
[*i
] == unassigned
);
2704 TORRENT_ASSERT(std::find(i
+1, m_free_slots
.end(), *i
)
2705 == m_free_slots
.end());
2708 for (std::vector
<int>::const_iterator i
= m_unallocated_slots
.begin();
2709 i
!= m_unallocated_slots
.end(); ++i
)
2711 TORRENT_ASSERT(*i
< (int)m_slot_to_piece
.size());
2712 TORRENT_ASSERT(*i
>= 0);
2713 TORRENT_ASSERT(m_slot_to_piece
[*i
] == unallocated
);
2714 TORRENT_ASSERT(std::find(i
+1, m_unallocated_slots
.end(), *i
)
2715 == m_unallocated_slots
.end());
2718 for (int i
= 0; i
< m_files
.num_pieces(); ++i
)
2720 // Check domain of piece_to_slot's elements
2721 if (m_piece_to_slot
[i
] != has_no_slot
)
2723 TORRENT_ASSERT(m_piece_to_slot
[i
] >= 0);
2724 TORRENT_ASSERT(m_piece_to_slot
[i
] < (int)m_slot_to_piece
.size());
2727 // Check domain of slot_to_piece's elements
2728 if (m_slot_to_piece
[i
] != unallocated
2729 && m_slot_to_piece
[i
] != unassigned
)
2731 TORRENT_ASSERT(m_slot_to_piece
[i
] >= 0);
2732 TORRENT_ASSERT(m_slot_to_piece
[i
] < (int)m_piece_to_slot
.size());
2735 // do more detailed checks on piece_to_slot
2736 if (m_piece_to_slot
[i
] >= 0)
2738 TORRENT_ASSERT(m_slot_to_piece
[m_piece_to_slot
[i
]] == i
);
2739 if (m_piece_to_slot
[i
] != i
)
2741 TORRENT_ASSERT(m_slot_to_piece
[i
] == unallocated
);
2746 TORRENT_ASSERT(m_piece_to_slot
[i
] == has_no_slot
);
2749 // do more detailed checks on slot_to_piece
2751 if (m_slot_to_piece
[i
] >= 0)
2753 TORRENT_ASSERT(m_slot_to_piece
[i
] < (int)m_piece_to_slot
.size());
2754 TORRENT_ASSERT(m_piece_to_slot
[m_slot_to_piece
[i
]] == i
);
2755 #ifdef TORRENT_STORAGE_DEBUG
2758 m_unallocated_slots
.begin()
2759 , m_unallocated_slots
.end()
2760 , i
) == m_unallocated_slots
.end()
2764 m_free_slots
.begin()
2765 , m_free_slots
.end()
2766 , i
) == m_free_slots
.end()
2770 else if (m_slot_to_piece
[i
] == unallocated
)
2772 #ifdef TORRENT_STORAGE_DEBUG
2773 TORRENT_ASSERT(m_unallocated_slots
.empty()
2775 m_unallocated_slots
.begin()
2776 , m_unallocated_slots
.end()
2777 , i
) != m_unallocated_slots
.end())
2781 else if (m_slot_to_piece
[i
] == unassigned
)
2783 #ifdef TORRENT_STORAGE_DEBUG
2786 m_free_slots
.begin()
2787 , m_free_slots
.end()
2788 , i
) != m_free_slots
.end()
2794 TORRENT_ASSERT(false && "m_slot_to_piece[i] is invalid");
2800 #ifdef TORRENT_STORAGE_DEBUG
2801 void piece_manager::debug_log() const
2803 std::stringstream s
;
2805 s
<< "index\tslot\tpiece\n";
2807 for (int i
= 0; i
< m_files
.num_pieces(); ++i
)
2809 s
<< i
<< "\t" << m_slot_to_piece
[i
] << "\t";
2810 s
<< m_piece_to_slot
[i
] << "\n";
2813 s
<< "---------------------------------\n";
2815 print_to_log(s
.str());
2819 } // namespace libtorrent