Line data Source code
1 : /* Copyright (C) 2022 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 :
29 : #include "lib/file/file_system.h"
30 :
31 : #include "lib/sysdep/filesystem.h"
32 :
33 : #include <boost/filesystem.hpp>
34 : #include <memory>
35 :
36 224 : bool DirectoryExists(const OsPath& path)
37 : {
38 224 : WDIR* dir = wopendir(path);
39 224 : if(dir)
40 : {
41 99 : wclosedir(dir);
42 99 : return true;
43 : }
44 125 : return false;
45 : }
46 :
47 :
48 0 : bool FileExists(const OsPath& pathname)
49 : {
50 : struct stat s;
51 0 : const bool exists = wstat(pathname, &s) == 0;
52 0 : return exists;
53 : }
54 :
55 :
56 0 : u64 FileSize(const OsPath& pathname)
57 : {
58 : struct stat s;
59 0 : ENSURE(wstat(pathname, &s) == 0);
60 0 : return s.st_size;
61 : }
62 :
63 :
64 1 : Status GetFileInfo(const OsPath& pathname, CFileInfo* pPtrInfo)
65 : {
66 1 : errno = 0;
67 : struct stat s;
68 1 : memset(&s, 0, sizeof(s));
69 1 : if(wstat(pathname, &s) != 0)
70 0 : WARN_RETURN(StatusFromErrno());
71 :
72 1 : *pPtrInfo = CFileInfo(pathname.Filename(), s.st_size, s.st_mtime);
73 1 : return INFO::OK;
74 : }
75 :
76 :
77 : struct DirDeleter
78 : {
79 767 : void operator()(WDIR* osDir) const
80 : {
81 767 : const int ret = wclosedir(osDir);
82 767 : ENSURE(ret == 0);
83 767 : }
84 : };
85 :
86 771 : Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames)
87 : {
88 : // open directory
89 771 : errno = 0;
90 771 : WDIR* pDir = wopendir(path);
91 771 : if(!pDir)
92 4 : return StatusFromErrno(); // NOWARN
93 1534 : std::shared_ptr<WDIR> osDir(pDir, DirDeleter());
94 :
95 : for(;;)
96 : {
97 5427 : errno = 0;
98 5427 : struct wdirent* osEnt = wreaddir(osDir.get());
99 5427 : if(!osEnt)
100 : {
101 : // no error, just no more entries to return
102 767 : if(!errno)
103 1534 : return INFO::OK;
104 0 : WARN_RETURN(StatusFromErrno());
105 : }
106 :
107 51623 : for(size_t i = 0; osEnt->d_name[i] != '\0'; i++)
108 46963 : RETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i]));
109 9320 : const OsPath name(osEnt->d_name);
110 :
111 : // get file information (mode, size, mtime)
112 : struct stat s;
113 : #if OS_WIN
114 : // .. return wdirent directly (much faster than calling stat).
115 : RETURN_STATUS_IF_ERR(wreaddir_stat_np(osDir.get(), &s));
116 : #else
117 : // .. call regular stat().
118 4660 : errno = 0;
119 9320 : const OsPath pathname = path / name;
120 4660 : if(wstat(pathname, &s) != 0)
121 0 : WARN_RETURN(StatusFromErrno());
122 : #endif
123 :
124 4660 : if(files && S_ISREG(s.st_mode))
125 2409 : files->push_back(CFileInfo(name, s.st_size, s.st_mtime));
126 2251 : else if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L"." && name != L"..")
127 717 : subdirectoryNames->push_back(name);
128 4660 : }
129 : }
130 :
131 :
132 308 : Status CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint)
133 : {
134 308 : if(path.empty())
135 0 : return INFO::OK;
136 :
137 : struct stat s;
138 308 : if(wstat(path, &s) == 0)
139 : {
140 102 : if(!S_ISDIR(s.st_mode)) // encountered a file
141 0 : WARN_RETURN(ERR::FAIL);
142 102 : return INFO::OK;
143 : }
144 :
145 : // If we were passed a path ending with '/', strip the '/' now so that
146 : // we can consistently use Parent to find parent directory names
147 206 : if(path.IsDirectory())
148 83 : return CreateDirectories(path.Parent(), mode, breakpoint);
149 :
150 123 : RETURN_STATUS_IF_ERR(CreateDirectories(path.Parent(), mode));
151 :
152 123 : errno = 0;
153 123 : if(wmkdir(path, mode) != 0)
154 : {
155 0 : debug_printf("CreateDirectories: failed to mkdir %s (mode %d)\n", path.string8().c_str(), mode);
156 0 : if (breakpoint)
157 0 : WARN_RETURN(StatusFromErrno());
158 : else
159 0 : return StatusFromErrno();
160 : }
161 :
162 123 : return INFO::OK;
163 : }
164 :
165 :
166 243 : Status DeleteDirectory(const OsPath& path)
167 : {
168 : // note: we have to recursively empty the directory before it can
169 : // be deleted (required by Windows and POSIX rmdir()).
170 :
171 486 : CFileInfos files; DirectoryNames subdirectoryNames;
172 243 : RETURN_STATUS_IF_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames));
173 :
174 : // delete files
175 333 : for(size_t i = 0; i < files.size(); i++)
176 : {
177 188 : const OsPath pathname = path / files[i].Name();
178 94 : errno = 0;
179 94 : if(wunlink(pathname) != 0)
180 0 : WARN_RETURN(StatusFromErrno());
181 : }
182 :
183 : // recurse over subdirectoryNames
184 388 : for(size_t i = 0; i < subdirectoryNames.size(); i++)
185 149 : RETURN_STATUS_IF_ERR(DeleteDirectory(path / subdirectoryNames[i]));
186 :
187 239 : errno = 0;
188 239 : if(wrmdir(path) != 0)
189 0 : WARN_RETURN(StatusFromErrno());
190 :
191 239 : return INFO::OK;
192 : }
193 :
194 0 : Status RenameFile(const OsPath& path, const OsPath& newPath)
195 : {
196 0 : if (path.empty())
197 0 : return INFO::OK;
198 :
199 : try
200 : {
201 0 : fs::rename(fs::path(path.string()), fs::path(newPath.string()));
202 : }
203 0 : catch (fs::filesystem_error& err)
204 : {
205 0 : debug_printf("RenameFile: failed to rename %s to %s.\n%s\n", path.string8().c_str(), path.string8().c_str(), err.what());
206 0 : return ERR::EXCEPTION;
207 : }
208 :
209 0 : return INFO::OK;
210 :
211 : }
212 :
213 0 : Status CopyFile(const OsPath& path, const OsPath& newPath, bool override_if_exists/* = false*/)
214 : {
215 0 : if(path.empty())
216 0 : return INFO::OK;
217 :
218 : try
219 : {
220 0 : if(override_if_exists)
221 0 : fs::copy_file(fs::path(path.string()), fs::path(newPath.string()), boost::filesystem::copy_option::overwrite_if_exists);
222 : else
223 0 : fs::copy_file(fs::path(path.string()), fs::path(newPath.string()));
224 : }
225 0 : catch(fs::filesystem_error& err)
226 : {
227 0 : debug_printf("CopyFile: failed to copy %s to %s.\n%s\n", path.string8().c_str(), path.string8().c_str(), err.what());
228 0 : return ERR::EXCEPTION;
229 : }
230 :
231 0 : return INFO::OK;
232 3 : }
|