FileSystem.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Context.h"
  25. #include "File.h"
  26. #include "FileSystem.h"
  27. #include "Log.h"
  28. #include "SharedArrayPtr.h"
  29. #include <cstdio>
  30. #ifdef _WIN32
  31. #include <Windows.h>
  32. #include <Shellapi.h>
  33. #include <direct.h>
  34. #include <process.h>
  35. // Enable SHGetSpecialFolderPath on MinGW
  36. #ifndef _MSC_VER
  37. #define _WIN32_IE 0x0400
  38. #endif
  39. #include <Shlobj.h>
  40. #else
  41. #include <dirent.h>
  42. #include <errno.h>
  43. #include <unistd.h>
  44. #include <sys/stat.h>
  45. #define MAX_PATH 256
  46. #endif
  47. #ifdef __APPLE__
  48. #include <mach-o/dyld.h>
  49. #endif
  50. #include "DebugNew.h"
  51. OBJECTTYPESTATIC(FileSystem);
  52. FileSystem::FileSystem(Context* context) :
  53. Object(context)
  54. {
  55. }
  56. FileSystem::~FileSystem()
  57. {
  58. }
  59. bool FileSystem::SetCurrentDir(const String& pathName)
  60. {
  61. if (!CheckAccess(pathName))
  62. {
  63. LOGERROR("Access denied to " + pathName);
  64. return false;
  65. }
  66. #ifdef _WIN32
  67. if (SetCurrentDirectory(GetNativePath(pathName).CString()) == FALSE)
  68. {
  69. LOGERROR("Failed to change directory to " + pathName);
  70. return false;
  71. }
  72. #else
  73. if (chdir(GetNativePath(pathName).CString()) != 0)
  74. {
  75. LOGERROR("Failed to change directory to " + pathName);
  76. return false;
  77. }
  78. #endif
  79. return true;
  80. }
  81. bool FileSystem::CreateDir(const String& pathName)
  82. {
  83. if (!CheckAccess(pathName))
  84. {
  85. LOGERROR("Access denied to " + pathName);
  86. return false;
  87. }
  88. #ifdef _WIN32
  89. bool success = (CreateDirectory(GetNativePath(RemoveTrailingSlash(pathName)).CString(), 0) == TRUE) ||
  90. (GetLastError() == ERROR_ALREADY_EXISTS);
  91. #else
  92. bool success = (mkdir(GetNativePath(RemoveTrailingSlash(pathName)).CString(), S_IRWXU) == 0) || (errno == EEXIST);
  93. #endif
  94. if (success)
  95. LOGDEBUG("Created directory " + pathName);
  96. else
  97. LOGERROR("Failed to create directory " + pathName);
  98. return success;
  99. }
  100. int FileSystem::SystemCommand(const String& commandLine)
  101. {
  102. if (allowedPaths_.Empty())
  103. return system(commandLine.CString());
  104. else
  105. {
  106. LOGERROR("Executing an external command is not allowed");
  107. return -1;
  108. }
  109. }
  110. int FileSystem::SystemRun(const String& fileName, const Vector<String>& arguments)
  111. {
  112. #ifdef _WIN32
  113. if (allowedPaths_.Empty())
  114. {
  115. String fixedFileName = GetNativePath(fileName);
  116. PODVector<const char*> argPtrs;
  117. argPtrs.Push(fixedFileName.CString());
  118. for (unsigned i = 0; i < arguments.Size(); ++i)
  119. argPtrs.Push(arguments[i].CString());
  120. argPtrs.Push(0);
  121. return _spawnv(_P_WAIT, fixedFileName.CString(), &argPtrs[0]);
  122. }
  123. else
  124. {
  125. LOGERROR("Executing an external command is not allowed");
  126. return -1;
  127. }
  128. #else
  129. /// \todo Implement on Unix-like systems
  130. LOGERROR("SystemRun not implemented");
  131. return false;
  132. #endif
  133. }
  134. bool FileSystem::SystemOpen(const String& fileName, const String& mode)
  135. {
  136. #ifdef _WIN32
  137. if (allowedPaths_.Empty())
  138. {
  139. if ((!FileExists(fileName)) && (!DirExists(fileName)))
  140. {
  141. LOGERROR("File or directory " + fileName + " not found");
  142. return false;
  143. }
  144. bool success = (int)ShellExecute(0, !mode.Empty() ? (char*)mode.CString() : 0,
  145. (char*)GetNativePath(fileName).CString(), 0, 0, SW_SHOW) > 32;
  146. if (!success)
  147. LOGERROR("Failed to open " + fileName + " externally");
  148. return success;
  149. }
  150. else
  151. {
  152. LOGERROR("Opening a file externally is not allowed");
  153. return false;
  154. }
  155. #else
  156. /// \todo Implement on Unix-like systems
  157. LOGERROR("SystemOpen not implemented");
  158. return false;
  159. #endif
  160. }
  161. bool FileSystem::Copy(const String& srcFileName, const String& destFileName)
  162. {
  163. if (!CheckAccess(GetPath(srcFileName)))
  164. {
  165. LOGERROR("Access denied to " + srcFileName);
  166. return false;
  167. }
  168. if (!CheckAccess(GetPath(destFileName)))
  169. {
  170. LOGERROR("Access denied to " + destFileName);
  171. return false;
  172. }
  173. SharedPtr<File> srcFile(new File(context_, srcFileName, FILE_READ));
  174. SharedPtr<File> destFile(new File(context_, destFileName, FILE_WRITE));
  175. if ((!srcFile->IsOpen()) || (!destFile->IsOpen()))
  176. return false;
  177. unsigned fileSize = srcFile->GetSize();
  178. SharedArrayPtr<unsigned char> buffer(new unsigned char[fileSize]);
  179. unsigned bytesRead = srcFile->Read(buffer.GetPtr(), fileSize);
  180. unsigned bytesWritten = destFile->Write(buffer.GetPtr(), fileSize);
  181. return (bytesRead == fileSize) && (bytesWritten == fileSize);
  182. }
  183. bool FileSystem::Rename(const String& srcFileName, const String& destFileName)
  184. {
  185. if (!CheckAccess(GetPath(srcFileName)))
  186. {
  187. LOGERROR("Access denied to " + srcFileName);
  188. return false;
  189. }
  190. if (!CheckAccess(GetPath(destFileName)))
  191. {
  192. LOGERROR("Access denied to " + destFileName);
  193. return false;
  194. }
  195. return rename(GetNativePath(srcFileName).CString(), GetNativePath(destFileName).CString()) == 0;
  196. }
  197. bool FileSystem::Delete(const String& fileName)
  198. {
  199. if (!CheckAccess(GetPath(fileName)))
  200. {
  201. LOGERROR("Access denied to " + fileName);
  202. return false;
  203. }
  204. return remove(GetNativePath(fileName).CString()) == 0;
  205. }
  206. String FileSystem::GetCurrentDir()
  207. {
  208. char path[MAX_PATH];
  209. path[0] = 0;
  210. #ifdef _WIN32
  211. GetCurrentDirectory(MAX_PATH, path);
  212. #else
  213. getcwd(path, MAX_PATH);
  214. #endif
  215. return AddTrailingSlash(String(path));
  216. }
  217. bool FileSystem::CheckAccess(const String& pathName)
  218. {
  219. String fixedPath = AddTrailingSlash(pathName);
  220. // If no allowed directories defined, succeed always
  221. if (allowedPaths_.Empty())
  222. return true;
  223. // If there is any attempt to go to a parent directory, disallow
  224. if (fixedPath.Find("..") != String::NPOS)
  225. return false;
  226. // Check if the path is a partial match of any of the allowed directories
  227. for (Set<String>::ConstIterator i = allowedPaths_.Begin(); i != allowedPaths_.End(); ++i)
  228. {
  229. if (fixedPath.Find(*i) == 0)
  230. return true;
  231. }
  232. // Not found, so disallow
  233. return false;
  234. }
  235. bool FileSystem::FileExists(const String& fileName)
  236. {
  237. if (!CheckAccess(GetPath(fileName)))
  238. return false;
  239. String fixedName = GetNativePath(RemoveTrailingSlash(fileName));
  240. #ifdef _WIN32
  241. DWORD attributes = GetFileAttributes(fixedName.CString());
  242. if ((attributes == INVALID_FILE_ATTRIBUTES) || (attributes & FILE_ATTRIBUTE_DIRECTORY))
  243. return false;
  244. #else
  245. struct stat st;
  246. if ((stat(fixedName.CString(), &st)) || (st.st_mode & S_IFDIR))
  247. return false;
  248. #endif
  249. return true;
  250. }
  251. bool FileSystem::DirExists(const String& pathName)
  252. {
  253. if (!CheckAccess(pathName))
  254. return false;
  255. String fixedName = GetNativePath(RemoveTrailingSlash(pathName));
  256. #ifdef _WIN32
  257. DWORD attributes = GetFileAttributes(fixedName.CString());
  258. if ((attributes == INVALID_FILE_ATTRIBUTES) || (!(attributes & FILE_ATTRIBUTE_DIRECTORY)))
  259. return false;
  260. #else
  261. struct stat st;
  262. if ((stat(fixedName.CString(), &st)) || (!(st.st_mode & S_IFDIR)))
  263. return false;
  264. #endif
  265. return true;
  266. }
  267. void FileSystem::ScanDir(Vector<String>& result, const String& pathName, const String& filter, unsigned flags, bool recursive)
  268. {
  269. result.Clear();
  270. if (CheckAccess(pathName))
  271. {
  272. String initialPath = AddTrailingSlash(pathName);
  273. ScanDirInternal(result, initialPath, initialPath, filter, flags, recursive);
  274. }
  275. }
  276. String FileSystem::GetProgramDir()
  277. {
  278. char exeName[MAX_PATH];
  279. exeName[0] = 0;
  280. #ifdef _WIN32
  281. GetModuleFileName(0, exeName, MAX_PATH);
  282. #endif
  283. #ifdef __APPLE__
  284. unsigned size = MAX_PATH;
  285. _NSGetExecutablePath(exeName, &size);
  286. #endif
  287. #ifdef __linux__
  288. unsigned pid = getpid();
  289. String link = "/proc/" + String(pid) + "/exe";
  290. readlink(link.CString(), exeName, MAX_PATH);
  291. #endif
  292. return GetPath(String(exeName));
  293. }
  294. String FileSystem::GetUserDocumentsDir()
  295. {
  296. char pathName[MAX_PATH];
  297. pathName[0] = 0;
  298. #ifdef _WIN32
  299. SHGetSpecialFolderPath(0, pathName, CSIDL_PERSONAL, 0);
  300. #else
  301. strcpy(pathName, getenv("HOME"));
  302. #endif
  303. return AddTrailingSlash(String(pathName));
  304. }
  305. void FileSystem::RegisterPath(const String& pathName)
  306. {
  307. if (pathName.Empty())
  308. return;
  309. allowedPaths_.Insert(AddTrailingSlash(pathName));
  310. }
  311. void FileSystem::ScanDirInternal(Vector<String>& result, String path, const String& startPath,
  312. const String& filter, unsigned flags, bool recursive)
  313. {
  314. path = AddTrailingSlash(path);
  315. String pathAndFilter = GetNativePath(path + filter);
  316. String deltaPath;
  317. if (path.Length() > startPath.Length())
  318. deltaPath = path.Substring(startPath.Length());
  319. #ifdef _WIN32
  320. WIN32_FIND_DATA info;
  321. HANDLE handle = FindFirstFile(pathAndFilter.CString(), &info);
  322. if (handle != INVALID_HANDLE_VALUE)
  323. {
  324. do
  325. {
  326. String fileName((const char*)&info.cFileName[0]);
  327. if (!fileName.Empty())
  328. {
  329. if ((info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) && (!(flags & SCAN_HIDDEN)))
  330. continue;
  331. if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  332. {
  333. if (flags & SCAN_DIRS)
  334. result.Push(deltaPath + fileName);
  335. if ((recursive) && (fileName != ".") && (fileName != ".."))
  336. ScanDirInternal(result, path + fileName, startPath, filter, flags, recursive);
  337. }
  338. else if (flags & SCAN_FILES)
  339. result.Push(deltaPath + fileName);
  340. }
  341. }
  342. while (FindNextFile(handle, &info));
  343. FindClose(handle);
  344. }
  345. #else
  346. DIR *dir;
  347. struct dirent *de;
  348. struct stat st;
  349. dir = opendir(GetNativePath(path).CString());
  350. if (dir)
  351. {
  352. while (de = readdir(dir))
  353. {
  354. String fileName(de->d_name);
  355. String pathAndName = path + fileName;
  356. if (!stat(pathAndName.CString(), &st))
  357. {
  358. if (st.st_mode & S_IFDIR)
  359. {
  360. if (flags & SCAN_DIRS)
  361. result.Push(deltaPath + fileName);
  362. if ((recursive) && (fileName != ".") && (fileName != ".."))
  363. ScanDirInternal(result, path + fileName, startPath, filter, flags, recursive);
  364. }
  365. else if (flags & SCAN_FILES)
  366. result.Push(deltaPath + fileName);
  367. }
  368. }
  369. closedir(dir);
  370. }
  371. #endif
  372. }
  373. void SplitPath(const String& fullPath, String& pathName, String& fileName, String& extension)
  374. {
  375. String fullPathCopy = GetInternalPath(fullPath);
  376. unsigned extPos = fullPathCopy.FindLast('.');
  377. if (extPos != String::NPOS)
  378. {
  379. extension = fullPathCopy.Substring(extPos).ToLower();
  380. fullPathCopy = fullPathCopy.Substring(0, extPos);
  381. }
  382. else
  383. extension.Clear();
  384. unsigned pathPos = fullPathCopy.FindLast('/');
  385. if (pathPos != String::NPOS)
  386. {
  387. fileName = fullPathCopy.Substring(pathPos + 1);
  388. pathName = fullPathCopy.Substring(0, pathPos + 1);
  389. }
  390. else
  391. {
  392. fileName = fullPathCopy;
  393. pathName.Clear();
  394. }
  395. }
  396. String GetPath(const String& fullPath)
  397. {
  398. String path, file, extension;
  399. SplitPath(fullPath, path, file, extension);
  400. return path;
  401. }
  402. String GetFileName(const String& fullPath)
  403. {
  404. String path, file, extension;
  405. SplitPath(fullPath, path, file, extension);
  406. return file;
  407. }
  408. String GetExtension(const String& fullPath)
  409. {
  410. String path, file, extension;
  411. SplitPath(fullPath, path, file, extension);
  412. return extension;
  413. }
  414. String GetFileNameAndExtension(const String& fileName)
  415. {
  416. String path, file, extension;
  417. SplitPath(fileName, path, file, extension);
  418. return file + extension;
  419. }
  420. String AddTrailingSlash(const String& pathName)
  421. {
  422. String ret = pathName;
  423. ret.Replace('\\', '/');
  424. if ((!ret.Empty()) && (ret.Back() != '/'))
  425. ret += '/';
  426. return ret;
  427. }
  428. String RemoveTrailingSlash(const String& pathName)
  429. {
  430. String ret = pathName;
  431. ret.Replace('\\', '/');
  432. if ((!ret.Empty()) && (ret.Back() == '/'))
  433. ret.Resize(ret.Length() - 1);
  434. return ret;
  435. }
  436. String GetParentPath(const String& path)
  437. {
  438. unsigned pos = RemoveTrailingSlash(path).FindLast('/');
  439. if (pos != String::NPOS)
  440. return path.Substring(0, pos + 1);
  441. else
  442. return String();
  443. }
  444. String GetInternalPath(const String& pathName)
  445. {
  446. String ret = pathName;
  447. ret.Replace('\\', '/');
  448. return ret;
  449. }
  450. String GetNativePath(const String& pathName)
  451. {
  452. #ifdef _WIN32
  453. String ret = pathName;
  454. ret.Replace('/', '\\');
  455. return ret;
  456. #else
  457. return pathName;
  458. #endif
  459. }