1 //
2 // This file is part of the aMule Project.
3 //
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 )
7 //
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.
11 //
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.
16 //
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.
21 //
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
25 //
26 
27 
28 #include <wx/app.h>			// Needed for wxTheApp
29 
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
39 
40 #ifdef HAVE_CONFIG_H
41 #	include "config.h"
42 #endif
43 
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 };
48 
49 
50 ////////////////////////////////////////////////////////////
51 // CHashingTask
52 
CHashingTask(const CPath & path,const CPath & filename,const CPartFile * part)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)),
56 	  m_path(path),
57 	  m_filename(filename),
58 	  m_toHash((EHashes)(EH_MD4 | EH_AICH)),
59 	  m_owner(part)
60 {
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()) {
67 		m_toHash = EH_MD4;
68 	}
69 }
70 
71 
CHashingTask(const CKnownFile * toAICHHash)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()),
77 	  m_toHash(EH_AICH),
78 	  m_owner(toAICHHash)
79 {
80 }
81 
82 
Entry()83 void CHashingTask::Entry()
84 {
85 	CFileAutoClose file;
86 
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);
91 		return;
92 	}
93 
94 	uint64 fileLength = 0;
95 	try {
96 		fileLength = file.GetLength();
97 	} catch (const CIOFailureException&) {
98 		AddDebugLogLineC(logHasher,
99 			CFormat(wxT("Warning, failed to retrieve file-length, skipping: %s")) % fullPath);
100 		return;
101 	}
102 
103 	if (fileLength > MAX_FILE_SIZE) {
104 		AddDebugLogLineC(logHasher,
105 			CFormat(wxT("Warning, file is larger than supported size, skipping: %s")) % fullPath);
106 		return;
107 	} else if (fileLength == 0) {
108 		if (m_owner) {
109 			// It makes no sense to try to hash empty partfiles ...
110 			wxFAIL;
111 		} else {
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);
115 		}
116 
117 		return;
118 	}
119 
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);
129 
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")) %
134 			m_filename );
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 );
142 	} else {
143 		wxCHECK_RET(0, (CFormat(wxT("No hashes requested for file, skipping: %s"))
144 			% m_filename).GetString());
145 	}
146 
147 	// This loops creates the part-hashes, loop-de-loop.
148 	try {
149 		for (uint16 part = 0; part < knownfile->GetPartCount() && !TestDestroy(); part++) {
150 			SetHashingProgress(part + 1);
151 			if (CreateNextPartHash(file, part, knownfile.get(), m_toHash) == false) {
152 				AddDebugLogLineC(logHasher,
153 					CFormat(wxT("Error while hashing file, skipping: %s"))
154 						% m_filename);
155 
156 				SetHashingProgress(0);
157 				return;
158 			}
159 		}
160 	} catch (const CSafeIOException& e) {
161 		AddDebugLogLineC(logHasher, wxT("IO exception while hashing file: ") + e.what());
162 		SetHashingProgress(0);
163 		return;
164 	}
165 	SetHashingProgress(0);
166 
167 	if ((m_toHash & EH_MD4) && !TestDestroy()) {
168 		// If the file is < PARTSIZE, then the filehash is that one hash,
169 		// otherwise, the filehash is the hash of the parthashes
170 		if ( knownfile->m_hashlist.size() == 1 ) {
171 			knownfile->m_abyFileHash = knownfile->m_hashlist[0];
172 			knownfile->m_hashlist.clear();
173 		} else if ( knownfile->m_hashlist.size() ) {
174 			CMD4Hash hash;
175 			knownfile->CreateHashFromHashlist(knownfile->m_hashlist, &hash);
176 			knownfile->m_abyFileHash = hash;
177 		} else {
178 			// This should not happen!
179 			wxFAIL;
180 		}
181 	}
182 
183 	// Did we create a AICH hashset?
184 	if ((m_toHash & EH_AICH) && !TestDestroy()) {
185 		CAICHHashSet* AICHHashSet = knownfile->GetAICHHashset();
186 
187 		AICHHashSet->ReCalculateHash(false);
188 		if (AICHHashSet->VerifyHashTree(true) ) {
189 			AICHHashSet->SetStatus(AICH_HASHSETCOMPLETE);
190 			if (!AICHHashSet->SaveHashSet()) {
191 				AddDebugLogLineC( logHasher,
192 					CFormat(wxT("Warning, failed to save AICH hashset for file: %s"))
193 						% m_filename );
194 			}
195 			// delete hashset now to free memory
196 			AICHHashSet->FreeHashSet();
197 		}
198 	}
199 
200 	if ((m_toHash == EH_AICH) && !TestDestroy()) {
201 		CHashingEvent evt(MULE_EVT_AICH_HASHING, knownfile.release(), m_owner);
202 
203 		wxPostEvent(wxTheApp, evt);
204 	} else if (!TestDestroy()) {
205 		CHashingEvent evt(MULE_EVT_HASHING, knownfile.release(), m_owner);
206 
207 		wxPostEvent(wxTheApp, evt);
208 	}
209 }
210 
211 
SetHashingProgress(uint16 part)212 void CHashingTask::SetHashingProgress(uint16 part)
213 {
214 	if (m_owner) {
215 		m_owner->SetHashingProgress(part);
216 	}
217 }
218 
219 
CreateNextPartHash(CFileAutoClose & file,uint16 part,CKnownFile * owner,EHashes toHash)220 bool CHashingTask::CreateNextPartHash(CFileAutoClose& file, uint16 part, CKnownFile* owner, EHashes toHash)
221 {
222 	wxCHECK_MSG(!file.Eof(), false, wxT("Unexpected EOF in CreateNextPartHash"));
223 
224 	const uint64 offset = part * PARTSIZE;
225 	// We'll read at most PARTSIZE bytes per cycle
226 	const uint64 partLength = owner->GetPartSize(part);
227 
228 	CMD4Hash hash;
229 	CMD4Hash* md4Hash = ((toHash & EH_MD4) ? &hash : NULL);
230 	CAICHHashTree* aichHash = NULL;
231 
232 	// Setup for AICH hashing
233 	if (toHash & EH_AICH) {
234 		aichHash = owner->GetAICHHashset()->m_pHashTree.FindHash(offset, partLength);
235 	}
236 
237 	owner->CreateHashFromFile(file, offset, partLength, md4Hash, aichHash);
238 
239 	if (toHash & EH_MD4) {
240 		// Store the md4 hash
241 		owner->m_hashlist.push_back(hash);
242 
243 		// This is because of the ed2k implementation for parts. A 2 * PARTSIZE
244 		// file i.e. will have 3 parts (see CKnownFile::SetFileSize for comments).
245 		// So we have to create the hash for the 0-size data, which will be the default
246 		// md4 hash for null data: 31D6CFE0D16AE931B73C59D7E0C089C0
247 		if ((partLength == PARTSIZE) && file.Eof()) {
248 			owner->m_hashlist.push_back(CMD4Hash(g_emptyMD4Hash));
249 		}
250 	}
251 
252 	return true;
253 }
254 
255 
OnLastTask()256 void CHashingTask::OnLastTask()
257 {
258 	if (GetType() == wxT("Hashing")) {
259 		// To prevent rehashing in case of crashes, we
260 		// explicity save the list of hashed files here.
261 		theApp->knownfiles->Save();
262 
263 		// Make sure the AICH-hashes are up to date.
264 		CThreadScheduler::AddTask(new CAICHSyncTask());
265 	}
266 }
267 
268 
269 ////////////////////////////////////////////////////////////
270 // CAICHSyncTask
271 
CAICHSyncTask()272 CAICHSyncTask::CAICHSyncTask()
273 	: CThreadTask(wxT("AICH Syncronizing"), wxEmptyString, ETP_Low)
274 {
275 }
276 
277 
Entry()278 void CAICHSyncTask::Entry()
279 {
280 	ConvertToKnown2ToKnown264();
281 
282 	AddDebugLogLineN( logAICHThread, wxT("Syncronization thread started.") );
283 
284 	// We collect all masterhashs which we find in the known2.met and store them in a list
285 	std::list<CAICHHash> hashlist;
286 	const CPath fullpath = CPath(thePrefs::GetConfigDir() + KNOWN2_MET_FILENAME);
287 
288 	CFile file;
289 	if (!fullpath.FileExists()) {
290 		// File does not exist. Try to create it to see if it can be created at all (and don't start hashing otherwise).
291 		if (!file.Open(fullpath, CFile::write)) {
292 			AddDebugLogLineC( logAICHThread, wxT("Error, failed to open 'known2_64.met' file!") );
293 			return;
294 		}
295 		try {
296 			file.WriteUInt8(KNOWN2_MET_VERSION);
297 		} catch (const CIOFailureException& e) {
298 			AddDebugLogLineC(logAICHThread, wxT("IO failure while creating hashlist (Aborting): ") + e.what());
299 			return;
300 		}
301 	} else {
302 		if (!file.Open(fullpath, CFile::read)) {
303 			AddDebugLogLineC( logAICHThread, wxT("Error, failed to open 'known2_64.met' file!") );
304 			return;
305 		}
306 
307 		uint32 nLastVerifiedPos = 0;
308 		try {
309 			if (file.ReadUInt8() != KNOWN2_MET_VERSION) {
310 				throw CEOFException(wxT("Invalid met-file header found, removing file."));
311 			}
312 
313 			uint64 nExistingSize = file.GetLength();
314 			while (file.GetPosition() < nExistingSize) {
315 				// Read the next hash
316 				hashlist.push_back(CAICHHash(&file));
317 
318 				uint32 nHashCount = file.ReadUInt32();
319 				if (file.GetPosition() + nHashCount * CAICHHash::GetHashSize() > nExistingSize){
320 					throw CEOFException(wxT("Hashlist ends past end of file."));
321 				}
322 
323 				// skip the rest of this hashset
324 				nLastVerifiedPos = file.Seek(nHashCount * HASHSIZE, wxFromCurrent);
325 			}
326 		} catch (const CEOFException&) {
327 			AddDebugLogLineC(logAICHThread, wxT("Hashlist corrupted, truncating file."));
328 			file.Close();
329 			file.Reopen(CFile::read_write);
330 			file.SetLength(nLastVerifiedPos);
331 		} catch (const CIOFailureException& e) {
332 			AddDebugLogLineC(logAICHThread, wxT("IO failure while reading hashlist (Aborting): ") + e.what());
333 
334 			return;
335 		}
336 
337 		AddDebugLogLineN( logAICHThread, wxT("Masterhashes of known files have been loaded.") );
338 	}
339 
340 	// Now we check that all files which are in the sharedfilelist have a
341 	// corresponding hash in our list. Those how don't are queued for hashing.
342 	theApp->sharedfiles->CheckAICHHashes(hashlist);
343 }
344 
345 
ConvertToKnown2ToKnown264()346 bool CAICHSyncTask::ConvertToKnown2ToKnown264()
347 {
348 	// converting known2.met to known2_64.met to support large files
349 	// changing hashcount from uint16 to uint32
350 
351 	const CPath oldfullpath = CPath(thePrefs::GetConfigDir() + OLD_KNOWN2_MET_FILENAME);
352 	const CPath newfullpath = CPath(thePrefs::GetConfigDir() + KNOWN2_MET_FILENAME);
353 
354 	if (newfullpath.FileExists() || !oldfullpath.FileExists()) {
355 		// In this case, there is nothing that we need to do.
356 		return false;
357 	}
358 
359 	CFile oldfile;
360 	CFile newfile;
361 
362 	if (!oldfile.Open(oldfullpath, CFile::read)) {
363 		AddDebugLogLineC(logAICHThread, wxT("Failed to open 'known2.met' file."));
364 
365 		// else -> known2.met also doesn't exists, so nothing to convert
366 		return false;
367 	}
368 
369 
370 	if (!newfile.Open(newfullpath, CFile::write_excl)) {
371 		AddDebugLogLineC(logAICHThread, wxT("Failed to create 'known2_64.met' file."));
372 
373 		return false;
374 	}
375 
376 	AddLogLineN(CFormat(_("Converting old AICH hashsets in '%s' to 64b in '%s'."))
377 			% OLD_KNOWN2_MET_FILENAME % KNOWN2_MET_FILENAME);
378 
379 	try {
380 		newfile.WriteUInt8(KNOWN2_MET_VERSION);
381 
382 		while (newfile.GetPosition() < oldfile.GetLength()) {
383 			CAICHHash aichHash(&oldfile);
384 			uint32 nHashCount = oldfile.ReadUInt16();
385 
386 			CScopedArray<byte> buffer(nHashCount * CAICHHash::GetHashSize());
387 
388 			oldfile.Read(buffer.get(), nHashCount * CAICHHash::GetHashSize());
389 			newfile.Write(aichHash.GetRawHash(), CAICHHash::GetHashSize());
390 			newfile.WriteUInt32(nHashCount);
391 			newfile.Write(buffer.get(), nHashCount * CAICHHash::GetHashSize());
392 		}
393 		newfile.Flush();
394 	} catch (const CEOFException& e) {
395 		AddDebugLogLineC(logAICHThread, wxT("Error reading old 'known2.met' file.") + e.what());
396 		return false;
397 	} catch (const CIOFailureException& e) {
398 		AddDebugLogLineC(logAICHThread, wxT("IO error while converting 'known2.met' file: ") + e.what());
399 		return false;
400 	}
401 
402 	// FIXME LARGE FILES (uncomment)
403 	//DeleteFile(oldfullpath);
404 
405 	return true;
406 }
407 
408 
409 
410 ////////////////////////////////////////////////////////////
411 // CCompletionTask
412 
413 
CCompletionTask(const CPartFile * file)414 CCompletionTask::CCompletionTask(const CPartFile* file)
415 	// GetPrintable is used to improve the readability of the log.
416 	: CThreadTask(wxT("Completing"), file->GetFullName().GetPrintable(), ETP_High),
417 	  m_filename(file->GetFileName()),
418 	  m_metPath(file->GetFullName()),
419 	  m_category(file->GetCategory()),
420 	  m_owner(file),
421 	  m_error(false)
422 {
423 	wxASSERT(m_filename.IsOk());
424 	wxASSERT(m_metPath.IsOk());
425 	wxASSERT(m_owner);
426 }
427 
428 
Entry()429 void CCompletionTask::Entry()
430 {
431 	CPath targetPath;
432 
433 	{
434 #ifndef AMULE_DAEMON
435 		// Prevent the preference values from changing underneeth us.
436 		wxMutexGuiLocker guiLock;
437 #else
438 		//#warning Thread-safety needed
439 #endif
440 
441 		targetPath = theApp->glob_prefs->GetCategory(m_category)->path;
442 		if (!targetPath.DirExists()) {
443 			targetPath = thePrefs::GetIncomingDir();
444 		}
445 	}
446 
447 	CPath dstName = m_filename.Cleanup(true, !PlatformSpecific::CanFSHandleSpecialChars(targetPath));
448 
449 	// Avoid empty filenames ...
450 	if (!dstName.IsOk()) {
451 		dstName = CPath(wxT("Unknown"));
452 	}
453 
454 	if (m_filename != dstName) {
455 		AddLogLineC(CFormat(_("WARNING: The filename '%s' is invalid and has been renamed to '%s'.")) % m_filename % dstName);
456 	}
457 
458 	// Avoid saving to an already existing filename
459 	CPath newName = targetPath.JoinPaths(dstName);
460 	for (unsigned count = 0; newName.FileExists(); ++count) {
461 		wxString postfix = CFormat(wxT("(%u)")) % count;
462 
463 		newName = targetPath.JoinPaths(dstName.AddPostfix(postfix));
464 	}
465 
466 	if (newName != targetPath.JoinPaths(dstName)) {
467 		AddLogLineC(CFormat(_("WARNING: The file '%s' already exists, new file renamed to '%s'.")) % dstName % newName.GetFullName());
468 	}
469 
470 	// Move will handle dirs on the same partition, otherwise copy is needed.
471 	CPath partfilename = m_metPath.RemoveExt();
472 	if (!CPath::RenameFile(partfilename, newName)) {
473 		if (!CPath::CloneFile(partfilename, newName, true)) {
474 			m_error = true;
475 			return;
476 		}
477 
478 		if (!CPath::RemoveFile(partfilename)) {
479 			AddDebugLogLineC(logPartFile, CFormat(wxT("WARNING: Could not remove original '%s' after creating backup")) % partfilename);
480 		}
481 	}
482 
483 	// Removes the various other data-files
484 	const wxChar* otherMetExt[] = { wxT(""), PARTMET_BAK_EXT, wxT(".seeds"), NULL };
485 	for (size_t i = 0; otherMetExt[i]; ++i) {
486 		CPath toRemove = m_metPath.AppendExt(otherMetExt[i]);
487 
488 		if (toRemove.FileExists()) {
489 			if (!CPath::RemoveFile(toRemove)) {
490 				AddDebugLogLineC(logPartFile, CFormat(wxT("WARNING: Failed to delete %s")) % toRemove);
491 			}
492 		}
493 	}
494 
495 	m_newName = newName;
496 }
497 
498 
OnExit()499 void CCompletionTask::OnExit()
500 {
501 	// Notify the app that the completion has finished for this file.
502 	CCompletionEvent evt(m_error, m_owner, m_newName);
503 
504 	wxPostEvent(wxTheApp, evt);
505 }
506 
507 
508 
509 ////////////////////////////////////////////////////////////
510 // CAllocateFileTask
511 
512 #ifdef HAVE_FALLOCATE
513 #	ifndef _GNU_SOURCE
514 #		define _GNU_SOURCE
515 #	endif
516 #	ifdef HAVE_FCNTL_H
517 #		include <fcntl.h>
518 #	endif
519 #	include <linux/falloc.h>
520 #elif defined HAVE_SYS_FALLOCATE
521 #	include <sys/syscall.h>
522 #	include <sys/types.h>
523 #	include <unistd.h>
524 #elif defined HAVE_POSIX_FALLOCATE
525 #	define _XOPEN_SOURCE 600
526 #	include <stdlib.h>
527 #	ifdef HAVE_FCNTL_H
528 #		include <fcntl.h>
529 #	endif
530 #endif
531 #include <stdlib.h>
532 #include <errno.h>
533 
CAllocateFileTask(CPartFile * file,bool pause)534 CAllocateFileTask::CAllocateFileTask(CPartFile *file, bool pause)
535 	// GetPrintable is used to improve the readability of the log.
536 	: CThreadTask(wxT("Allocating"), file->GetFullName().RemoveExt().GetPrintable(), ETP_High),
537 	  m_file(file), m_pause(pause), m_result(ENOSYS)
538 {
539 	wxASSERT(file != NULL);
540 }
541 
Entry()542 void CAllocateFileTask::Entry()
543 {
544 	if (m_file->GetFileSize() == 0) {
545 		m_result = 0;
546 		return;
547 	}
548 
549 	uint64_t minFree = thePrefs::IsCheckDiskspaceEnabled() ? thePrefs::GetMinFreeDiskSpace() : 0;
550 	int64_t freeSpace = CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
551 
552 	// Don't even try to allocate, if there's no space to complete the operation.
553 	if (freeSpace != wxInvalidOffset) {
554 		if ((uint64_t)freeSpace < m_file->GetFileSize() + minFree) {
555 			m_result = ENOSPC;
556 			return;
557 		}
558 	}
559 
560 	CFile file;
561 	file.Open(m_file->GetFullName().RemoveExt(), CFile::read_write);
562 
563 #ifdef __WINDOWS__
564 	try {
565 		// File is already created as non-sparse, so we only need to set the length.
566 		// This will fail to allocate the file e.g. under wine on linux/ext3,
567 		// but works with NTFS and FAT32.
568 		file.Seek(m_file->GetFileSize() - 1, wxFromStart);
569 		file.WriteUInt8(0);
570 		file.Close();
571 		m_result = 0;
572 	} catch (const CSafeIOException&) {
573 		m_result = errno;
574 	}
575 #else
576 	// Use kernel level routines if possible
577 #  ifdef HAVE_FALLOCATE
578 	m_result = fallocate(file.fd(), 0, 0, m_file->GetFileSize());
579 #  elif defined HAVE_SYS_FALLOCATE
580 	m_result = syscall(SYS_fallocate, file.fd(), 0, (loff_t)0, (loff_t)m_file->GetFileSize());
581 	if (m_result == -1) {
582 		m_result = errno;
583 	}
584 #  elif defined HAVE_POSIX_FALLOCATE
585 	// otherwise use glibc implementation, if available
586 	m_result = posix_fallocate(file.fd(), 0, m_file->GetFileSize());
587 #  endif
588 
589 	if (m_result != 0 && m_result != ENOSPC) {
590 		// If everything else fails, use slow-and-dirty method of allocating the file: write the whole file with zeroes.
591 #  define BLOCK_SIZE	1048576		/* Write 1 MB blocks */
592 		void *zero = calloc(1, BLOCK_SIZE);
593 		if (zero != NULL) {
594 			try {
595 				uint64_t size = m_file->GetFileSize();
596 				for (; size >= BLOCK_SIZE; size -= BLOCK_SIZE) {
597 					file.Write(zero, BLOCK_SIZE);
598 				}
599 				if (size > 0) {
600 					file.Write(zero, size);
601 				}
602 				file.Close();
603 				m_result = 0;
604 			} catch (const CSafeIOException&) {
605 				m_result = errno;
606 			}
607 			free(zero);
608 		} else {
609 			m_result = ENOMEM;
610 		}
611 	}
612 
613 #endif
614 	if (file.IsOpened()) {
615 		file.Close();
616 	}
617 }
618 
OnExit()619 void CAllocateFileTask::OnExit()
620 {
621 	// Notify the app that the preallocation has finished for this file.
622 	CAllocFinishedEvent evt(m_file, m_pause, m_result);
623 
624 	wxPostEvent(wxTheApp, evt);
625 }
626 
627 
628 
629 ////////////////////////////////////////////////////////////
630 // CHashingEvent
631 
632 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_HASHING)
DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_AICH_HASHING)633 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_AICH_HASHING)
634 
635 CHashingEvent::CHashingEvent(wxEventType type, CKnownFile* result, const CKnownFile* owner)
636 	: wxEvent(-1, type),
637 	  m_owner(owner),
638 	  m_result(result)
639 {
640 }
641 
642 
Clone() const643 wxEvent* CHashingEvent::Clone() const
644 {
645 	return new CHashingEvent(GetEventType(), m_result, m_owner);
646 }
647 
648 
GetOwner() const649 const CKnownFile* CHashingEvent::GetOwner() const
650 {
651 	return m_owner;
652 }
653 
654 
GetResult() const655 CKnownFile* CHashingEvent::GetResult() const
656 {
657 	return m_result;
658 }
659 
660 
661 
662 
663 ////////////////////////////////////////////////////////////
664 // CCompletionEvent
665 
DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_FILE_COMPLETED)666 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_FILE_COMPLETED)
667 
668 
669 CCompletionEvent::CCompletionEvent(bool errorOccured, const CPartFile* owner, const CPath& fullPath)
670 	: wxEvent(-1, MULE_EVT_FILE_COMPLETED),
671 	  m_fullPath(fullPath),
672 	  m_owner(owner),
673 	  m_error(errorOccured)
674 {
675 }
676 
677 
Clone() const678 wxEvent* CCompletionEvent::Clone() const
679 {
680 	return new CCompletionEvent(m_error, m_owner, m_fullPath);
681 }
682 
683 
ErrorOccured() const684 bool CCompletionEvent::ErrorOccured() const
685 {
686 	return m_error;
687 }
688 
689 
GetOwner() const690 const CPartFile* CCompletionEvent::GetOwner() const
691 {
692 	return m_owner;
693 }
694 
695 
GetFullPath() const696 const CPath& CCompletionEvent::GetFullPath() const
697 {
698 	return m_fullPath;
699 }
700 
701 
702 ////////////////////////////////////////////////////////////
703 // CAllocFinishedEvent
704 
DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_ALLOC_FINISHED)705 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_ALLOC_FINISHED)
706 
707 wxEvent *CAllocFinishedEvent::Clone() const
708 {
709 	return new CAllocFinishedEvent(m_file, m_pause, m_result);
710 }
711 
712 // File_checked_for_headers
713