1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2008-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
23 //
24 
25 #include "PlatformSpecific.h"
26 #include "config.h"
27 
28 // NTFS Sparse Files (only for MSW)
29 #ifdef __WINDOWS__
30 #include "common/Format.h"
31 #include "Logger.h"
32 #include <winbase.h>
33 #include <winioctl.h>
34 #ifndef FSCTL_SET_SPARSE
35 #	define FSCTL_SET_SPARSE		CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
36 #endif
37 #ifndef FSCTL_SET_ZERO_DATA
38 #	define FSCTL_SET_ZERO_DATA	CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, FILE_WRITE_DATA)
39 #endif
40 
41 // Create a message from a Windows error code
SystemError()42 static wxString SystemError()
43 {
44 	WCHAR * lpMsgBuf = NULL;
45 
46 	FormatMessageW(
47 		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
48 		NULL,
49 		GetLastError(),
50 		0, // Default language
51 		(LPWSTR) &lpMsgBuf,
52 		0,
53 		NULL
54 	);
55 
56 	wxString ret(lpMsgBuf);
57 	LocalFree(lpMsgBuf);
58 	return ret;
59 }
60 
61 // Create a file in sparse mode
CreateSparseFile(const CPath & name,uint64_t size)62 bool PlatformSpecific::CreateSparseFile(const CPath& name, uint64_t size)
63 {
64 	DWORD dwReturnedBytes=0;
65 
66 	HANDLE hd = CreateFileW(name.GetRaw().c_str(),
67 		GENERIC_READ | GENERIC_WRITE,
68 		0,       // share - not shareable
69 		NULL,    // security - not inheritable
70 		CREATE_ALWAYS,
71 		FILE_ATTRIBUTE_ARCHIVE,
72 		NULL);
73 	if (hd == INVALID_HANDLE_VALUE) {
74 		AddDebugLogLineC(logPartFile, CFormat(wxT("converting %s to sparse failed (OPEN): %s ")) % name % SystemError());
75 		return false;
76 	}
77 
78 	if (!DeviceIoControl(hd, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwReturnedBytes, NULL)) {
79 		AddDebugLogLineC(logPartFile, CFormat(wxT("converting %s to sparse failed (SET_SPARSE): %s ")) % name % SystemError());
80 	} else {
81 		// FILE_ZERO_DATA_INFORMATION is not defined here
82 		struct {
83 			uint64 FileOffset;
84 			uint64 BeyondFinalZero;
85 		} fzdi;
86 		fzdi.FileOffset = 0;
87 		fzdi.BeyondFinalZero = size;
88 		LARGE_INTEGER largo;
89 		largo.QuadPart = size;
90 
91 		// zero the data
92 		if (!DeviceIoControl(hd, FSCTL_SET_ZERO_DATA, (LPVOID) &fzdi, sizeof(fzdi), NULL, 0, &dwReturnedBytes, NULL)) {
93 			AddDebugLogLineC(logPartFile, CFormat(wxT("converting %s to sparse failed (ZERO): %s")) % name % SystemError());
94 		} else if (!SetFilePointerEx(hd, largo, NULL, FILE_BEGIN) || !SetEndOfFile(hd)) {
95 			AddDebugLogLineC(logPartFile, CFormat(wxT("converting %s to sparse failed (SEEK): %s")) % name % SystemError());
96 		}
97 	}
98 	CloseHandle(hd);
99 	return true;
100 }
101 
102 #else  // non Windows systems don't need all this
103 #include "CFile.h"
104 
CreateSparseFile(const CPath & name,uint64_t WXUNUSED (size))105 bool PlatformSpecific::CreateSparseFile(const CPath& name, uint64_t WXUNUSED(size))
106 {
107 	CFile f;
108 	return f.Create(name.GetRaw(), true) && f.Close();
109 }
110 
111 #endif
112 
113 #ifdef __WINDOWS__
114 #include <wx/msw/registry.h>
115 #include <wx/utils.h>
116 
117 // Get the max number of connections that the OS supports, or -1 for default
GetMaxConnections()118 int PlatformSpecific::GetMaxConnections()
119 {
120 	int maxconn = -1;
121 	// Try to get the max connection value in the registry
122 	wxRegKey key( wxT("HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Services\\VxD\\MSTCP\\MaxConnections") );
123 	wxString value;
124 	if ( key.Exists() ) {
125 		value = key.QueryDefaultValue();
126 	}
127 	if ( !value.IsEmpty() && value.IsNumber() ) {
128 		long mc;
129 		value.ToLong(&mc);
130 		maxconn = (int)mc;
131 	} else {
132 		switch (wxGetOsVersion()) {
133 		case wxOS_WINDOWS_9X:
134 			// This includes all Win9x versions
135 			maxconn = 50;
136 			break;
137 		case wxOS_WINDOWS_NT:
138 			// This includes NT based windows
139 			maxconn = 500;
140 			break;
141 		default:
142 			// Anything else. Let aMule decide...
143 			break;
144 		}
145 	}
146 
147 	return maxconn;
148 }
149 #endif
150 
151 
152 #ifdef __WINDOWS__
153 #include <winbase.h>
154 #include <shlwapi.h>
155 
doGetFilesystemType(const CPath & path)156 static PlatformSpecific::EFSType doGetFilesystemType(const CPath& path)
157 {
158 	wxWritableWCharBuffer pathRaw(path.GetRaw().wchar_str());
159 	LPWSTR volume = pathRaw;
160 	if (!PathStripToRootW(volume)) {
161 		return PlatformSpecific::fsOther;
162 	}
163 	PathAddBackslashW(volume);
164 
165 	DWORD maximumComponentLength = 0;
166 	DWORD filesystemFlags = 0;
167 	WCHAR filesystemNameBuffer[128];
168 	if (!GetVolumeInformationW(volume, NULL, 0, NULL, &maximumComponentLength, &filesystemFlags, filesystemNameBuffer, 128)) {
169 		return PlatformSpecific::fsOther;
170 	}
171 	if (wxStrnicmp(filesystemNameBuffer, wxT("FAT"), 3) == 0) {
172 		return PlatformSpecific::fsFAT;
173 	} else if (wxStrcmp(filesystemNameBuffer, wxT("NTFS")) == 0) {
174 		return PlatformSpecific::fsNTFS;
175 	}
176 	return PlatformSpecific::fsOther;
177 };
178 
179 #elif defined(HAVE_GETMNTENT) && defined(HAVE_MNTENT_H)
180 #include <stdio.h>
181 #include <string.h>
182 #include <mntent.h>
183 #ifndef _PATH_MOUNTED
184 #	define _PATH_MOUNTED	"/etc/mtab"
185 #endif
186 #include <common/StringFunctions.h>
187 
doGetFilesystemType(const CPath & path)188 static PlatformSpecific::EFSType doGetFilesystemType(const CPath& path)
189 {
190 	struct mntent *entry = NULL;
191 	PlatformSpecific::EFSType retval = PlatformSpecific::fsOther;
192 	FILE *mnttab = fopen(_PATH_MOUNTED, "r");
193 	unsigned bestPrefixLen = 0;
194 
195 	if (mnttab == NULL) {
196 		return PlatformSpecific::fsOther;
197 	}
198 
199 	while ((entry = getmntent(mnttab)) != NULL) {
200 		if (entry->mnt_dir) {
201 			wxString dir = char2unicode(entry->mnt_dir);
202 			if (dir == path.GetRaw().Mid(0, dir.Length())) {
203 				if (dir.Length() >= bestPrefixLen) {
204 					if (entry->mnt_type == NULL) {
205 						break;
206 					} else if (!strcmp(entry->mnt_type, "ntfs")) {
207 						retval = PlatformSpecific::fsNTFS;
208 					} else if (!strcmp(entry->mnt_type, "msdos") ||
209 						   !strcmp(entry->mnt_type, "umsdos") ||
210 						   !strcmp(entry->mnt_type, "vfat") ||
211 						   !strncmp(entry->mnt_type, "fat", 3)) {
212 						retval = PlatformSpecific::fsFAT;
213 					} else if (!strcmp(entry->mnt_type, "hfs")) {
214 						retval = PlatformSpecific::fsHFS;
215 					} else if (!strcmp(entry->mnt_type, "hpfs")) {
216 						retval = PlatformSpecific::fsHPFS;
217 					} else if (!strcmp(entry->mnt_type, "minix")) {
218 						retval = PlatformSpecific::fsMINIX;
219 					} /* Add more filesystem types here */
220 					else if (dir.Length() > bestPrefixLen) {
221 						retval = PlatformSpecific::fsOther;
222 					}
223 					bestPrefixLen = dir.Length();
224 				}
225 			}
226 		}
227 	}
228 	fclose(mnttab);
229 	return retval;
230 }
231 
232 #elif defined(HAVE_GETMNTENT) && defined(HAVE_SYS_MNTENT_H) && defined(HAVE_SYS_MNTTAB_H)
233 #include <stdio.h>
234 #include <string.h>
235 #include <sys/mntent.h>
236 #include <sys/mnttab.h>
237 #ifndef MNTTAB
238 #	define MNTTAB	"/etc/mnttab"
239 #endif
240 #include <common/StringFunctions.h>
241 
doGetFilesystemType(const CPath & path)242 static PlatformSpecific::EFSType doGetFilesystemType(const CPath& path)
243 {
244 	struct mnttab entryStatic;
245 	struct mnttab *entry = &entryStatic;
246 	PlatformSpecific::EFSType retval = PlatformSpecific::fsOther;
247 	FILE *fmnttab = fopen(MNTTAB, "r");
248 	unsigned bestPrefixLen = 0;
249 
250 	if (fmnttab == NULL) {
251 		return PlatformSpecific::fsOther;
252 	}
253 
254 	while (getmntent(fmnttab, entry) == 0) {
255 		if (entry->mnt_mountp) {
256 			wxString dir = char2unicode(entry->mnt_mountp);
257 			if (dir == path.GetRaw().Mid(0, dir.Length())) {
258 				if (dir.Length() >= bestPrefixLen) {
259 					if (entry->mnt_fstype == NULL) {
260 						break;
261 					} else if (!strcmp(entry->mnt_fstype, MNTTYPE_PCFS)) {
262 						retval = PlatformSpecific::fsFAT;
263 					} else if (hasmntopt(entry, MNTOPT_NOLARGEFILES)) {
264 						// MINIX is a file system that can handle special chars but has no large files.
265 						retval = PlatformSpecific::fsMINIX;
266 					} else if (dir.Length() > bestPrefixLen) {
267 						retval = PlatformSpecific::fsOther;
268 					}
269 					bestPrefixLen = dir.Length();
270 				}
271 			}
272 		}
273 	}
274 	fclose(fmnttab);
275 	return retval;
276 }
277 
278 #else
279 
280 // No way to determine filesystem type, no restrictions apply.
doGetFilesystemType(const CPath & WXUNUSED (path))281 static inline PlatformSpecific::EFSType doGetFilesystemType(const CPath& WXUNUSED(path))
282 {
283 	return PlatformSpecific::fsOther;
284 }
285 
286 #endif
287 
288 #include <map>
289 #include <wx/thread.h>
290 
GetFilesystemType(const CPath & path)291 PlatformSpecific::EFSType PlatformSpecific::GetFilesystemType(const CPath& path)
292 {
293 	typedef std::map<wxString, EFSType>	FSMap;
294 	// Caching previous results, to speed up further checks.
295 	static FSMap	s_fscache;
296 	// Lock used to ensure the integrity of the cache.
297 	static wxMutex	s_lock;
298 
299 	wxCHECK_MSG(path.IsOk(), fsOther, wxT("Invalid path in GetFilesystemType()"));
300 
301 	wxMutexLocker locker(s_lock);
302 
303 	FSMap::iterator it = s_fscache.find(path.GetRaw());
304 	if (it != s_fscache.end()) {
305 		return it->second;
306 	}
307 
308 	return s_fscache[path.GetRaw()] = doGetFilesystemType(path);
309 }
310 
311 
312 // Power event vetoing
313 
314 static bool m_preventingSleepMode = false;
315 
316 #if defined(__WXMAC__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050	// 10.5 only
317 	#include <IOKit/pwr_mgt/IOPMLib.h>
318 	static IOPMAssertionID assertionID;
319 #endif
320 
PreventSleepMode()321 void PlatformSpecific::PreventSleepMode()
322 {
323 	if (!m_preventingSleepMode) {
324 		#ifdef _MSC_VER
325 			SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
326 			m_preventingSleepMode = true;
327 
328 		// IOPMAssertionCreate has been introduced in Leopard (10.5) but deprecated starting from Snow Leopard(10.6)
329 		// For more details see:
330 		// - http://developer.apple.com/library/mac/#qa/qa1340/_index.html
331 		// - http://www.cimgf.com/2009/10/14/the-journey-to-disabling-sleep-with-iokit/
332 		#elif defined(__WXMAC__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060	// 10.6 only
333 			CFStringRef reasonForActivity= CFSTR("Prevent Display Sleep");
334 			IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
335 												kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
336 			if (success == kIOReturnSuccess) {
337 				// Correctly vetoed, flag so we don't do it again.
338 				m_preventingSleepMode = true;
339 			} else {
340 				// May be should be better to trace in log?
341 			}
342 
343 		#elif defined(__WXMAC__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050	// 10.5 only
344 			IOReturn success = IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep,
345 												kIOPMAssertionLevelOn, &assertionID);
346 			if (success == kIOReturnSuccess) {
347 				// Correctly vetoed, flag so we don't do it again.
348 				m_preventingSleepMode = true;
349 			} else {
350 				// ??
351 			}
352 		#else
353 			//#warning Power event vetoing not implemented.
354 			// Not implemented
355 		#endif
356 	}
357 }
358 
AllowSleepMode()359 void PlatformSpecific::AllowSleepMode()
360 {
361 	if (m_preventingSleepMode) {
362 		#ifdef _MSC_VER
363 			SetThreadExecutionState(ES_CONTINUOUS); // Clear the system request flag.
364 			m_preventingSleepMode = false;
365 		#elif defined(__WXMAC__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050	// 10.5 only
366 			IOReturn success = IOPMAssertionRelease(assertionID);
367 			if (success == kIOReturnSuccess) {
368 				// Correctly restored, flag so we don't do it again.
369 				m_preventingSleepMode = false;
370 			} else {
371 				// ??
372 			}
373 		#else
374 			// Not implemented
375 		#endif
376 	}
377 }
378