FileSystem.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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 "StringUtils.h"
  30. #include <cstdio>
  31. #include <direct.h>
  32. #include <process.h>
  33. #include <Windows.h>
  34. #include <shellapi.h>
  35. // Enable SHGetSpecialFolderPath on MinGW
  36. #ifndef _MSC_VER
  37. #define _WIN32_IE 0x0400
  38. #endif
  39. #include <shlobj.h>
  40. #include "DebugNew.h"
  41. OBJECTTYPESTATIC(FileSystem);
  42. FileSystem::FileSystem(Context* context) :
  43. Object(context)
  44. {
  45. }
  46. FileSystem::~FileSystem()
  47. {
  48. }
  49. bool FileSystem::SetCurrentDir(const String& pathName)
  50. {
  51. if (!CheckAccess(pathName))
  52. {
  53. LOGERROR("Access denied to " + pathName);
  54. return false;
  55. }
  56. if (SetCurrentDirectory(GetNativePath(pathName, true).CString()) == FALSE)
  57. {
  58. LOGERROR("Failed to change directory to " + pathName);
  59. return false;
  60. }
  61. return true;
  62. }
  63. bool FileSystem::CreateDir(const String& pathName)
  64. {
  65. if (!CheckAccess(pathName))
  66. {
  67. LOGERROR("Access denied to " + pathName);
  68. return false;
  69. }
  70. bool success = (CreateDirectory(GetNativePath(RemoveTrailingSlash(pathName), true).CString(), 0) == TRUE) || (GetLastError() == ERROR_ALREADY_EXISTS);
  71. if (success)
  72. LOGDEBUG("Created directory " + pathName);
  73. else
  74. LOGERROR("Failed to create directory " + pathName);
  75. return success;
  76. }
  77. int FileSystem::SystemCommand(const String& commandLine)
  78. {
  79. if (allowedPaths_.Empty())
  80. return system(commandLine.CString());
  81. else
  82. {
  83. LOGERROR("Executing an external command is not allowed");
  84. return -1;
  85. }
  86. }
  87. int FileSystem::SystemRun(const String& fileName, const Vector<String>& arguments)
  88. {
  89. if (allowedPaths_.Empty())
  90. {
  91. String fixedFileName = GetNativePath(fileName, true);
  92. Vector<const char*> argPtrs;
  93. argPtrs.Push(fixedFileName.CString());
  94. for (unsigned i = 0; i < arguments.Size(); ++i)
  95. argPtrs.Push(arguments[i].CString());
  96. argPtrs.Push(0);
  97. return _spawnv(_P_WAIT, fixedFileName.CString(), &argPtrs[0]);
  98. }
  99. else
  100. {
  101. LOGERROR("Executing an external command is not allowed");
  102. return -1;
  103. }
  104. }
  105. bool FileSystem::SystemOpen(const String& fileName, const String& mode)
  106. {
  107. if (allowedPaths_.Empty())
  108. {
  109. if ((!FileExists(fileName)) && (!DirExists(fileName)))
  110. {
  111. LOGERROR("File or directory " + fileName + " not found");
  112. return false;
  113. }
  114. bool success = (int)ShellExecute(0, !mode.Empty() ? (char*)mode.CString() : 0,
  115. (char*)GetNativePath(fileName, true).CString(), 0, 0, SW_SHOW) > 32;
  116. if (!success)
  117. LOGERROR("Failed to open " + fileName + " externally");
  118. return success;
  119. }
  120. else
  121. {
  122. LOGERROR("Opening a file externally is not allowed");
  123. return false;
  124. }
  125. }
  126. bool FileSystem::Copy(const String& srcFileName, const String& destFileName)
  127. {
  128. if (!CheckAccess(GetPath(srcFileName)))
  129. {
  130. LOGERROR("Access denied to " + srcFileName);
  131. return false;
  132. }
  133. if (!CheckAccess(GetPath(destFileName)))
  134. {
  135. LOGERROR("Access denied to " + destFileName);
  136. return false;
  137. }
  138. SharedPtr<File> srcFile(new File(context_, srcFileName, FILE_READ));
  139. SharedPtr<File> destFile(new File(context_, destFileName, FILE_WRITE));
  140. if ((!srcFile->IsOpen()) || (!destFile->IsOpen()))
  141. return false;
  142. unsigned fileSize = srcFile->GetSize();
  143. SharedArrayPtr<unsigned char> buffer(new unsigned char[fileSize]);
  144. unsigned bytesRead = srcFile->Read(buffer.GetPtr(), fileSize);
  145. unsigned bytesWritten = destFile->Write(buffer.GetPtr(), fileSize);
  146. return (bytesRead == fileSize) && (bytesWritten == fileSize);
  147. }
  148. bool FileSystem::Rename(const String& srcFileName, const String& destFileName)
  149. {
  150. if (!CheckAccess(GetPath(srcFileName)))
  151. {
  152. LOGERROR("Access denied to " + srcFileName);
  153. return false;
  154. }
  155. if (!CheckAccess(GetPath(destFileName)))
  156. {
  157. LOGERROR("Access denied to " + destFileName);
  158. return false;
  159. }
  160. return rename(GetNativePath(srcFileName).CString(), GetNativePath(destFileName).CString()) == 0;
  161. }
  162. bool FileSystem::Delete(const String& fileName)
  163. {
  164. if (!CheckAccess(GetPath(fileName)))
  165. {
  166. LOGERROR("Access denied to " + fileName);
  167. return false;
  168. }
  169. return remove(GetNativePath(fileName).CString()) == 0;
  170. }
  171. String FileSystem::GetCurrentDir()
  172. {
  173. char path[MAX_PATH];
  174. GetCurrentDirectory(MAX_PATH, path);
  175. return AddTrailingSlash(String(path));
  176. }
  177. bool FileSystem::CheckAccess(const String& pathName)
  178. {
  179. String fixedPath = AddTrailingSlash(pathName);
  180. // If no allowed directories defined, succeed always
  181. if (allowedPaths_.Empty())
  182. return true;
  183. // If there is any attempt to go to a parent directory, disallow
  184. if (fixedPath.Find("..") != String::NPOS)
  185. return false;
  186. // Check if the path is a partial match of any of the allowed directories
  187. for (Set<String>::ConstIterator i = allowedPaths_.Begin(); i != allowedPaths_.End(); ++i)
  188. {
  189. if (fixedPath.Find(*i) == 0)
  190. return true;
  191. }
  192. // Not found, so disallow
  193. return false;
  194. }
  195. bool FileSystem::FileExists(const String& fileName)
  196. {
  197. if (!CheckAccess(GetPath(fileName)))
  198. return false;
  199. String fixedName = GetNativePath(RemoveTrailingSlash(fileName), true);
  200. DWORD attributes = GetFileAttributes(fixedName.CString());
  201. if ((attributes == INVALID_FILE_ATTRIBUTES) || (attributes & FILE_ATTRIBUTE_DIRECTORY))
  202. return false;
  203. return true;
  204. }
  205. bool FileSystem::DirExists(const String& pathName)
  206. {
  207. if (!CheckAccess(pathName))
  208. return false;
  209. String fixedName = GetNativePath(RemoveTrailingSlash(pathName), true);
  210. DWORD attributes = GetFileAttributes(fixedName.CString());
  211. if ((attributes == INVALID_FILE_ATTRIBUTES) || (!(attributes & FILE_ATTRIBUTE_DIRECTORY)))
  212. return false;
  213. return true;
  214. }
  215. void FileSystem::ScanDir(Vector<String>& result, const String& pathName, const String& filter, unsigned flags, bool recursive)
  216. {
  217. result.Clear();
  218. if (CheckAccess(pathName))
  219. {
  220. String initialPath = AddTrailingSlash(pathName);
  221. ScanDirInternal(result, initialPath, initialPath, filter, flags, recursive);
  222. }
  223. }
  224. String FileSystem::GetProgramDir()
  225. {
  226. char exeName[MAX_PATH];
  227. exeName[0] = 0;
  228. GetModuleFileName(0, exeName, MAX_PATH);
  229. return GetPath(String(exeName));
  230. }
  231. String FileSystem::GetUserDocumentsDir()
  232. {
  233. char pathName[MAX_PATH];
  234. pathName[0] = 0;
  235. SHGetSpecialFolderPath(0, pathName, CSIDL_PERSONAL, 0);
  236. return AddTrailingSlash(String(pathName));
  237. }
  238. String FileSystem::GetSystemFontDir()
  239. {
  240. char pathName[MAX_PATH];
  241. if (!ExpandEnvironmentStrings("%WinDir%", pathName, MAX_PATH))
  242. return String();
  243. return AddTrailingSlash(String(pathName)) + "Fonts";
  244. }
  245. void FileSystem::RegisterPath(const String& pathName)
  246. {
  247. if (pathName.Empty())
  248. return;
  249. allowedPaths_.Insert(AddTrailingSlash(pathName));
  250. }
  251. void FileSystem::ScanDirInternal(Vector<String>& result, String path, const String& startPath,
  252. const String& filter, unsigned flags, bool recursive)
  253. {
  254. path = AddTrailingSlash(path);
  255. String pathAndFilter = GetNativePath(path + filter, true);
  256. String deltaPath;
  257. if (path.Length() > startPath.Length())
  258. deltaPath = path.Substring(startPath.Length());
  259. WIN32_FIND_DATA info;
  260. HANDLE handle = FindFirstFile(pathAndFilter.CString(), &info);
  261. if (handle != INVALID_HANDLE_VALUE)
  262. {
  263. do
  264. {
  265. String fileName((const char*)&info.cFileName[0]);
  266. if (!fileName.Empty())
  267. {
  268. if ((info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) && (!(flags & SCAN_HIDDEN)))
  269. continue;
  270. if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  271. {
  272. if (flags & SCAN_DIRS)
  273. result.Push(deltaPath + fileName);
  274. if ((recursive) && (fileName != ".") && (fileName != ".."))
  275. ScanDirInternal(result, path + fileName, startPath, filter, flags, recursive);
  276. }
  277. else if (flags & SCAN_FILES)
  278. result.Push(deltaPath + fileName);
  279. }
  280. }
  281. while (FindNextFile(handle, &info));
  282. FindClose(handle);
  283. }
  284. }
  285. void SplitPath(const String& fullPath, String& pathName, String& fileName, String& extension)
  286. {
  287. String fullPathCopy = fullPath.Replace('\\', '/');
  288. unsigned extPos = fullPathCopy.FindLast('.');
  289. if (extPos != String::NPOS)
  290. {
  291. extension = fullPathCopy.Substring(extPos).ToLower();
  292. fullPathCopy = fullPathCopy.Substring(0, extPos);
  293. }
  294. else
  295. extension.Clear();
  296. unsigned pathPos = fullPathCopy.FindLast('/');
  297. if (pathPos != String::NPOS)
  298. {
  299. fileName = fullPathCopy.Substring(pathPos + 1);
  300. pathName = fullPathCopy.Substring(0, pathPos + 1);
  301. }
  302. else
  303. {
  304. fileName = fullPathCopy;
  305. pathName.Clear();
  306. }
  307. }
  308. String GetPath(const String& fullPath)
  309. {
  310. String path, file, extension;
  311. SplitPath(fullPath, path, file, extension);
  312. return path;
  313. }
  314. String GetFileName(const String& fullPath)
  315. {
  316. String path, file, extension;
  317. SplitPath(fullPath, path, file, extension);
  318. return file;
  319. }
  320. String GetExtension(const String& fullPath)
  321. {
  322. String path, file, extension;
  323. SplitPath(fullPath, path, file, extension);
  324. return extension;
  325. }
  326. String GetFileNameAndExtension(const String& fileName)
  327. {
  328. String path, file, extension;
  329. SplitPath(fileName, path, file, extension);
  330. return file + extension;
  331. }
  332. String AddTrailingSlash(const String& path)
  333. {
  334. String ret;
  335. if (!path.Empty())
  336. {
  337. ret = path;
  338. char last = path[path.Length() - 1];
  339. if ((last != '/') && (last != '\\'))
  340. ret += '/';
  341. }
  342. ret.ReplaceInPlace('\\', '/');
  343. return ret;
  344. }
  345. String RemoveTrailingSlash(const String& path)
  346. {
  347. if (!path.Empty())
  348. {
  349. char last = path[path.Length() - 1];
  350. if ((last == '/') || (last == '\\'))
  351. return path.Substring(0, path.Length() - 1);
  352. }
  353. return path;
  354. }
  355. String GetParentPath(const String& path)
  356. {
  357. unsigned pos = RemoveTrailingSlash(path).FindLast('/');
  358. if (pos != String::NPOS)
  359. return path.Substring(0, pos + 1);
  360. else
  361. return path;
  362. }
  363. String GetNativePath(const String& pathName, bool forNativeApi)
  364. {
  365. // On MSVC, replace slash always with backslash. On MinGW only if going to do Win32 native calls
  366. #ifdef _MSC_VER
  367. forNativeApi = true;
  368. #endif
  369. if (forNativeApi)
  370. return pathName.Replace('/', '\\');
  371. else
  372. return pathName;
  373. }