diff options
Diffstat (limited to 'utils/rbutilqt/base/utils.cpp')
-rw-r--r-- | utils/rbutilqt/base/utils.cpp | 1062 |
1 files changed, 1062 insertions, 0 deletions
diff --git a/utils/rbutilqt/base/utils.cpp b/utils/rbutilqt/base/utils.cpp new file mode 100644 index 0000000000..f2d3f04887 --- /dev/null +++ b/utils/rbutilqt/base/utils.cpp | |||
@@ -0,0 +1,1062 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2007 by Dominik Wenger | ||
10 | * | ||
11 | * All files in this archive are subject to the GNU General Public License. | ||
12 | * See the file COPYING in the source tree root for full license agreement. | ||
13 | * | ||
14 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
15 | * KIND, either express or implied. | ||
16 | * | ||
17 | ****************************************************************************/ | ||
18 | |||
19 | #include "utils.h" | ||
20 | #include "rockboxinfo.h" | ||
21 | #include "system.h" | ||
22 | #include "rbsettings.h" | ||
23 | #include "playerbuildinfo.h" | ||
24 | #include "Logger.h" | ||
25 | |||
26 | #if !defined(_UNICODE) | ||
27 | #define _UNICODE | ||
28 | #endif | ||
29 | |||
30 | #include <QtCore> | ||
31 | #include <QDebug> | ||
32 | #include <cstdlib> | ||
33 | #include <stdio.h> | ||
34 | |||
35 | #if defined(Q_OS_LINUX) || defined(Q_OS_MACX) | ||
36 | #include <sys/statvfs.h> | ||
37 | #endif | ||
38 | #if defined(Q_OS_LINUX) || defined(Q_OS_MACX) | ||
39 | #include <stdio.h> | ||
40 | #endif | ||
41 | #if defined(Q_OS_LINUX) | ||
42 | #include <mntent.h> | ||
43 | #endif | ||
44 | #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD) | ||
45 | #include <sys/param.h> | ||
46 | #include <sys/ucred.h> | ||
47 | #include <sys/mount.h> | ||
48 | #endif | ||
49 | #if defined(Q_OS_WIN32) | ||
50 | #include <stdio.h> | ||
51 | #include <tchar.h> | ||
52 | #include <windows.h> | ||
53 | #include <setupapi.h> | ||
54 | #include <winioctl.h> | ||
55 | #include <tlhelp32.h> | ||
56 | #endif | ||
57 | #if defined(Q_OS_MACX) | ||
58 | #include <Carbon/Carbon.h> | ||
59 | #include <CoreFoundation/CoreFoundation.h> | ||
60 | #include <CoreServices/CoreServices.h> | ||
61 | #include <IOKit/IOKitLib.h> | ||
62 | #endif | ||
63 | |||
64 | // recursive function to delete a dir with files | ||
65 | bool Utils::recursiveRmdir( const QString &dirName ) | ||
66 | { | ||
67 | QString dirN = dirName; | ||
68 | QDir dir(dirN); | ||
69 | // make list of entries in directory | ||
70 | QStringList list = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot); | ||
71 | QFileInfo fileInfo; | ||
72 | QString curItem; | ||
73 | for(int i = 0; i < list.size(); i++){ // loop through all items of list | ||
74 | QString name = list.at(i); | ||
75 | curItem = dirN + "/" + name; | ||
76 | fileInfo.setFile(curItem); | ||
77 | if(fileInfo.isDir()) // is directory | ||
78 | recursiveRmdir(curItem); // call recRmdir() recursively for | ||
79 | // deleting subdirectory | ||
80 | else // is file | ||
81 | QFile::remove(curItem); // ok, delete file | ||
82 | } | ||
83 | dir.cdUp(); | ||
84 | return dir.rmdir(dirN); // delete empty dir and return if (now empty) | ||
85 | // dir-removing was successfull | ||
86 | } | ||
87 | |||
88 | |||
89 | //! @brief resolves the given path, ignoring case. | ||
90 | //! @param path absolute path to resolve. | ||
91 | //! @return returns exact casing of path, empty string if path not found. | ||
92 | QString Utils::resolvePathCase(QString path) | ||
93 | { | ||
94 | int start; | ||
95 | QString realpath; | ||
96 | #if QT_VERSION >= 0x050e00 | ||
97 | QStringList elems = path.split("/", Qt::SkipEmptyParts); | ||
98 | #else | ||
99 | QStringList elems = path.split("/", QString::SkipEmptyParts); | ||
100 | #endif | ||
101 | |||
102 | if(path.isEmpty()) | ||
103 | return QString(); | ||
104 | #if defined(Q_OS_WIN32) | ||
105 | // on windows we must make sure to start with the first entry (i.e. the | ||
106 | // drive letter) instead of a single / to make resolving work. | ||
107 | start = 1; | ||
108 | realpath = elems.at(0) + "/"; | ||
109 | #else | ||
110 | start = 0; | ||
111 | realpath = "/"; | ||
112 | #endif | ||
113 | |||
114 | for(int i = start; i < elems.size(); i++) { | ||
115 | QStringList direlems | ||
116 | = QDir(realpath).entryList(QDir::AllEntries|QDir::Hidden|QDir::System); | ||
117 | if(direlems.contains(elems.at(i), Qt::CaseInsensitive)) { | ||
118 | // need to filter using QRegExp as QStringList::filter(QString) | ||
119 | // matches any substring | ||
120 | QString expr = QString("^" + elems.at(i) + "$"); | ||
121 | QRegExp rx = QRegExp(expr, Qt::CaseInsensitive); | ||
122 | QStringList a = direlems.filter(rx); | ||
123 | |||
124 | if(a.size() != 1) | ||
125 | return QString(""); | ||
126 | if(!realpath.endsWith("/")) | ||
127 | realpath += "/"; | ||
128 | realpath += a.at(0); | ||
129 | } | ||
130 | else | ||
131 | return QString(""); | ||
132 | } | ||
133 | LOG_INFO() << "resolving path" << path << "->" << realpath; | ||
134 | return realpath; | ||
135 | } | ||
136 | |||
137 | |||
138 | QString Utils::filesystemType(QString path) | ||
139 | { | ||
140 | #if defined(Q_OS_LINUX) | ||
141 | FILE *mn = setmntent("/etc/mtab", "r"); | ||
142 | if(!mn) | ||
143 | return QString(""); | ||
144 | |||
145 | struct mntent *ent; | ||
146 | while((ent = getmntent(mn))) { | ||
147 | if(QString(ent->mnt_dir) == path) { | ||
148 | endmntent(mn); | ||
149 | LOG_INFO() << "device type is" << ent->mnt_type; | ||
150 | return QString(ent->mnt_type); | ||
151 | } | ||
152 | } | ||
153 | endmntent(mn); | ||
154 | #endif | ||
155 | |||
156 | #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD) | ||
157 | int num; | ||
158 | struct statfs *mntinf; | ||
159 | |||
160 | num = getmntinfo(&mntinf, MNT_WAIT); | ||
161 | while(num--) { | ||
162 | if(QString(mntinf->f_mntonname) == path) { | ||
163 | LOG_INFO() << "device type is" << mntinf->f_fstypename; | ||
164 | return QString(mntinf->f_fstypename); | ||
165 | } | ||
166 | mntinf++; | ||
167 | } | ||
168 | #endif | ||
169 | |||
170 | #if defined(Q_OS_WIN32) | ||
171 | wchar_t t[64]; | ||
172 | memset(t, 0, 32); | ||
173 | if(GetVolumeInformationW((LPCWSTR)path.utf16(), | ||
174 | NULL, 0, NULL, NULL, NULL, t, 64)) { | ||
175 | LOG_INFO() << "device type is" << t; | ||
176 | return QString::fromWCharArray(t); | ||
177 | } | ||
178 | #endif | ||
179 | return QString("-"); | ||
180 | } | ||
181 | |||
182 | |||
183 | QString Utils::filesystemName(QString path) | ||
184 | { | ||
185 | QString name; | ||
186 | #if defined(Q_OS_WIN32) | ||
187 | wchar_t volname[MAX_PATH+1]; | ||
188 | bool res = GetVolumeInformationW((LPTSTR)path.utf16(), volname, MAX_PATH+1, | ||
189 | NULL, NULL, NULL, NULL, 0); | ||
190 | if(res) { | ||
191 | name = QString::fromWCharArray(volname); | ||
192 | } | ||
193 | #endif | ||
194 | #if defined(Q_OS_MACX) | ||
195 | // BSD label does not include folder. | ||
196 | QString bsd = Utils::resolveDevicename(path).remove("/dev/"); | ||
197 | if(bsd.isEmpty()) { | ||
198 | return name; | ||
199 | } | ||
200 | OSStatus result; | ||
201 | ItemCount index = 1; | ||
202 | |||
203 | do { | ||
204 | FSVolumeRefNum volrefnum; | ||
205 | HFSUniStr255 volname; | ||
206 | |||
207 | result = FSGetVolumeInfo(kFSInvalidVolumeRefNum, index, &volrefnum, | ||
208 | kFSVolInfoFSInfo, NULL, &volname, NULL); | ||
209 | |||
210 | if(result == noErr) { | ||
211 | GetVolParmsInfoBuffer volparms; | ||
212 | /* PBHGetVolParmsSync() is not available for 64bit while | ||
213 | FSGetVolumeParms() is available in 10.5+. Thus we need to use | ||
214 | PBHGetVolParmsSync() for 10.4, and that also requires 10.4 to | ||
215 | always use 32bit. | ||
216 | Qt 4 supports 32bit on 10.6 Cocoa only. | ||
217 | */ | ||
218 | #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 | ||
219 | if(FSGetVolumeParms(volrefnum, &volparms, sizeof(volparms)) == noErr) | ||
220 | #else | ||
221 | HParamBlockRec hpb; | ||
222 | hpb.ioParam.ioNamePtr = NULL; | ||
223 | hpb.ioParam.ioVRefNum = volrefnum; | ||
224 | hpb.ioParam.ioBuffer = (Ptr)&volparms; | ||
225 | hpb.ioParam.ioReqCount = sizeof(volparms); | ||
226 | if(PBHGetVolParmsSync(&hpb) == noErr) | ||
227 | #endif | ||
228 | { | ||
229 | if(volparms.vMServerAdr == 0) { | ||
230 | if(bsd == (char*)volparms.vMDeviceID) { | ||
231 | name = QString::fromUtf16((const ushort*)volname.unicode, | ||
232 | (int)volname.length); | ||
233 | break; | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | index++; | ||
239 | } while(result == noErr); | ||
240 | #endif | ||
241 | |||
242 | LOG_INFO() << "Volume name of" << path << "is" << name; | ||
243 | return name; | ||
244 | } | ||
245 | |||
246 | |||
247 | //! @brief figure the free disk space on a filesystem | ||
248 | //! @param path path on the filesystem to check | ||
249 | //! @return size in bytes | ||
250 | qulonglong Utils::filesystemFree(QString path) | ||
251 | { | ||
252 | qulonglong size = filesystemSize(path, FilesystemFree); | ||
253 | LOG_INFO() << "free disk space for" << path << size; | ||
254 | return size; | ||
255 | } | ||
256 | |||
257 | |||
258 | qulonglong Utils::filesystemTotal(QString path) | ||
259 | { | ||
260 | qulonglong size = filesystemSize(path, FilesystemTotal); | ||
261 | LOG_INFO() << "total disk space for" << path << size; | ||
262 | return size; | ||
263 | } | ||
264 | |||
265 | |||
266 | qulonglong Utils::filesystemSize(QString path, enum Utils::Size type) | ||
267 | { | ||
268 | qulonglong size = 0; | ||
269 | #if defined(Q_OS_LINUX) || defined(Q_OS_MACX) | ||
270 | // the usage of statfs() is deprecated by the LSB so use statvfs(). | ||
271 | struct statvfs fs; | ||
272 | int ret; | ||
273 | |||
274 | ret = statvfs(qPrintable(path), &fs); | ||
275 | |||
276 | if(ret == 0) { | ||
277 | if(type == FilesystemFree) { | ||
278 | size = (qulonglong)fs.f_frsize * (qulonglong)fs.f_bavail; | ||
279 | } | ||
280 | if(type == FilesystemTotal) { | ||
281 | size = (qulonglong)fs.f_frsize * (qulonglong)fs.f_blocks; | ||
282 | } | ||
283 | if(type == FilesystemClusterSize) { | ||
284 | size = (qulonglong)fs.f_frsize; | ||
285 | } | ||
286 | } | ||
287 | #endif | ||
288 | #if defined(Q_OS_WIN32) | ||
289 | BOOL ret; | ||
290 | ULARGE_INTEGER freeAvailBytes; | ||
291 | ULARGE_INTEGER totalNumberBytes; | ||
292 | |||
293 | ret = GetDiskFreeSpaceExW((LPCTSTR)path.utf16(), &freeAvailBytes, | ||
294 | &totalNumberBytes, NULL); | ||
295 | if(ret) { | ||
296 | if(type == FilesystemFree) { | ||
297 | size = freeAvailBytes.QuadPart; | ||
298 | } | ||
299 | if(type == FilesystemTotal) { | ||
300 | size = totalNumberBytes.QuadPart; | ||
301 | } | ||
302 | if(type == FilesystemClusterSize) { | ||
303 | DWORD sectorsPerCluster; | ||
304 | DWORD bytesPerSector; | ||
305 | DWORD freeClusters; | ||
306 | DWORD totalClusters; | ||
307 | ret = GetDiskFreeSpaceW((LPCTSTR)path.utf16(), §orsPerCluster, | ||
308 | &bytesPerSector, &freeClusters, &totalClusters); | ||
309 | if(ret) { | ||
310 | size = bytesPerSector * sectorsPerCluster; | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | #endif | ||
315 | return size; | ||
316 | } | ||
317 | |||
318 | //! \brief searches for a Executable in the Environement Path | ||
319 | QString Utils::findExecutable(QString name) | ||
320 | { | ||
321 | //try autodetect tts | ||
322 | #if defined(Q_OS_LINUX) || defined(Q_OS_MACX) || defined(Q_OS_OPENBSD) | ||
323 | #if QT_VERSION >= 0x050e00 | ||
324 | QStringList path = QString(getenv("PATH")).split(":", Qt::SkipEmptyParts); | ||
325 | #else | ||
326 | QStringList path = QString(getenv("PATH")).split(":", QString::SkipEmptyParts); | ||
327 | #endif | ||
328 | #elif defined(Q_OS_WIN) | ||
329 | #if QT_VERSION >= 0x050e00 | ||
330 | QStringList path = QString(getenv("PATH")).split(";", Qt::SkipEmptyParts); | ||
331 | #else | ||
332 | QStringList path = QString(getenv("PATH")).split(";", QString::SkipEmptyParts); | ||
333 | #endif | ||
334 | #endif | ||
335 | LOG_INFO() << "system path:" << path; | ||
336 | for(int i = 0; i < path.size(); i++) | ||
337 | { | ||
338 | QString executable = QDir::fromNativeSeparators(path.at(i)) + "/" + name; | ||
339 | #if defined(Q_OS_WIN) | ||
340 | executable += ".exe"; | ||
341 | #if QT_VERSION >= 0x050e00 | ||
342 | QStringList ex = executable.split("\"", Qt::SkipEmptyParts); | ||
343 | #else | ||
344 | QStringList ex = executable.split("\"", QString::SkipEmptyParts); | ||
345 | #endif | ||
346 | executable = ex.join(""); | ||
347 | #endif | ||
348 | if(QFileInfo(executable).isExecutable()) | ||
349 | { | ||
350 | LOG_INFO() << "findExecutable: found" << executable; | ||
351 | return QDir::toNativeSeparators(executable); | ||
352 | } | ||
353 | } | ||
354 | LOG_INFO() << "findExecutable: could not find" << name; | ||
355 | return ""; | ||
356 | } | ||
357 | |||
358 | |||
359 | /** @brief checks different Enviroment things. Ask if user wants to continue. | ||
360 | * @param permission if it should check for permission | ||
361 | * @return string with error messages if problems occurred, empty strings if none. | ||
362 | */ | ||
363 | QString Utils::checkEnvironment(bool permission) | ||
364 | { | ||
365 | LOG_INFO() << "checking environment"; | ||
366 | QString text = ""; | ||
367 | |||
368 | // check permission | ||
369 | if(permission) | ||
370 | { | ||
371 | #if defined(Q_OS_WIN32) | ||
372 | if(System::userPermissions() != System::ADMIN) | ||
373 | { | ||
374 | text += tr("<li>Permissions insufficient for bootloader " | ||
375 | "installation.\nAdministrator priviledges are necessary.</li>"); | ||
376 | } | ||
377 | #endif | ||
378 | } | ||
379 | |||
380 | // Check TargetId | ||
381 | RockboxInfo rbinfo(RbSettings::value(RbSettings::Mountpoint).toString()); | ||
382 | QString installed = rbinfo.target(); | ||
383 | if(!installed.isEmpty() && installed != | ||
384 | RbSettings::value(RbSettings::CurrentPlatform).toString().split(".").at(0)) | ||
385 | { | ||
386 | text += tr("<li>Target mismatch detected.<br/>" | ||
387 | "Installed target: %1<br/>Selected target: %2.</li>") | ||
388 | .arg(PlayerBuildInfo::instance()->value( | ||
389 | PlayerBuildInfo::DisplayName, installed).toString(), | ||
390 | PlayerBuildInfo::instance()->value( | ||
391 | PlayerBuildInfo::DisplayName).toString()); | ||
392 | } | ||
393 | |||
394 | if(!text.isEmpty()) | ||
395 | return tr("Problem detected:") + "<ul>" + text + "</ul>"; | ||
396 | else | ||
397 | return text; | ||
398 | } | ||
399 | |||
400 | /** @brief Trim version string from filename to version part only. | ||
401 | * @param s Version string | ||
402 | * @return Version part of string if found, input string on error. | ||
403 | */ | ||
404 | QString Utils::trimVersionString(QString s) | ||
405 | { | ||
406 | QRegExp r = QRegExp(".*([\\d\\.]+\\d+[a-z]?).*"); | ||
407 | if(r.indexIn(s) != -1) { | ||
408 | return r.cap(1); | ||
409 | } | ||
410 | return s; | ||
411 | } | ||
412 | |||
413 | /** @brief Compare two version strings. | ||
414 | * @param s1 first version string | ||
415 | * @param s2 second version string | ||
416 | * @return 0 if strings identical, 1 if second is newer, -1 if first. | ||
417 | */ | ||
418 | int Utils::compareVersionStrings(QString s1, QString s2) | ||
419 | { | ||
420 | LOG_INFO() << "comparing version strings" << s1 << "and" << s2; | ||
421 | QString a = s1.trimmed(); | ||
422 | QString b = s2.trimmed(); | ||
423 | // if strings are identical return 0. | ||
424 | if(a.isEmpty()) | ||
425 | return 1; | ||
426 | if(b.isEmpty()) | ||
427 | return -1; | ||
428 | |||
429 | while(!a.isEmpty() || !b.isEmpty()) { | ||
430 | // trim all leading non-digits and non-dots (dots are removed afterwards) | ||
431 | a.remove(QRegExp("^[^\\d\\.]*")); | ||
432 | b.remove(QRegExp("^[^\\d\\.]*")); | ||
433 | |||
434 | // trim all trailing non-digits for conversion (QString::toInt() | ||
435 | // requires this). Copy strings first as replace() changes the string. | ||
436 | QString numa = a; | ||
437 | QString numb = b; | ||
438 | numa.remove(QRegExp("\\D+.*$")); | ||
439 | numb.remove(QRegExp("\\D+.*$")); | ||
440 | |||
441 | // convert to number | ||
442 | bool ok1, ok2; | ||
443 | unsigned int vala = numa.toUInt(&ok1); | ||
444 | unsigned int valb = numb.toUInt(&ok2); | ||
445 | // if none of the numbers converted successfully we're at trailing garbage. | ||
446 | if(!ok1 && !ok2) | ||
447 | break; | ||
448 | if(!ok1) | ||
449 | return 1; | ||
450 | if(!ok2) | ||
451 | return -1; | ||
452 | |||
453 | // if numbers mismatch we have a decision. | ||
454 | if(vala != valb) | ||
455 | return (vala > valb) ? -1 : 1; | ||
456 | |||
457 | // trim leading digits. | ||
458 | a.remove(QRegExp("^\\d*")); | ||
459 | b.remove(QRegExp("^\\d*")); | ||
460 | |||
461 | // If only one of the following characters is a dot that one is | ||
462 | // "greater" then anything else. Make sure it's followed by a number, | ||
463 | // Otherwise it might be the end of the string or suffix. Do this | ||
464 | // before version addon characters check to avoid stopping too early. | ||
465 | bool adot = a.contains(QRegExp("^[a-zA-Z]*\\.[0-9]")); | ||
466 | bool bdot = b.contains(QRegExp("^[a-zA-Z]*\\.[0-9]")); | ||
467 | if(adot && !bdot) | ||
468 | return -1; | ||
469 | if(!adot && bdot) | ||
470 | return 1; | ||
471 | // if number is immediately followed by a character consider it as | ||
472 | // version addon (like 1.2.3b). In this case compare characters and end | ||
473 | // (version numbers like 1.2b.3 aren't handled). | ||
474 | QChar ltra; | ||
475 | QChar ltrb; | ||
476 | if(a.contains(QRegExp("^[a-zA-Z]"))) | ||
477 | ltra = a.at(0); | ||
478 | if(b.contains(QRegExp("^[a-zA-Z]"))) | ||
479 | ltrb = b.at(0); | ||
480 | if(ltra != ltrb) | ||
481 | return (ltra < ltrb) ? 1 : -1; | ||
482 | |||
483 | // both are identical or no addon characters, ignore. | ||
484 | // remove modifiers and following dot. | ||
485 | a.remove(QRegExp("^[a-zA-Z]*\\.")); | ||
486 | b.remove(QRegExp("^[a-zA-Z]*\\.")); | ||
487 | } | ||
488 | |||
489 | // no differences found. | ||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | |||
494 | /** Resolve mountpoint to devicename / disk number | ||
495 | * @param path mountpoint path / drive letter | ||
496 | * @return devicename / disk number | ||
497 | */ | ||
498 | QString Utils::resolveDevicename(QString path) | ||
499 | { | ||
500 | LOG_INFO() << "resolving device name" << path; | ||
501 | #if defined(Q_OS_LINUX) | ||
502 | FILE *mn = setmntent("/etc/mtab", "r"); | ||
503 | if(!mn) | ||
504 | return QString(""); | ||
505 | |||
506 | struct mntent *ent; | ||
507 | while((ent = getmntent(mn))) { | ||
508 | // check for valid filesystem type. | ||
509 | // Linux can handle hfs (and hfsplus), so consider it a valid file | ||
510 | // system. Otherwise resolving the device name would fail, which in | ||
511 | // turn would make it impossible to warn about a MacPod. | ||
512 | if(QString(ent->mnt_dir) == path | ||
513 | && (QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive) | ||
514 | || QString(ent->mnt_type).contains("hfs", Qt::CaseInsensitive))) { | ||
515 | endmntent(mn); | ||
516 | LOG_INFO() << "device name is" << ent->mnt_fsname; | ||
517 | return QString(ent->mnt_fsname); | ||
518 | } | ||
519 | } | ||
520 | endmntent(mn); | ||
521 | |||
522 | #endif | ||
523 | |||
524 | #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD) | ||
525 | int num; | ||
526 | struct statfs *mntinf; | ||
527 | |||
528 | num = getmntinfo(&mntinf, MNT_WAIT); | ||
529 | while(num--) { | ||
530 | // check for valid filesystem type. OS X can handle hfs (hfs+ is | ||
531 | // treated as hfs), BSD should be the same. | ||
532 | if(QString(mntinf->f_mntonname) == path | ||
533 | && (QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive) | ||
534 | || QString(mntinf->f_fstypename).contains("hfs", Qt::CaseInsensitive))) { | ||
535 | LOG_INFO() << "device name is" << mntinf->f_mntfromname; | ||
536 | return QString(mntinf->f_mntfromname); | ||
537 | } | ||
538 | mntinf++; | ||
539 | } | ||
540 | #endif | ||
541 | |||
542 | #if defined(Q_OS_WIN32) | ||
543 | DWORD written; | ||
544 | HANDLE h; | ||
545 | TCHAR uncpath[MAX_PATH]; | ||
546 | UCHAR buffer[0x400]; | ||
547 | PVOLUME_DISK_EXTENTS extents = (PVOLUME_DISK_EXTENTS)buffer; | ||
548 | |||
549 | _stprintf(uncpath, _TEXT("\\\\.\\%c:"), path.toLatin1().at(0)); | ||
550 | h = CreateFile(uncpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, | ||
551 | NULL, OPEN_EXISTING, 0, NULL); | ||
552 | if(h == INVALID_HANDLE_VALUE) { | ||
553 | //LOG_INFO() << "error getting extents for" << uncpath; | ||
554 | return ""; | ||
555 | } | ||
556 | // get the extents | ||
557 | if(DeviceIoControl(h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, | ||
558 | NULL, 0, extents, sizeof(buffer), &written, NULL)) { | ||
559 | if(extents->NumberOfDiskExtents == 1) { | ||
560 | CloseHandle(h); | ||
561 | LOG_INFO() << "device name is" << extents->Extents[0].DiskNumber; | ||
562 | return QString("%1").arg(extents->Extents[0].DiskNumber); | ||
563 | } | ||
564 | LOG_INFO() << "resolving device name: volume spans multiple disks!"; | ||
565 | } | ||
566 | CloseHandle(h); | ||
567 | #endif | ||
568 | return QString(""); | ||
569 | |||
570 | } | ||
571 | |||
572 | |||
573 | /** resolve device name to mount point / drive letter | ||
574 | * @param device device name / disk number | ||
575 | * @return mount point / drive letter | ||
576 | */ | ||
577 | QString Utils::resolveMountPoint(QString device) | ||
578 | { | ||
579 | LOG_INFO() << "resolving mountpoint:" << device; | ||
580 | |||
581 | #if defined(Q_OS_LINUX) | ||
582 | FILE *mn = setmntent("/etc/mtab", "r"); | ||
583 | if(!mn) | ||
584 | return QString(""); | ||
585 | |||
586 | struct mntent *ent; | ||
587 | while((ent = getmntent(mn))) { | ||
588 | // Check for valid filesystem. Allow hfs too, as an Ipod might be a | ||
589 | // MacPod. | ||
590 | if(QString(ent->mnt_fsname) == device) { | ||
591 | QString result; | ||
592 | if(QString(ent->mnt_type).contains("vfat", Qt::CaseInsensitive) | ||
593 | || QString(ent->mnt_type).contains("hfs", Qt::CaseInsensitive)) { | ||
594 | LOG_INFO() << "resolved mountpoint is:" << ent->mnt_dir; | ||
595 | result = QString(ent->mnt_dir); | ||
596 | } | ||
597 | else { | ||
598 | LOG_INFO() << "mountpoint is wrong filesystem!"; | ||
599 | } | ||
600 | endmntent(mn); | ||
601 | return result; | ||
602 | } | ||
603 | } | ||
604 | endmntent(mn); | ||
605 | |||
606 | #endif | ||
607 | |||
608 | #if defined(Q_OS_MACX) || defined(Q_OS_OPENBSD) | ||
609 | int num; | ||
610 | struct statfs *mntinf; | ||
611 | |||
612 | num = getmntinfo(&mntinf, MNT_WAIT); | ||
613 | while(num--) { | ||
614 | // Check for valid filesystem. Allow hfs too, as an Ipod might be a | ||
615 | // MacPod. | ||
616 | if(QString(mntinf->f_mntfromname) == device) { | ||
617 | if(QString(mntinf->f_fstypename).contains("msdos", Qt::CaseInsensitive) | ||
618 | || QString(mntinf->f_fstypename).contains("hfs", Qt::CaseInsensitive)) { | ||
619 | LOG_INFO() << "resolved mountpoint is:" << mntinf->f_mntonname; | ||
620 | return QString(mntinf->f_mntonname); | ||
621 | } | ||
622 | else { | ||
623 | LOG_INFO() << "mountpoint is wrong filesystem!"; | ||
624 | return QString(); | ||
625 | } | ||
626 | } | ||
627 | mntinf++; | ||
628 | } | ||
629 | #endif | ||
630 | |||
631 | #if defined(Q_OS_WIN32) | ||
632 | QString result; | ||
633 | unsigned int driveno = device.replace(QRegExp("^.*([0-9]+)"), "\\1").toInt(); | ||
634 | |||
635 | int letter; | ||
636 | for(letter = 'A'; letter <= 'Z'; letter++) { | ||
637 | if(resolveDevicename(QString(letter)).toUInt() == driveno) { | ||
638 | result = letter; | ||
639 | LOG_INFO() << "resolved mountpoint is:" << result; | ||
640 | break; | ||
641 | } | ||
642 | } | ||
643 | if(!result.isEmpty()) | ||
644 | return result + ":/"; | ||
645 | #endif | ||
646 | LOG_INFO() << "resolving mountpoint failed!"; | ||
647 | return QString(""); | ||
648 | } | ||
649 | |||
650 | |||
651 | QStringList Utils::mountpoints(enum MountpointsFilter type) | ||
652 | { | ||
653 | QStringList supported; | ||
654 | QStringList tempList; | ||
655 | #if defined(Q_OS_WIN32) | ||
656 | supported << "FAT32" << "FAT16" << "FAT12" << "FAT" << "HFS"; | ||
657 | QFileInfoList list = QDir::drives(); | ||
658 | for(int i=0; i<list.size();i++) | ||
659 | { | ||
660 | wchar_t t[32]; | ||
661 | memset(t, 0, 32); | ||
662 | if(GetVolumeInformationW((LPCWSTR)list.at(i).absolutePath().utf16(), | ||
663 | NULL, 0, NULL, NULL, NULL, t, 32) == 0) { | ||
664 | // on error empty retrieved type -- don't rely on | ||
665 | // GetVolumeInformation not changing it. | ||
666 | memset(t, 0, sizeof(t)); | ||
667 | } | ||
668 | |||
669 | QString fstype = QString::fromWCharArray(t); | ||
670 | if(type == MountpointsAll || supported.contains(fstype)) { | ||
671 | tempList << list.at(i).absolutePath(); | ||
672 | LOG_INFO() << "Added:" << list.at(i).absolutePath() | ||
673 | << "type" << fstype; | ||
674 | } | ||
675 | else { | ||
676 | LOG_INFO() << "Ignored:" << list.at(i).absolutePath() | ||
677 | << "type" << fstype; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | #elif defined(Q_OS_MACX) || defined(Q_OS_OPENBSD) | ||
682 | supported << "vfat" << "msdos" << "hfs"; | ||
683 | int num; | ||
684 | struct statfs *mntinf; | ||
685 | |||
686 | num = getmntinfo(&mntinf, MNT_WAIT); | ||
687 | while(num--) { | ||
688 | if(type == MountpointsAll || supported.contains(mntinf->f_fstypename)) { | ||
689 | tempList << QString(mntinf->f_mntonname); | ||
690 | LOG_INFO() << "Added:" << mntinf->f_mntonname | ||
691 | << "is" << mntinf->f_mntfromname << "type" << mntinf->f_fstypename; | ||
692 | } | ||
693 | else { | ||
694 | LOG_INFO() << "Ignored:" << mntinf->f_mntonname | ||
695 | << "is" << mntinf->f_mntfromname << "type" << mntinf->f_fstypename; | ||
696 | } | ||
697 | mntinf++; | ||
698 | } | ||
699 | #elif defined(Q_OS_LINUX) | ||
700 | supported << "vfat" << "msdos" << "hfsplus"; | ||
701 | FILE *mn = setmntent("/etc/mtab", "r"); | ||
702 | if(!mn) | ||
703 | return QStringList(""); | ||
704 | |||
705 | struct mntent *ent; | ||
706 | while((ent = getmntent(mn))) { | ||
707 | if(type == MountpointsAll || supported.contains(ent->mnt_type)) { | ||
708 | tempList << QString(ent->mnt_dir); | ||
709 | LOG_INFO() << "Added:" << ent->mnt_dir | ||
710 | << "is" << ent->mnt_fsname << "type" << ent->mnt_type; | ||
711 | } | ||
712 | else { | ||
713 | LOG_INFO() << "Ignored:" << ent->mnt_dir | ||
714 | << "is" << ent->mnt_fsname << "type" << ent->mnt_type; | ||
715 | } | ||
716 | } | ||
717 | endmntent(mn); | ||
718 | |||
719 | #else | ||
720 | #error Unknown Platform | ||
721 | #endif | ||
722 | return tempList; | ||
723 | } | ||
724 | |||
725 | |||
726 | /** Check if a process with a given name is running | ||
727 | * @param names list of names to filter on. All processes if empty list. | ||
728 | * @return list of processname, process ID pairs. | ||
729 | */ | ||
730 | QMap<QString, QList<int> > Utils::findRunningProcess(QStringList names) | ||
731 | { | ||
732 | QMap<QString, QList<int> > processlist; | ||
733 | QMap<QString, QList<int> > found; | ||
734 | #if defined(Q_OS_WIN32) | ||
735 | HANDLE hdl; | ||
736 | PROCESSENTRY32 entry; | ||
737 | bool result; | ||
738 | |||
739 | hdl = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | ||
740 | if(hdl == INVALID_HANDLE_VALUE) { | ||
741 | LOG_ERROR() << "CreateToolhelp32Snapshot failed."; | ||
742 | return found; | ||
743 | } | ||
744 | entry.dwSize = sizeof(PROCESSENTRY32); | ||
745 | entry.szExeFile[0] = '\0'; | ||
746 | if(!Process32First(hdl, &entry)) { | ||
747 | LOG_ERROR() << "Process32First failed."; | ||
748 | return found; | ||
749 | } | ||
750 | |||
751 | do { | ||
752 | int pid = entry.th32ProcessID; // FIXME: DWORD vs int! | ||
753 | QString name = QString::fromWCharArray(entry.szExeFile); | ||
754 | if(processlist.find(name) == processlist.end()) { | ||
755 | processlist.insert(name, QList<int>()); | ||
756 | } | ||
757 | processlist[name].append(pid); | ||
758 | entry.dwSize = sizeof(PROCESSENTRY32); | ||
759 | entry.szExeFile[0] = '\0'; | ||
760 | result = Process32Next(hdl, &entry); | ||
761 | } while(result); | ||
762 | CloseHandle(hdl); | ||
763 | #endif | ||
764 | #if defined(Q_OS_MACX) | ||
765 | ProcessSerialNumber psn = { 0, kNoProcess }; | ||
766 | OSErr err; | ||
767 | do { | ||
768 | pid_t pid; | ||
769 | err = GetNextProcess(&psn); | ||
770 | err = GetProcessPID(&psn, &pid); | ||
771 | if(err == noErr) { | ||
772 | char buf[32] = {0}; | ||
773 | ProcessInfoRec info; | ||
774 | memset(&info, 0, sizeof(ProcessInfoRec)); | ||
775 | info.processName = (unsigned char*)buf; | ||
776 | info.processInfoLength = sizeof(ProcessInfoRec); | ||
777 | err = GetProcessInformation(&psn, &info); | ||
778 | if(err == noErr) { | ||
779 | // some processes start with nonprintable characters. Skip those. | ||
780 | int i; | ||
781 | for(i = 0; i < 32; i++) { | ||
782 | if(isprint(buf[i])) break; | ||
783 | } | ||
784 | // avoid adding duplicates. | ||
785 | QString name = QString::fromUtf8(&buf[i]); | ||
786 | if(processlist.find(name) == processlist.end()) { | ||
787 | processlist.insert(name, QList<int>()); | ||
788 | } | ||
789 | processlist[name].append(pid); | ||
790 | } | ||
791 | } | ||
792 | } while(err == noErr); | ||
793 | #endif | ||
794 | #if defined(Q_OS_LINUX) | ||
795 | // not implemented for Linux! | ||
796 | #endif | ||
797 | // Filter for names (unless empty) | ||
798 | if(names.size() > 0) { | ||
799 | for(int i = 0; i < names.size(); ++i) { | ||
800 | QStringList k(processlist.keys()); | ||
801 | #if defined(Q_OS_WIN32) | ||
802 | // the process name might be truncated. Allow the extension to be partial. | ||
803 | int index = k.indexOf(QRegExp(names.at(i) + "(\\.(e(x(e?)?)?)?)?", | ||
804 | Qt::CaseInsensitive)); | ||
805 | #else | ||
806 | int index = k.indexOf(names.at(i)); | ||
807 | #endif | ||
808 | if(index != -1) { | ||
809 | found.insert(k[index], processlist[k[index]]); | ||
810 | } | ||
811 | } | ||
812 | } | ||
813 | else { | ||
814 | found = processlist; | ||
815 | } | ||
816 | LOG_INFO() << "Looking for processes" << names << "found" << found; | ||
817 | return found; | ||
818 | } | ||
819 | |||
820 | |||
821 | /** Suspends/resumes processes | ||
822 | * @param pidlist a list of PIDs to suspend/resume | ||
823 | * @param suspend processes are suspended if true, or resumed when false | ||
824 | * @return a list of PIDs successfully suspended/resumed | ||
825 | */ | ||
826 | QList<int> Utils::suspendProcess(QList<int> pidlist, bool suspend) | ||
827 | { | ||
828 | QList<int> result; | ||
829 | #if defined(Q_OS_WIN32) | ||
830 | // Enable debug privilege | ||
831 | HANDLE hToken = NULL; | ||
832 | LUID seDebugValue; | ||
833 | TOKEN_PRIVILEGES tNext, tPrev; | ||
834 | DWORD sPrev; | ||
835 | if(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &seDebugValue)) { | ||
836 | if(OpenProcessToken(GetCurrentProcess(), | ||
837 | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { | ||
838 | memset(&tNext, 0, sizeof(tNext)); | ||
839 | tNext.PrivilegeCount = 1; | ||
840 | tNext.Privileges[0].Luid = seDebugValue; | ||
841 | tNext.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | ||
842 | if(!AdjustTokenPrivileges(hToken, FALSE, &tNext, sizeof(tNext), | ||
843 | &tPrev, &sPrev) || GetLastError() != 0) { | ||
844 | CloseHandle(hToken); | ||
845 | hToken = NULL; | ||
846 | LOG_ERROR() << "AdjustTokenPrivileges(next) error" << GetLastError(); | ||
847 | } | ||
848 | } | ||
849 | else { | ||
850 | LOG_ERROR() << "OpenProcessToken error" << GetLastError(); | ||
851 | } | ||
852 | } | ||
853 | else { | ||
854 | LOG_ERROR() << "LookupPrivilegeValue error" << GetLastError(); | ||
855 | } | ||
856 | |||
857 | // Suspend/resume threads | ||
858 | for(int i = 0; i < pidlist.size(); i++) { | ||
859 | HANDLE hdl = INVALID_HANDLE_VALUE; | ||
860 | THREADENTRY32 entry; | ||
861 | int n_fails = 0; | ||
862 | |||
863 | hdl = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); | ||
864 | if(hdl == INVALID_HANDLE_VALUE) { | ||
865 | LOG_ERROR() << "CreateToolhelp32Snapshot error" << GetLastError(); | ||
866 | continue; | ||
867 | } | ||
868 | entry.dwSize = sizeof(THREADENTRY32); | ||
869 | if(!Thread32First(hdl, &entry)) { | ||
870 | LOG_ERROR() << "Process32First error" << GetLastError(); | ||
871 | CloseHandle(hdl); | ||
872 | continue; | ||
873 | } | ||
874 | |||
875 | do { | ||
876 | if(entry.th32OwnerProcessID != (DWORD)(pidlist[i])) | ||
877 | continue; | ||
878 | HANDLE thr = OpenThread(THREAD_SUSPEND_RESUME, | ||
879 | FALSE, entry.th32ThreadID); | ||
880 | if(!thr) { | ||
881 | LOG_ERROR() << "OpenThread" << entry.th32ThreadID | ||
882 | << "error" << GetLastError(); | ||
883 | n_fails++; | ||
884 | continue; | ||
885 | } | ||
886 | if(suspend) { | ||
887 | // Execution of the specified thread is suspended and | ||
888 | // the thread's suspend count is incremented. | ||
889 | if(SuspendThread(thr) == (DWORD)(-1)) { | ||
890 | LOG_ERROR() << "SuspendThread" << entry.th32ThreadID | ||
891 | << "error" << GetLastError(); | ||
892 | n_fails++; | ||
893 | } | ||
894 | } | ||
895 | else { | ||
896 | // Decrements a thread's suspend count. When the | ||
897 | // suspend count is decremented to zero, the | ||
898 | // execution of the thread is resumed. | ||
899 | if(ResumeThread(thr) == (DWORD)(-1)) { | ||
900 | LOG_ERROR() << "ResumeThread" << entry.th32ThreadID | ||
901 | << "error" << GetLastError(); | ||
902 | n_fails++; | ||
903 | } | ||
904 | } | ||
905 | CloseHandle(thr); | ||
906 | } while(Thread32Next(hdl, &entry)); | ||
907 | if (!n_fails) | ||
908 | result.append(pidlist[i]); | ||
909 | CloseHandle(hdl); | ||
910 | } | ||
911 | |||
912 | // Restore previous debug privilege | ||
913 | if (hToken) { | ||
914 | if(!AdjustTokenPrivileges(hToken, FALSE, | ||
915 | &tPrev, sPrev, NULL, NULL) || GetLastError() != 0) { | ||
916 | LOG_ERROR() << "AdjustTokenPrivileges(prev) error" << GetLastError(); | ||
917 | } | ||
918 | CloseHandle(hToken); | ||
919 | } | ||
920 | #endif | ||
921 | #if defined(Q_OS_MACX) | ||
922 | int signal = suspend ? SIGSTOP : SIGCONT; | ||
923 | for(int i = 0; i < pidlist.size(); i++) { | ||
924 | pid_t pid = pidlist[i]; | ||
925 | if(kill(pid, signal) != 0) { | ||
926 | LOG_ERROR() << "kill signal" << signal | ||
927 | << "for PID" << pid << "error:" << errno; | ||
928 | } | ||
929 | else { | ||
930 | result.append(pidlist[i]); | ||
931 | } | ||
932 | } | ||
933 | #endif | ||
934 | #if defined(Q_OS_LINUX) | ||
935 | // not implemented for Linux! | ||
936 | #endif | ||
937 | LOG_INFO() << (suspend ? "Suspending" : "Resuming") | ||
938 | << "PIDs" << pidlist << "result" << result; | ||
939 | return result; | ||
940 | } | ||
941 | |||
942 | |||
943 | /** Eject device from PC. | ||
944 | * Request the OS to eject the player. | ||
945 | * @param device mountpoint of the device | ||
946 | * @return true on success, fals otherwise. | ||
947 | */ | ||
948 | bool Utils::ejectDevice(QString device) | ||
949 | { | ||
950 | #if defined(Q_OS_WIN32) | ||
951 | /* See http://support.microsoft.com/kb/165721 on the procedure to eject a | ||
952 | * device. */ | ||
953 | bool success = false; | ||
954 | int i; | ||
955 | HANDLE hdl; | ||
956 | DWORD bytesReturned; | ||
957 | TCHAR volume[8]; | ||
958 | |||
959 | /* CreateFile */ | ||
960 | _stprintf(volume, _TEXT("\\\\.\\%c:"), device.toLatin1().at(0)); | ||
961 | hdl = CreateFile(volume, GENERIC_READ | GENERIC_WRITE, | ||
962 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | ||
963 | OPEN_EXISTING, 0, NULL); | ||
964 | if(hdl == INVALID_HANDLE_VALUE) | ||
965 | return false; | ||
966 | |||
967 | /* lock volume to make sure no other application is accessing the volume. | ||
968 | * Try up to 10 times. */ | ||
969 | for(i = 0; i < 10; i++) { | ||
970 | if(DeviceIoControl(hdl, FSCTL_LOCK_VOLUME, | ||
971 | NULL, 0, NULL, 0, &bytesReturned, NULL)) | ||
972 | break; | ||
973 | /* short break before retry */ | ||
974 | Sleep(100); | ||
975 | } | ||
976 | if(i < 10) { | ||
977 | /* successfully locked, now dismount */ | ||
978 | if(DeviceIoControl(hdl, FSCTL_DISMOUNT_VOLUME, | ||
979 | NULL, 0, NULL, 0, &bytesReturned, NULL)) { | ||
980 | /* make sure media can be removed. */ | ||
981 | PREVENT_MEDIA_REMOVAL pmr; | ||
982 | pmr.PreventMediaRemoval = false; | ||
983 | if(DeviceIoControl(hdl, IOCTL_STORAGE_MEDIA_REMOVAL, | ||
984 | &pmr, sizeof(PREVENT_MEDIA_REMOVAL), | ||
985 | NULL, 0, &bytesReturned, NULL)) { | ||
986 | /* eject the media */ | ||
987 | if(DeviceIoControl(hdl, IOCTL_STORAGE_EJECT_MEDIA, | ||
988 | NULL, 0, NULL, 0, &bytesReturned, NULL)) | ||
989 | success = true; | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | /* close handle */ | ||
994 | CloseHandle(hdl); | ||
995 | return success; | ||
996 | |||
997 | #endif | ||
998 | #if defined(Q_OS_MACX) | ||
999 | // FIXME: FSUnmountVolumeSync is deprecated starting with 10.8. | ||
1000 | // Use DADiskUnmount / DiskArbitration framework eventually. | ||
1001 | // BSD label does not include folder. | ||
1002 | QString bsd = Utils::resolveDevicename(device).remove("/dev/"); | ||
1003 | OSStatus result; | ||
1004 | ItemCount index = 1; | ||
1005 | bool found = false; | ||
1006 | |||
1007 | do { | ||
1008 | FSVolumeRefNum volrefnum; | ||
1009 | |||
1010 | result = FSGetVolumeInfo(kFSInvalidVolumeRefNum, index, &volrefnum, | ||
1011 | kFSVolInfoFSInfo, NULL, NULL, NULL); | ||
1012 | if(result == noErr) { | ||
1013 | GetVolParmsInfoBuffer volparms; | ||
1014 | /* See above -- PBHGetVolParmsSync() is not available for 64bit, | ||
1015 | * and FSGetVolumeParms() on 10.5+ only. */ | ||
1016 | #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 | ||
1017 | if(FSGetVolumeParms(volrefnum, &volparms, sizeof(volparms)) == noErr) | ||
1018 | #else | ||
1019 | HParamBlockRec hpb; | ||
1020 | hpb.ioParam.ioNamePtr = NULL; | ||
1021 | hpb.ioParam.ioVRefNum = volrefnum; | ||
1022 | hpb.ioParam.ioBuffer = (Ptr)&volparms; | ||
1023 | hpb.ioParam.ioReqCount = sizeof(volparms); | ||
1024 | if(PBHGetVolParmsSync(&hpb) == noErr) | ||
1025 | #endif | ||
1026 | { | ||
1027 | if(volparms.vMServerAdr == 0) { | ||
1028 | if(bsd == (char*)volparms.vMDeviceID) { | ||
1029 | pid_t dissenter; | ||
1030 | result = FSUnmountVolumeSync(volrefnum, 0, &dissenter); | ||
1031 | found = true; | ||
1032 | break; | ||
1033 | } | ||
1034 | } | ||
1035 | } | ||
1036 | } | ||
1037 | index++; | ||
1038 | } while(result == noErr); | ||
1039 | if(result == noErr && found) | ||
1040 | return true; | ||
1041 | |||
1042 | #endif | ||
1043 | #if defined(Q_OS_LINUX) | ||
1044 | (void)device; | ||
1045 | #endif | ||
1046 | return false; | ||
1047 | } | ||
1048 | |||
1049 | |||
1050 | qint64 Utils::recursiveFolderSize(QString path) | ||
1051 | { | ||
1052 | qint64 size = 0; | ||
1053 | QList<QFileInfo> items = QDir(path).entryInfoList(QDir::Files | QDir::NoDotAndDotDot); | ||
1054 | for (auto item: items) { | ||
1055 | size += item.size(); | ||
1056 | } | ||
1057 | QList<QString> folders = QDir(path).entryList(QDir::Dirs | QDir::NoDotAndDotDot); | ||
1058 | for (auto folder: folders) { | ||
1059 | size += recursiveFolderSize(path + "/" + folder); | ||
1060 | } | ||
1061 | return size; | ||
1062 | } | ||