2 // This file is part of the aMule Project.
4 // Copyright (c) 2006-2011 Mikkel Schubert ( xaignar@amule.org / http:://www.amule.org )
5 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include <wx/app.h> // Needed for wxTheApp
30 #include "ThreadTasks.h" // Interface declarations
31 #include "PartFile.h" // Needed for CPartFile
32 #include "Logger.h" // Needed for Add(Debug)LogLine{C,N}
33 #include <common/Format.h> // Needed for CFormat
34 #include "amule.h" // Needed for theApp
35 #include "KnownFileList.h" // Needed for theApp->knownfiles
36 #include "Preferences.h" // Needed for thePrefs
37 #include "ScopedPtr.h" // Needed for CScopedPtr and CScopedArray
38 #include "PlatformSpecific.h" // Needed for CanFSHandleSpecialChars
44 //! This hash represents the value for an empty MD4 hashing
45 const byte g_emptyMD4Hash
[16] = {
46 0x31, 0xD6, 0xCF, 0xE0, 0xD1, 0x6A, 0xE9, 0x31,
47 0xB7, 0x3C, 0x59, 0xD7, 0xE0, 0xC0, 0x89, 0xC0 };
50 ////////////////////////////////////////////////////////////
53 CHashingTask::CHashingTask(const CPath
& path
, const CPath
& filename
, const CPartFile
* part
)
54 // GetPrintable is used to improve the readability of the log.
55 : CThreadTask(wxT("Hashing"), path
.JoinPaths(filename
).GetPrintable(), (part
? ETP_High
: ETP_Normal
)),
58 m_toHash((EHashes
)(EH_MD4
| EH_AICH
)),
61 // We can only create the AICH hashset if the file is a knownfile or
62 // if the partfile is complete, since the MD4 hashset is checked first,
63 // so that the AICH hashset only gets assigned if the MD4 hashset
64 // matches what we expected. Due to the rareity of post-completion
65 // corruptions, this gives us a nice speedup in most cases.
66 if (part
&& !part
->GetGapList().empty()) {
72 CHashingTask::CHashingTask(const CKnownFile
* toAICHHash
)
73 // GetPrintable is used to improve the readability of the log.
74 : CThreadTask(wxT("AICH Hashing"), toAICHHash
->GetFilePath().JoinPaths(toAICHHash
->GetFileName()).GetPrintable(), ETP_Low
),
75 m_path(toAICHHash
->GetFilePath()),
76 m_filename(toAICHHash
->GetFileName()),
83 void CHashingTask::Entry()
87 CPath fullPath
= m_path
.JoinPaths(m_filename
);
88 if (!file
.Open(fullPath
, CFile::read
)) {
89 AddDebugLogLineC(logHasher
,
90 CFormat(wxT("Warning, failed to open file, skipping: %s")) % fullPath
);
94 uint64 fileLength
= 0;
96 fileLength
= file
.GetLength();
97 } catch (const CIOFailureException
&) {
98 AddDebugLogLineC(logHasher
,
99 CFormat(wxT("Warning, failed to retrieve file-length, skipping: %s")) % fullPath
);
103 if (fileLength
> MAX_FILE_SIZE
) {
104 AddDebugLogLineC(logHasher
,
105 CFormat(wxT("Warning, file is larger than supported size, skipping: %s")) % fullPath
);
107 } else if (fileLength
== 0) {
109 // It makes no sense to try to hash empty partfiles ...
112 // Zero-size partfiles should be hashed, but not zero-sized shared-files.
113 AddDebugLogLineC(logHasher
,
114 CFormat(wxT("Warning, 0-size file, skipping: %s")) % fullPath
);
120 // For thread-safety, results are passed via a temporary file object.
121 CScopedPtr
<CKnownFile
> knownfile
;
122 knownfile
->m_filePath
= m_path
;
123 knownfile
->SetFileName(m_filename
);
124 knownfile
->SetFileSize(fileLength
);
125 knownfile
->m_lastDateChanged
= CPath::GetModificationTime(fullPath
);
126 knownfile
->m_AvailPartFrequency
.insert(
127 knownfile
->m_AvailPartFrequency
.begin(),
128 knownfile
->GetPartCount(), 0);
130 if ((m_toHash
& EH_MD4
) && (m_toHash
& EH_AICH
)) {
131 knownfile
->GetAICHHashset()->FreeHashSet();
132 AddDebugLogLineN( logHasher
, CFormat(
133 wxT("Starting to create MD4 and AICH hash for file: %s")) %
135 } else if ((m_toHash
& EH_MD4
)) {
136 AddDebugLogLineN( logHasher
, CFormat(
137 wxT("Starting to create MD4 hash for file: %s")) % m_filename
);
138 } else if ((m_toHash
& EH_AICH
)) {
139 knownfile
->GetAICHHashset()->FreeHashSet();
140 AddDebugLogLineN( logHasher
, CFormat(
141 wxT("Starting to create AICH hash for file: %s")) % m_filename
);
143 wxCHECK_RET(0, (CFormat(wxT("No hashes requested for file, skipping: %s"))
144 % m_filename
).GetString());
148 // This loops creates the part-hashes, loop-de-loop.
150 for (uint16 part
= 0; part
< knownfile
->GetPartCount() && !TestDestroy(); part
++) {
151 if (CreateNextPartHash(file
, part
, knownfile
.get(), m_toHash
) == false) {
152 AddDebugLogLineC(logHasher
,
153 CFormat(wxT("Error while hashing file, skipping: %s"))
159 } catch (const CSafeIOException
& e
) {
160 AddDebugLogLineC(logHasher
, wxT("IO exception while hashing file: ") + e
.what());
164 if ((m_toHash
& EH_MD4
) && !TestDestroy()) {
165 // If the file is < PARTSIZE, then the filehash is that one hash,
166 // otherwise, the filehash is the hash of the parthashes
167 if ( knownfile
->m_hashlist
.size() == 1 ) {
168 knownfile
->m_abyFileHash
= knownfile
->m_hashlist
[0];
169 knownfile
->m_hashlist
.clear();
170 } else if ( knownfile
->m_hashlist
.size() ) {
172 knownfile
->CreateHashFromHashlist(knownfile
->m_hashlist
, &hash
);
173 knownfile
->m_abyFileHash
= hash
;
175 // This should not happen!
180 // Did we create a AICH hashset?
181 if ((m_toHash
& EH_AICH
) && !TestDestroy()) {
182 CAICHHashSet
* AICHHashSet
= knownfile
->GetAICHHashset();
184 AICHHashSet
->ReCalculateHash(false);
185 if (AICHHashSet
->VerifyHashTree(true) ) {
186 AICHHashSet
->SetStatus(AICH_HASHSETCOMPLETE
);
187 if (!AICHHashSet
->SaveHashSet()) {
188 AddDebugLogLineC( logHasher
,
189 CFormat(wxT("Warning, failed to save AICH hashset for file: %s"))
195 if ((m_toHash
== EH_AICH
) && !TestDestroy()) {
196 CHashingEvent
evt(MULE_EVT_AICH_HASHING
, knownfile
.release(), m_owner
);
198 wxPostEvent(wxTheApp
, evt
);
199 } else if (!TestDestroy()) {
200 CHashingEvent
evt(MULE_EVT_HASHING
, knownfile
.release(), m_owner
);
202 wxPostEvent(wxTheApp
, evt
);
207 bool CHashingTask::CreateNextPartHash(CFileAutoClose
& file
, uint16 part
, CKnownFile
* owner
, EHashes toHash
)
209 wxCHECK_MSG(!file
.Eof(), false, wxT("Unexpected EOF in CreateNextPartHash"));
211 const uint64 offset
= part
* PARTSIZE
;
212 // We'll read at most PARTSIZE bytes per cycle
213 const uint64 partLength
= owner
->GetPartSize(part
);
216 CMD4Hash
* md4Hash
= ((toHash
& EH_MD4
) ? &hash
: NULL
);
217 CAICHHashTree
* aichHash
= NULL
;
219 // Setup for AICH hashing
220 if (toHash
& EH_AICH
) {
221 aichHash
= owner
->GetAICHHashset()->m_pHashTree
.FindHash(offset
, partLength
);
224 owner
->CreateHashFromFile(file
, offset
, partLength
, md4Hash
, aichHash
);
226 if (toHash
& EH_MD4
) {
227 // Store the md4 hash
228 owner
->m_hashlist
.push_back(hash
);
230 // This is because of the ed2k implementation for parts. A 2 * PARTSIZE
231 // file i.e. will have 3 parts (see CKnownFile::SetFileSize for comments).
232 // So we have to create the hash for the 0-size data, which will be the default
233 // md4 hash for null data: 31D6CFE0D16AE931B73C59D7E0C089C0
234 if ((partLength
== PARTSIZE
) && file
.Eof()) {
235 owner
->m_hashlist
.push_back(CMD4Hash(g_emptyMD4Hash
));
243 void CHashingTask::OnLastTask()
245 if (GetType() == wxT("Hashing")) {
246 // To prevent rehashing in case of crashes, we
247 // explicity save the list of hashed files here.
248 theApp
->knownfiles
->Save();
250 // Make sure the AICH-hashes are up to date.
251 CThreadScheduler::AddTask(new CAICHSyncTask());
256 ////////////////////////////////////////////////////////////
259 CAICHSyncTask::CAICHSyncTask()
260 : CThreadTask(wxT("AICH Syncronizing"), wxEmptyString
, ETP_Low
)
265 void CAICHSyncTask::Entry()
267 ConvertToKnown2ToKnown264();
269 AddDebugLogLineN( logAICHThread
, wxT("Syncronization thread started.") );
271 // We collect all masterhashs which we find in the known2.met and store them in a list
272 std::list
<CAICHHash
> hashlist
;
273 const CPath fullpath
= CPath(theApp
->ConfigDir
+ KNOWN2_MET_FILENAME
);
276 if (!fullpath
.FileExists()) {
277 // File does not exist. Try to create it to see if it can be created at all (and don't start hashing otherwise).
278 if (!file
.Open(fullpath
, CFile::write
)) {
279 AddDebugLogLineC( logAICHThread
, wxT("Error, failed to open 'known2_64.met' file!") );
283 file
.WriteUInt8(KNOWN2_MET_VERSION
);
284 } catch (const CIOFailureException
& e
) {
285 AddDebugLogLineC(logAICHThread
, wxT("IO failure while creating hashlist (Aborting): ") + e
.what());
289 if (!file
.Open(fullpath
, CFile::read
)) {
290 AddDebugLogLineC( logAICHThread
, wxT("Error, failed to open 'known2_64.met' file!") );
294 uint32 nLastVerifiedPos
= 0;
296 if (file
.ReadUInt8() != KNOWN2_MET_VERSION
) {
297 throw CEOFException(wxT("Invalid met-file header found, removing file."));
300 uint64 nExistingSize
= file
.GetLength();
301 while (file
.GetPosition() < nExistingSize
) {
302 // Read the next hash
303 hashlist
.push_back(CAICHHash(&file
));
305 uint32 nHashCount
= file
.ReadUInt32();
306 if (file
.GetPosition() + nHashCount
* CAICHHash::GetHashSize() > nExistingSize
){
307 throw CEOFException(wxT("Hashlist ends past end of file."));
310 // skip the rest of this hashset
311 nLastVerifiedPos
= file
.Seek(nHashCount
* HASHSIZE
, wxFromCurrent
);
313 } catch (const CEOFException
&) {
314 AddDebugLogLineC(logAICHThread
, wxT("Hashlist corrupted, truncating file."));
316 file
.Reopen(CFile::read_write
);
317 file
.SetLength(nLastVerifiedPos
);
318 } catch (const CIOFailureException
& e
) {
319 AddDebugLogLineC(logAICHThread
, wxT("IO failure while reading hashlist (Aborting): ") + e
.what());
324 AddDebugLogLineN( logAICHThread
, wxT("Masterhashes of known files have been loaded.") );
327 // Now we check that all files which are in the sharedfilelist have a
328 // corresponding hash in our list. Those how don't are queued for hashing.
329 theApp
->sharedfiles
->CheckAICHHashes(hashlist
);
333 bool CAICHSyncTask::ConvertToKnown2ToKnown264()
335 // converting known2.met to known2_64.met to support large files
336 // changing hashcount from uint16 to uint32
338 const CPath oldfullpath
= CPath(theApp
->ConfigDir
+ OLD_KNOWN2_MET_FILENAME
);
339 const CPath newfullpath
= CPath(theApp
->ConfigDir
+ KNOWN2_MET_FILENAME
);
341 if (newfullpath
.FileExists() || !oldfullpath
.FileExists()) {
342 // In this case, there is nothing that we need to do.
349 if (!oldfile
.Open(oldfullpath
, CFile::read
)) {
350 AddDebugLogLineC(logAICHThread
, wxT("Failed to open 'known2.met' file."));
352 // else -> known2.met also doesn't exists, so nothing to convert
357 if (!newfile
.Open(newfullpath
, CFile::write_excl
)) {
358 AddDebugLogLineC(logAICHThread
, wxT("Failed to create 'known2_64.met' file."));
363 AddLogLineN(CFormat(_("Converting old AICH hashsets in '%s' to 64b in '%s'."))
364 % OLD_KNOWN2_MET_FILENAME
% KNOWN2_MET_FILENAME
);
367 newfile
.WriteUInt8(KNOWN2_MET_VERSION
);
369 while (newfile
.GetPosition() < oldfile
.GetLength()) {
370 CAICHHash
aichHash(&oldfile
);
371 uint32 nHashCount
= oldfile
.ReadUInt16();
373 CScopedArray
<byte
> buffer(nHashCount
* CAICHHash::GetHashSize());
375 oldfile
.Read(buffer
.get(), nHashCount
* CAICHHash::GetHashSize());
376 newfile
.Write(aichHash
.GetRawHash(), CAICHHash::GetHashSize());
377 newfile
.WriteUInt32(nHashCount
);
378 newfile
.Write(buffer
.get(), nHashCount
* CAICHHash::GetHashSize());
381 } catch (const CEOFException
& e
) {
382 AddDebugLogLineC(logAICHThread
, wxT("Error reading old 'known2.met' file.") + e
.what());
384 } catch (const CIOFailureException
& e
) {
385 AddDebugLogLineC(logAICHThread
, wxT("IO error while converting 'known2.met' file: ") + e
.what());
389 // FIXME LARGE FILES (uncomment)
390 //DeleteFile(oldfullpath);
397 ////////////////////////////////////////////////////////////
401 CCompletionTask::CCompletionTask(const CPartFile
* file
)
402 // GetPrintable is used to improve the readability of the log.
403 : CThreadTask(wxT("Completing"), file
->GetFullName().GetPrintable(), ETP_High
),
404 m_filename(file
->GetFileName()),
405 m_metPath(file
->GetFullName()),
406 m_category(file
->GetCategory()),
410 wxASSERT(m_filename
.IsOk());
411 wxASSERT(m_metPath
.IsOk());
416 void CCompletionTask::Entry()
422 // Prevent the preference values from changing underneeth us.
423 wxMutexGuiLocker guiLock
;
425 //#warning Thread-safety needed
428 targetPath
= theApp
->glob_prefs
->GetCategory(m_category
)->path
;
429 if (!targetPath
.DirExists()) {
430 targetPath
= thePrefs::GetIncomingDir();
434 CPath dstName
= m_filename
.Cleanup(true, !PlatformSpecific::CanFSHandleSpecialChars(targetPath
));
436 // Avoid empty filenames ...
437 if (!dstName
.IsOk()) {
438 dstName
= CPath(wxT("Unknown"));
441 if (m_filename
!= dstName
) {
442 AddLogLineC(CFormat(_("WARNING: The filename '%s' is invalid and has been renamed to '%s'.")) % m_filename
% dstName
);
445 // Avoid saving to an already existing filename
446 CPath newName
= targetPath
.JoinPaths(dstName
);
447 for (unsigned count
= 0; newName
.FileExists(); ++count
) {
448 wxString postfix
= CFormat(wxT("(%u)")) % count
;
450 newName
= targetPath
.JoinPaths(dstName
.AddPostfix(postfix
));
453 if (newName
!= targetPath
.JoinPaths(dstName
)) {
454 AddLogLineC(CFormat(_("WARNING: The file '%s' already exists, new file renamed to '%s'.")) % dstName
% newName
.GetFullName());
457 // Move will handle dirs on the same partition, otherwise copy is needed.
458 CPath partfilename
= m_metPath
.RemoveExt();
459 if (!CPath::RenameFile(partfilename
, newName
)) {
460 if (!CPath::CloneFile(partfilename
, newName
, true)) {
465 if (!CPath::RemoveFile(partfilename
)) {
466 AddDebugLogLineC(logPartFile
, CFormat(wxT("WARNING: Could not remove original '%s' after creating backup")) % partfilename
);
470 // Removes the various other data-files
471 const wxChar
* otherMetExt
[] = { wxT(""), PARTMET_BAK_EXT
, wxT(".seeds"), NULL
};
472 for (size_t i
= 0; otherMetExt
[i
]; ++i
) {
473 CPath toRemove
= m_metPath
.AppendExt(otherMetExt
[i
]);
475 if (toRemove
.FileExists()) {
476 if (!CPath::RemoveFile(toRemove
)) {
477 AddDebugLogLineC(logPartFile
, CFormat(wxT("WARNING: Failed to delete %s")) % toRemove
);
486 void CCompletionTask::OnExit()
488 // Notify the app that the completion has finished for this file.
489 CCompletionEvent
evt(m_error
, m_owner
, m_newName
);
491 wxPostEvent(wxTheApp
, evt
);
496 ////////////////////////////////////////////////////////////
499 #ifdef HAVE_FALLOCATE
506 # include <linux/falloc.h>
507 #elif defined HAVE_SYS_FALLOCATE
508 # include <sys/syscall.h>
509 # include <sys/types.h>
511 #elif defined HAVE_POSIX_FALLOCATE
512 # define _XOPEN_SOURCE 600
521 CAllocateFileTask::CAllocateFileTask(CPartFile
*file
, bool pause
)
522 // GetPrintable is used to improve the readability of the log.
523 : CThreadTask(wxT("Allocating"), file
->GetFullName().RemoveExt().GetPrintable(), ETP_High
),
524 m_file(file
), m_pause(pause
), m_result(ENOSYS
)
526 wxASSERT(file
!= NULL
);
529 void CAllocateFileTask::Entry()
531 if (m_file
->GetFileSize() == 0) {
536 uint64_t minFree
= thePrefs::IsCheckDiskspaceEnabled() ? thePrefs::GetMinFreeDiskSpace() : 0;
537 int64_t freeSpace
= CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
539 // Don't even try to allocate, if there's no space to complete the operation.
540 if (freeSpace
!= wxInvalidOffset
) {
541 if ((uint64_t)freeSpace
< m_file
->GetFileSize() + minFree
) {
548 file
.Open(m_file
->GetFullName().RemoveExt(), CFile::read_write
);
552 // File is already created as non-sparse, so we only need to set the length.
553 // This will fail to allocate the file e.g. under wine on linux/ext3,
554 // but works with NTFS and FAT32.
555 file
.Seek(m_file
->GetFileSize() - 1, wxFromStart
);
559 } catch (const CSafeIOException
&) {
563 // Use kernel level routines if possible
564 # ifdef HAVE_FALLOCATE
565 m_result
= fallocate(file
.fd(), 0, 0, m_file
->GetFileSize());
566 # elif defined HAVE_SYS_FALLOCATE
567 m_result
= syscall(SYS_fallocate
, file
.fd(), 0, (loff_t
)0, (loff_t
)m_file
->GetFileSize());
568 if (m_result
== -1) {
571 # elif defined HAVE_POSIX_FALLOCATE
572 // otherwise use glibc implementation, if available
573 m_result
= posix_fallocate(file
.fd(), 0, m_file
->GetFileSize());
576 if (m_result
!= 0 && m_result
!= ENOSPC
) {
577 // If everything else fails, use slow-and-dirty method of allocating the file: write the whole file with zeroes.
578 # define BLOCK_SIZE 1048576 /* Write 1 MB blocks */
579 void *zero
= calloc(1, BLOCK_SIZE
);
582 uint64_t size
= m_file
->GetFileSize();
583 for (; size
>= BLOCK_SIZE
; size
-= BLOCK_SIZE
) {
584 file
.Write(zero
, BLOCK_SIZE
);
587 file
.Write(zero
, size
);
591 } catch (const CSafeIOException
&) {
601 if (file
.IsOpened()) {
606 void CAllocateFileTask::OnExit()
608 // Notify the app that the preallocation has finished for this file.
609 CAllocFinishedEvent
evt(m_file
, m_pause
, m_result
);
611 wxPostEvent(wxTheApp
, evt
);
616 ////////////////////////////////////////////////////////////
619 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_HASHING
)
620 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_AICH_HASHING
)
622 CHashingEvent::CHashingEvent(wxEventType type
, CKnownFile
* result
, const CKnownFile
* owner
)
630 wxEvent
* CHashingEvent::Clone() const
632 return new CHashingEvent(GetEventType(), m_result
, m_owner
);
636 const CKnownFile
* CHashingEvent::GetOwner() const
642 CKnownFile
* CHashingEvent::GetResult() const
650 ////////////////////////////////////////////////////////////
653 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_FILE_COMPLETED
)
656 CCompletionEvent::CCompletionEvent(bool errorOccured
, const CPartFile
* owner
, const CPath
& fullPath
)
657 : wxEvent(-1, MULE_EVT_FILE_COMPLETED
),
658 m_fullPath(fullPath
),
660 m_error(errorOccured
)
665 wxEvent
* CCompletionEvent::Clone() const
667 return new CCompletionEvent(m_error
, m_owner
, m_fullPath
);
671 bool CCompletionEvent::ErrorOccured() const
677 const CPartFile
* CCompletionEvent::GetOwner() const
683 const CPath
& CCompletionEvent::GetFullPath() const
689 ////////////////////////////////////////////////////////////
690 // CAllocFinishedEvent
692 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_ALLOC_FINISHED
)
694 wxEvent
*CAllocFinishedEvent::Clone() const
696 return new CAllocFinishedEvent(m_file
, m_pause
, m_result
);
699 // File_checked_for_headers