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