FileSystem.cpp 13 KB

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