1 /* Copyright (C) 2018 Wildfire Games.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * higher-level interface on top of sysdep/filesystem.h
25  */
26 
27 #include "precompiled.h"
28 #include "lib/file/file_system.h"
29 
30 #include <vector>
31 #include <algorithm>
32 #include <string>
33 
34 #include "lib/sysdep/filesystem.h"
35 
36 
DirectoryExists(const OsPath & path)37 bool DirectoryExists(const OsPath& path)
38 {
39 	WDIR* dir = wopendir(path);
40 	if(dir)
41 	{
42 		wclosedir(dir);
43 		return true;
44 	}
45 	return false;
46 }
47 
48 
FileExists(const OsPath & pathname)49 bool FileExists(const OsPath& pathname)
50 {
51 	struct stat s;
52 	const bool exists = wstat(pathname, &s) == 0;
53 	return exists;
54 }
55 
56 
FileSize(const OsPath & pathname)57 u64 FileSize(const OsPath& pathname)
58 {
59 	struct stat s;
60 	ENSURE(wstat(pathname, &s) == 0);
61 	return s.st_size;
62 }
63 
64 
GetFileInfo(const OsPath & pathname,CFileInfo * pPtrInfo)65 Status GetFileInfo(const OsPath& pathname, CFileInfo* pPtrInfo)
66 {
67 	errno = 0;
68 	struct stat s;
69 	memset(&s, 0, sizeof(s));
70 	if(wstat(pathname, &s) != 0)
71 		WARN_RETURN(StatusFromErrno());
72 
73 	*pPtrInfo = CFileInfo(pathname.Filename(), s.st_size, s.st_mtime);
74 	return INFO::OK;
75 }
76 
77 
78 struct DirDeleter
79 {
operator ()DirDeleter80 	void operator()(WDIR* osDir) const
81 	{
82 		const int ret = wclosedir(osDir);
83 		ENSURE(ret == 0);
84 	}
85 };
86 
GetDirectoryEntries(const OsPath & path,CFileInfos * files,DirectoryNames * subdirectoryNames)87 Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames)
88 {
89 	// open directory
90 	errno = 0;
91 	WDIR* pDir = wopendir(path);
92 	if(!pDir)
93 		return StatusFromErrno();	// NOWARN
94 	shared_ptr<WDIR> osDir(pDir, DirDeleter());
95 
96 	for(;;)
97 	{
98 		errno = 0;
99 		struct wdirent* osEnt = wreaddir(osDir.get());
100 		if(!osEnt)
101 		{
102 			// no error, just no more entries to return
103 			if(!errno)
104 				return INFO::OK;
105 			WARN_RETURN(StatusFromErrno());
106 		}
107 
108 		for(size_t i = 0; osEnt->d_name[i] != '\0'; i++)
109 			RETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i]));
110 		const OsPath name(osEnt->d_name);
111 
112 		// get file information (mode, size, mtime)
113 		struct stat s;
114 #if OS_WIN
115 		// .. return wdirent directly (much faster than calling stat).
116 		RETURN_STATUS_IF_ERR(wreaddir_stat_np(osDir.get(), &s));
117 #else
118 		// .. call regular stat().
119 		errno = 0;
120 		const OsPath pathname = path / name;
121 		if(wstat(pathname, &s) != 0)
122 			WARN_RETURN(StatusFromErrno());
123 #endif
124 
125 		if(files && S_ISREG(s.st_mode))
126 			files->push_back(CFileInfo(name, s.st_size, s.st_mtime));
127 		else if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L"." && name != L"..")
128 			subdirectoryNames->push_back(name);
129 	}
130 }
131 
132 
CreateDirectories(const OsPath & path,mode_t mode,bool breakpoint)133 Status CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint)
134 {
135 	if(path.empty())
136 		return INFO::OK;
137 
138 	struct stat s;
139 	if(wstat(path, &s) == 0)
140 	{
141 		if(!S_ISDIR(s.st_mode))	// encountered a file
142 			WARN_RETURN(ERR::FAIL);
143 		return INFO::OK;
144 	}
145 
146 	// If we were passed a path ending with '/', strip the '/' now so that
147 	// we can consistently use Parent to find parent directory names
148 	if(path.IsDirectory())
149 		return CreateDirectories(path.Parent(), mode, breakpoint);
150 
151 	RETURN_STATUS_IF_ERR(CreateDirectories(path.Parent(), mode));
152 
153 	errno = 0;
154 	if(wmkdir(path, mode) != 0)
155 	{
156 		debug_printf("CreateDirectories: failed to mkdir %s (mode %d)\n", path.string8().c_str(), mode);
157 		if (breakpoint)
158 			WARN_RETURN(StatusFromErrno());
159 		else
160 			return StatusFromErrno();
161 	}
162 
163 	return INFO::OK;
164 }
165 
166 
DeleteDirectory(const OsPath & path)167 Status DeleteDirectory(const OsPath& path)
168 {
169 	// note: we have to recursively empty the directory before it can
170 	// be deleted (required by Windows and POSIX rmdir()).
171 
172 	CFileInfos files; DirectoryNames subdirectoryNames;
173 	RETURN_STATUS_IF_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames));
174 
175 	// delete files
176 	for(size_t i = 0; i < files.size(); i++)
177 	{
178 		const OsPath pathname = path / files[i].Name();
179 		errno = 0;
180 		if(wunlink(pathname) != 0)
181 			WARN_RETURN(StatusFromErrno());
182 	}
183 
184 	// recurse over subdirectoryNames
185 	for(size_t i = 0; i < subdirectoryNames.size(); i++)
186 		RETURN_STATUS_IF_ERR(DeleteDirectory(path / subdirectoryNames[i]));
187 
188 	errno = 0;
189 	if(wrmdir(path) != 0)
190 		WARN_RETURN(StatusFromErrno());
191 
192 	return INFO::OK;
193 }
194 
195 
CopyFile(const OsPath & path,const OsPath & newPath,bool override_if_exists)196 Status CopyFile(const OsPath& path, const OsPath& newPath, bool override_if_exists/* = false*/)
197 {
198 	if(path.empty())
199 		return INFO::OK;
200 
201 	try
202 	{
203 		if(override_if_exists)
204 			fs::copy_file(path.string8(), newPath.string8(), boost::filesystem::copy_option::overwrite_if_exists);
205 		else
206 			fs::copy_file(path.string8(), newPath.string8());
207 	}
208 	catch(fs::filesystem_error& err)
209 	{
210 		debug_printf("CopyFile: failed to copy %s to %s.\n%s\n", path.string8().c_str(), path.string8().c_str(), err.what());
211 		return ERR::EXCEPTION;
212 	}
213 
214 	return INFO::OK;
215 }
216