FileSystem.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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. #include "DebugNew.h"
  48. OBJECTTYPESTATIC(FileSystem);
  49. FileSystem::FileSystem(Context* context) :
  50. Object(context)
  51. {
  52. }
  53. FileSystem::~FileSystem()
  54. {
  55. }
  56. bool FileSystem::SetCurrentDir(const String& pathName)
  57. {
  58. if (!CheckAccess(pathName))
  59. {
  60. LOGERROR("Access denied to " + pathName);
  61. return false;
  62. }
  63. #ifdef WIN32
  64. if (SetCurrentDirectory(GetNativePath(pathName).CString()) == FALSE)
  65. {
  66. LOGERROR("Failed to change directory to " + pathName);
  67. return false;
  68. }
  69. #else
  70. if (chdir(GetNativePath(pathName).CString()) != 0)
  71. {
  72. LOGERROR("Failed to change directory to " + pathName);
  73. return false;
  74. }
  75. #endif
  76. return true;
  77. }
  78. bool FileSystem::CreateDir(const String& pathName)
  79. {
  80. if (!CheckAccess(pathName))
  81. {
  82. LOGERROR("Access denied to " + pathName);
  83. return false;
  84. }
  85. #ifdef WIN32
  86. bool success = (CreateDirectory(GetNativePath(RemoveTrailingSlash(pathName)).CString(), 0) == TRUE) ||
  87. (GetLastError() == ERROR_ALREADY_EXISTS);
  88. #else
  89. bool success = (mkdir(GetNativePath(RemoveTrailingSlash(pathName)).CString(), S_IRWXU) == 0) || (errno == EEXIST);
  90. #endif
  91. if (success)
  92. LOGDEBUG("Created directory " + pathName);
  93. else
  94. LOGERROR("Failed to create directory " + pathName);
  95. return success;
  96. }
  97. int FileSystem::SystemCommand(const String& commandLine)
  98. {
  99. if (allowedPaths_.Empty())
  100. return system(commandLine.CString());
  101. else
  102. {
  103. LOGERROR("Executing an external command is not allowed");
  104. return -1;
  105. }
  106. }
  107. int FileSystem::SystemRun(const String& fileName, const Vector<String>& arguments)
  108. {
  109. #ifdef WIN32
  110. if (allowedPaths_.Empty())
  111. {
  112. String fixedFileName = GetNativePath(fileName);
  113. PODVector<const char*> argPtrs;
  114. argPtrs.Push(fixedFileName.CString());
  115. for (unsigned i = 0; i < arguments.Size(); ++i)
  116. argPtrs.Push(arguments[i].CString());
  117. argPtrs.Push(0);
  118. return _spawnv(_P_WAIT, fixedFileName.CString(), &argPtrs[0]);
  119. }
  120. else
  121. {
  122. LOGERROR("Executing an external command is not allowed");
  123. return -1;
  124. }
  125. #else
  126. /// \todo Implement on Unix-like systems
  127. LOGERROR("SystemRun not implemented");
  128. return false;
  129. #endif
  130. }
  131. bool FileSystem::SystemOpen(const String& fileName, const String& mode)
  132. {
  133. #ifdef WIN32
  134. if (allowedPaths_.Empty())
  135. {
  136. if ((!FileExists(fileName)) && (!DirExists(fileName)))
  137. {
  138. LOGERROR("File or directory " + fileName + " not found");
  139. return false;
  140. }
  141. bool success = (int)ShellExecute(0, !mode.Empty() ? (char*)mode.CString() : 0,
  142. (char*)GetNativePath(fileName).CString(), 0, 0, SW_SHOW) > 32;
  143. if (!success)
  144. LOGERROR("Failed to open " + fileName + " externally");
  145. return success;
  146. }
  147. else
  148. {
  149. LOGERROR("Opening a file externally is not allowed");
  150. return false;
  151. }
  152. #else
  153. /// \todo Implement on Unix-like systems
  154. LOGERROR("SystemOpen not implemented");
  155. return false;
  156. #endif
  157. }
  158. bool FileSystem::Copy(const String& srcFileName, const String& destFileName)
  159. {
  160. if (!CheckAccess(GetPath(srcFileName)))
  161. {
  162. LOGERROR("Access denied to " + srcFileName);
  163. return false;
  164. }
  165. if (!CheckAccess(GetPath(destFileName)))
  166. {
  167. LOGERROR("Access denied to " + destFileName);
  168. return false;
  169. }
  170. SharedPtr<File> srcFile(new File(context_, srcFileName, FILE_READ));
  171. SharedPtr<File> destFile(new File(context_, destFileName, FILE_WRITE));
  172. if ((!srcFile->IsOpen()) || (!destFile->IsOpen()))
  173. return false;
  174. unsigned fileSize = srcFile->GetSize();
  175. SharedArrayPtr<unsigned char> buffer(new unsigned char[fileSize]);
  176. unsigned bytesRead = srcFile->Read(buffer.GetPtr(), fileSize);
  177. unsigned bytesWritten = destFile->Write(buffer.GetPtr(), fileSize);
  178. return (bytesRead == fileSize) && (bytesWritten == fileSize);
  179. }
  180. bool FileSystem::Rename(const String& srcFileName, const String& destFileName)
  181. {
  182. if (!CheckAccess(GetPath(srcFileName)))
  183. {
  184. LOGERROR("Access denied to " + srcFileName);
  185. return false;
  186. }
  187. if (!CheckAccess(GetPath(destFileName)))
  188. {
  189. LOGERROR("Access denied to " + destFileName);
  190. return false;
  191. }
  192. return rename(GetNativePath(srcFileName).CString(), GetNativePath(destFileName).CString()) == 0;
  193. }
  194. bool FileSystem::Delete(const String& fileName)
  195. {
  196. if (!CheckAccess(GetPath(fileName)))
  197. {
  198. LOGERROR("Access denied to " + fileName);
  199. return false;
  200. }
  201. return remove(GetNativePath(fileName).CString()) == 0;
  202. }
  203. String FileSystem::GetCurrentDir()
  204. {
  205. char path[MAX_PATH];
  206. path[0] = 0;
  207. #ifdef WIN32
  208. GetCurrentDirectory(MAX_PATH, path);
  209. #else
  210. getcwd(path, MAX_PATH);
  211. #endif
  212. return AddTrailingSlash(String(path));
  213. }
  214. bool FileSystem::CheckAccess(const String& pathName)
  215. {
  216. String fixedPath = AddTrailingSlash(pathName);
  217. // If no allowed directories defined, succeed always
  218. if (allowedPaths_.Empty())
  219. return true;
  220. // If there is any attempt to go to a parent directory, disallow
  221. if (fixedPath.Find("..") != String::NPOS)
  222. return false;
  223. // Check if the path is a partial match of any of the allowed directories
  224. for (Set<String>::ConstIterator i = allowedPaths_.Begin(); i != allowedPaths_.End(); ++i)
  225. {
  226. if (fixedPath.Find(*i) == 0)
  227. return true;
  228. }
  229. // Not found, so disallow
  230. return false;
  231. }
  232. bool FileSystem::FileExists(const String& fileName)
  233. {
  234. if (!CheckAccess(GetPath(fileName)))
  235. return false;
  236. String fixedName = GetNativePath(RemoveTrailingSlash(fileName));
  237. #ifdef WIN32
  238. DWORD attributes = GetFileAttributes(fixedName.CString());
  239. if ((attributes == INVALID_FILE_ATTRIBUTES) || (attributes & FILE_ATTRIBUTE_DIRECTORY))
  240. return false;
  241. #else
  242. struct stat st;
  243. if ((stat(fixedName.CString(), &st)) || (st.st_mode & S_IFDIR))
  244. return false;
  245. #endif
  246. return true;
  247. }
  248. bool FileSystem::DirExists(const String& pathName)
  249. {
  250. if (!CheckAccess(pathName))
  251. return false;
  252. String fixedName = GetNativePath(RemoveTrailingSlash(pathName));
  253. #ifdef WIN32
  254. DWORD attributes = GetFileAttributes(fixedName.CString());
  255. if ((attributes == INVALID_FILE_ATTRIBUTES) || (!(attributes & FILE_ATTRIBUTE_DIRECTORY)))
  256. return false;
  257. #else
  258. struct stat st;
  259. if ((stat(fixedName.CString(), &st)) || (!(st.st_mode & S_IFDIR)))
  260. return false;
  261. #endif
  262. return true;
  263. }
  264. void FileSystem::ScanDir(Vector<String>& result, const String& pathName, const String& filter, unsigned flags, bool recursive)
  265. {
  266. result.Clear();
  267. if (CheckAccess(pathName))
  268. {
  269. String initialPath = AddTrailingSlash(pathName);
  270. ScanDirInternal(result, initialPath, initialPath, filter, flags, recursive);
  271. }
  272. }
  273. String FileSystem::GetProgramDir()
  274. {
  275. char exeName[MAX_PATH];
  276. exeName[0] = 0;
  277. #ifdef WIN32
  278. GetModuleFileName(0, exeName, MAX_PATH);
  279. #else
  280. unsigned pid = getpid();
  281. String link = "/proc/" + String(pid) + "/exe";
  282. readlink(link.CString(), exeName, MAX_PATH);
  283. #endif
  284. return GetPath(String(exeName));
  285. }
  286. String FileSystem::GetUserDocumentsDir()
  287. {
  288. char pathName[MAX_PATH];
  289. pathName[0] = 0;
  290. #ifdef WIN32
  291. SHGetSpecialFolderPath(0, pathName, CSIDL_PERSONAL, 0);
  292. #else
  293. strcpy(pathName, getenv("HOME"));
  294. #endif
  295. return AddTrailingSlash(String(pathName));
  296. }
  297. void FileSystem::RegisterPath(const String& pathName)
  298. {
  299. if (pathName.Empty())
  300. return;
  301. allowedPaths_.Insert(AddTrailingSlash(pathName));
  302. }
  303. void FileSystem::ScanDirInternal(Vector<String>& result, String path, const String& startPath,
  304. const String& filter, unsigned flags, bool recursive)
  305. {
  306. path = AddTrailingSlash(path);
  307. String pathAndFilter = GetNativePath(path + filter);
  308. String deltaPath;
  309. if (path.Length() > startPath.Length())
  310. deltaPath = path.Substring(startPath.Length());
  311. #ifdef WIN32
  312. WIN32_FIND_DATA info;
  313. HANDLE handle = FindFirstFile(pathAndFilter.CString(), &info);
  314. if (handle != INVALID_HANDLE_VALUE)
  315. {
  316. do
  317. {
  318. String fileName((const char*)&info.cFileName[0]);
  319. if (!fileName.Empty())
  320. {
  321. if ((info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) && (!(flags & SCAN_HIDDEN)))
  322. continue;
  323. if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  324. {
  325. if (flags & SCAN_DIRS)
  326. result.Push(deltaPath + fileName);
  327. if ((recursive) && (fileName != ".") && (fileName != ".."))
  328. ScanDirInternal(result, path + fileName, startPath, filter, flags, recursive);
  329. }
  330. else if (flags & SCAN_FILES)
  331. result.Push(deltaPath + fileName);
  332. }
  333. }
  334. while (FindNextFile(handle, &info));
  335. FindClose(handle);
  336. }
  337. #else
  338. DIR *dir;
  339. struct dirent *de;
  340. struct stat st;
  341. dir = opendir(GetNativePath(path).CString());
  342. if (dir)
  343. {
  344. while (de = readdir(dir))
  345. {
  346. String fileName(de->d_name);
  347. String pathAndName = path + fileName;
  348. if (!stat(pathAndName.CString(), &st))
  349. {
  350. if (st.st_mode & S_IFDIR)
  351. {
  352. if (flags & SCAN_DIRS)
  353. result.Push(deltaPath + fileName);
  354. if ((recursive) && (fileName != ".") && (fileName != ".."))
  355. ScanDirInternal(result, path + fileName, startPath, filter, flags, recursive);
  356. }
  357. else if (flags & SCAN_FILES)
  358. result.Push(deltaPath + fileName);
  359. }
  360. }
  361. closedir(dir);
  362. }
  363. #endif
  364. }
  365. void SplitPath(const String& fullPath, String& pathName, String& fileName, String& extension)
  366. {
  367. String fullPathCopy = GetInternalPath(fullPath);
  368. unsigned extPos = fullPathCopy.FindLast('.');
  369. if (extPos != String::NPOS)
  370. {
  371. extension = fullPathCopy.Substring(extPos).ToLower();
  372. fullPathCopy = fullPathCopy.Substring(0, extPos);
  373. }
  374. else
  375. extension.Clear();
  376. unsigned pathPos = fullPathCopy.FindLast('/');
  377. if (pathPos != String::NPOS)
  378. {
  379. fileName = fullPathCopy.Substring(pathPos + 1);
  380. pathName = fullPathCopy.Substring(0, pathPos + 1);
  381. }
  382. else
  383. {
  384. fileName = fullPathCopy;
  385. pathName.Clear();
  386. }
  387. }
  388. String GetPath(const String& fullPath)
  389. {
  390. String path, file, extension;
  391. SplitPath(fullPath, path, file, extension);
  392. return path;
  393. }
  394. String GetFileName(const String& fullPath)
  395. {
  396. String path, file, extension;
  397. SplitPath(fullPath, path, file, extension);
  398. return file;
  399. }
  400. String GetExtension(const String& fullPath)
  401. {
  402. String path, file, extension;
  403. SplitPath(fullPath, path, file, extension);
  404. return extension;
  405. }
  406. String GetFileNameAndExtension(const String& fileName)
  407. {
  408. String path, file, extension;
  409. SplitPath(fileName, path, file, extension);
  410. return file + extension;
  411. }
  412. String AddTrailingSlash(const String& pathName)
  413. {
  414. String ret = pathName;
  415. ret.Replace('\\', '/');
  416. if ((!ret.Empty()) && (ret.Back() != '/'))
  417. ret += '/';
  418. return ret;
  419. }
  420. String RemoveTrailingSlash(const String& pathName)
  421. {
  422. String ret = pathName;
  423. ret.Replace('\\', '/');
  424. if ((!ret.Empty()) && (ret.Back() == '/'))
  425. ret.Resize(ret.Length() - 1);
  426. return ret;
  427. }
  428. String GetParentPath(const String& path)
  429. {
  430. unsigned pos = RemoveTrailingSlash(path).FindLast('/');
  431. if (pos != String::NPOS)
  432. return path.Substring(0, pos + 1);
  433. else
  434. return String();
  435. }
  436. String GetInternalPath(const String& pathName)
  437. {
  438. String ret = pathName;
  439. ret.Replace('\\', '/');
  440. return ret;
  441. }
  442. String GetNativePath(const String& pathName)
  443. {
  444. #ifdef WIN32
  445. String ret = pathName;
  446. ret.Replace('/', '\\');
  447. return ret;
  448. #else
  449. return pathName;
  450. #endif
  451. }