1 /*************************************************************************/
2 /*  dir_access_unix.cpp                                                  */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "dir_access_unix.h"
31 
32 #if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED)
33 
34 #ifndef ANDROID_ENABLED
35 #include <sys/statvfs.h>
36 #endif
37 
38 #include "os/memory.h"
39 #include "print_string.h"
40 #include <errno.h>
41 #include <stdio.h>
42 
create_fs()43 DirAccess *DirAccessUnix::create_fs() {
44 
45 	return memnew(DirAccessUnix);
46 }
47 
list_dir_begin()48 bool DirAccessUnix::list_dir_begin() {
49 
50 	list_dir_end(); //close any previous dir opening!
51 
52 	//	char real_current_dir_name[2048]; //is this enough?!
53 	//getcwd(real_current_dir_name,2048);
54 	//chdir(curent_path.utf8().get_data());
55 	dir_stream = opendir(current_dir.utf8().get_data());
56 	//chdir(real_current_dir_name);
57 	if (!dir_stream)
58 		return true; //error!
59 
60 	return false;
61 }
62 
file_exists(String p_file)63 bool DirAccessUnix::file_exists(String p_file) {
64 
65 	GLOBAL_LOCK_FUNCTION
66 
67 	if (p_file.is_rel_path())
68 		p_file = current_dir.plus_file(p_file);
69 
70 	p_file = fix_path(p_file);
71 
72 	struct stat flags;
73 	bool success = (stat(p_file.utf8().get_data(), &flags) == 0);
74 
75 	if (success && S_ISDIR(flags.st_mode)) {
76 		success = false;
77 	}
78 
79 	return success;
80 }
81 
dir_exists(String p_dir)82 bool DirAccessUnix::dir_exists(String p_dir) {
83 
84 	GLOBAL_LOCK_FUNCTION
85 
86 	if (p_dir.is_rel_path())
87 		p_dir = get_current_dir().plus_file(p_dir);
88 
89 	p_dir = fix_path(p_dir);
90 
91 	struct stat flags;
92 	bool success = (stat(p_dir.utf8().get_data(), &flags) == 0);
93 
94 	if (success && S_ISDIR(flags.st_mode))
95 		return true;
96 
97 	return false;
98 }
99 
get_modified_time(String p_file)100 uint64_t DirAccessUnix::get_modified_time(String p_file) {
101 
102 	if (p_file.is_rel_path())
103 		p_file = current_dir.plus_file(p_file);
104 
105 	p_file = fix_path(p_file);
106 
107 	struct stat flags;
108 	bool success = (stat(p_file.utf8().get_data(), &flags) == 0);
109 
110 	if (success) {
111 		return flags.st_mtime;
112 	} else {
113 
114 		ERR_FAIL_V(0);
115 	};
116 	return 0;
117 };
118 
get_next()119 String DirAccessUnix::get_next() {
120 
121 	if (!dir_stream)
122 		return "";
123 	dirent *entry;
124 
125 	entry = readdir(dir_stream);
126 
127 	if (entry == NULL) {
128 
129 		list_dir_end();
130 		return "";
131 	}
132 
133 	//typedef struct stat Stat;
134 	struct stat flags;
135 
136 	String fname = fix_unicode_name(entry->d_name);
137 
138 	String f = current_dir.plus_file(fname);
139 
140 	if (stat(f.utf8().get_data(), &flags) == 0) {
141 
142 		if (S_ISDIR(flags.st_mode)) {
143 
144 			_cisdir = true;
145 
146 		} else {
147 
148 			_cisdir = false;
149 		}
150 
151 	} else {
152 
153 		_cisdir = false;
154 	}
155 
156 	_cishidden = (fname != "." && fname != ".." && fname.begins_with("."));
157 
158 	return fname;
159 }
160 
current_is_dir() const161 bool DirAccessUnix::current_is_dir() const {
162 
163 	return _cisdir;
164 }
165 
current_is_hidden() const166 bool DirAccessUnix::current_is_hidden() const {
167 
168 	return _cishidden;
169 }
170 
list_dir_end()171 void DirAccessUnix::list_dir_end() {
172 
173 	if (dir_stream)
174 		closedir(dir_stream);
175 	dir_stream = 0;
176 	_cisdir = false;
177 }
178 
get_drive_count()179 int DirAccessUnix::get_drive_count() {
180 
181 	return 0;
182 }
get_drive(int p_drive)183 String DirAccessUnix::get_drive(int p_drive) {
184 
185 	return "";
186 }
187 
make_dir(String p_dir)188 Error DirAccessUnix::make_dir(String p_dir) {
189 
190 	GLOBAL_LOCK_FUNCTION
191 
192 	if (p_dir.is_rel_path())
193 		p_dir = get_current_dir().plus_file(p_dir);
194 
195 	p_dir = fix_path(p_dir);
196 
197 #if 1
198 
199 	bool success = (mkdir(p_dir.utf8().get_data(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0);
200 	int err = errno;
201 
202 #else
203 	char real_current_dir_name[2048];
204 	getcwd(real_current_dir_name, 2048);
205 	chdir(current_dir.utf8().get_data()); //ascii since this may be unicode or wathever the host os wants
206 
207 	bool success = (mkdir(p_dir.utf8().get_data(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0);
208 	int err = errno;
209 
210 	chdir(real_current_dir_name);
211 #endif
212 	if (success) {
213 		return OK;
214 	};
215 
216 	if (err == EEXIST) {
217 		return ERR_ALREADY_EXISTS;
218 	};
219 
220 	return ERR_CANT_CREATE;
221 }
222 
change_dir(String p_dir)223 Error DirAccessUnix::change_dir(String p_dir) {
224 
225 	GLOBAL_LOCK_FUNCTION
226 	p_dir = fix_path(p_dir);
227 
228 	char real_current_dir_name[2048];
229 	getcwd(real_current_dir_name, 2048);
230 	String prev_dir;
231 	if (prev_dir.parse_utf8(real_current_dir_name))
232 		prev_dir = real_current_dir_name; //no utf8, maybe latin?
233 
234 	chdir(current_dir.utf8().get_data()); //ascii since this may be unicode or wathever the host os wants
235 	bool worked = (chdir(p_dir.utf8().get_data()) == 0); // we can only give this utf8
236 
237 	String base = _get_root_path();
238 	if (base != "") {
239 
240 		getcwd(real_current_dir_name, 2048);
241 		String new_dir;
242 		new_dir.parse_utf8(real_current_dir_name);
243 		if (!new_dir.begins_with(base))
244 			worked = false;
245 	}
246 
247 	if (worked) {
248 
249 		getcwd(real_current_dir_name, 2048);
250 		if (current_dir.parse_utf8(real_current_dir_name))
251 			current_dir = real_current_dir_name; //no utf8, maybe latin?
252 	}
253 
254 	chdir(prev_dir.utf8().get_data());
255 	return worked ? OK : ERR_INVALID_PARAMETER;
256 }
257 
get_current_dir()258 String DirAccessUnix::get_current_dir() {
259 
260 	String base = _get_root_path();
261 	if (base != "") {
262 
263 		String bd = current_dir.replace_first(base, "");
264 		if (bd.begins_with("/"))
265 			return _get_root_string() + bd.substr(1, bd.length());
266 		else
267 			return _get_root_string() + bd;
268 	}
269 	return current_dir;
270 }
271 
rename(String p_path,String p_new_path)272 Error DirAccessUnix::rename(String p_path, String p_new_path) {
273 
274 	if (p_path.is_rel_path())
275 		p_path = get_current_dir().plus_file(p_path);
276 
277 	p_path = fix_path(p_path);
278 
279 	if (p_new_path.is_rel_path())
280 		p_new_path = get_current_dir().plus_file(p_new_path);
281 
282 	p_new_path = fix_path(p_new_path);
283 
284 	return ::rename(p_path.utf8().get_data(), p_new_path.utf8().get_data()) == 0 ? OK : FAILED;
285 }
remove(String p_path)286 Error DirAccessUnix::remove(String p_path) {
287 
288 	if (p_path.is_rel_path())
289 		p_path = get_current_dir().plus_file(p_path);
290 
291 	p_path = fix_path(p_path);
292 
293 	struct stat flags;
294 	if ((stat(p_path.utf8().get_data(), &flags) != 0))
295 		return FAILED;
296 
297 	if (S_ISDIR(flags.st_mode))
298 		return ::rmdir(p_path.utf8().get_data()) == 0 ? OK : FAILED;
299 	else
300 		return ::unlink(p_path.utf8().get_data()) == 0 ? OK : FAILED;
301 }
302 
get_space_left()303 size_t DirAccessUnix::get_space_left() {
304 
305 #ifndef NO_STATVFS
306 	struct statvfs vfs;
307 	if (statvfs(current_dir.utf8().get_data(), &vfs) != 0) {
308 
309 		return 0;
310 	};
311 
312 	return vfs.f_bfree * vfs.f_bsize;
313 #else
314 #warning THIS IS BROKEN
315 	return 0;
316 #endif
317 };
318 
DirAccessUnix()319 DirAccessUnix::DirAccessUnix() {
320 
321 	dir_stream = 0;
322 	current_dir = ".";
323 	_cisdir = false;
324 
325 	/* determine drive count */
326 
327 	change_dir(current_dir);
328 }
329 
~DirAccessUnix()330 DirAccessUnix::~DirAccessUnix() {
331 
332 	list_dir_end();
333 }
334 
335 #endif //posix_enabled
336