Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
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 :
24 : #include "SPFilesystem.h"
25 :
26 : #if WIN32
27 : #include "SPLog.h"
28 : #else
29 : #include <unistd.h>
30 : #endif
31 :
32 : namespace STAPPLER_VERSIONIZED stappler::filepath {
33 :
34 : SPUNUSED static bool inAppBundle(StringView path);
35 :
36 : }
37 :
38 : namespace STAPPLER_VERSIONIZED stappler::filesystem {
39 :
40 300 : File File::open_tmp(StringView prefix, bool delOnClose) {
41 300 : if (prefix.empty()) {
42 0 : prefix = StringView("sa.tmp");
43 : }
44 :
45 : #if WIN32
46 : log::warn("filesystem", "File::open_tmp unavailable on win32");
47 : #else
48 300 : char buf[256] = { 0 };
49 300 : const char *tmp = P_tmpdir;
50 300 : size_t len = strlen(tmp);
51 300 : strcpy(&buf[0], tmp);
52 300 : strcpy(&buf[len], "/");
53 300 : strncpy(&buf[len + 1], prefix.data(), prefix.size());
54 300 : len += prefix.size();
55 300 : strcpy(&buf[len + 1], "XXXXXX");
56 :
57 300 : if (auto fd = ::mkstemp(buf)) {
58 300 : if (auto f = ::fdopen(fd, "wb+")) {
59 300 : auto ret = File(f, delOnClose ? Flags::DelOnClose : Flags::None);
60 300 : ret.set_tmp_path(buf);
61 300 : return ret;
62 300 : }
63 : }
64 : #endif
65 :
66 0 : return File();
67 : }
68 :
69 325 : File::File() : _isBundled(false), _nativeFile(nullptr) { }
70 22859 : File::File(FILE *f, Flags flags) : _isBundled(false), _flags(flags), _nativeFile(f) {
71 22859 : if (is_open()) {
72 22859 : auto pos = seek(0, io::Seek::Current);
73 22859 : auto size = seek(0, io::Seek::End);
74 22859 : if (pos != maxOf<size_t>()) {
75 22859 : seek(pos, io::Seek::Set);
76 : }
77 22859 : _size = (size != maxOf<size_t>())?size:0;
78 : }
79 22859 : }
80 0 : File::File(void *f) : _isBundled(true), _platformFile(f) {
81 0 : if (is_open()) {
82 0 : auto pos = seek(0, io::Seek::Current);
83 0 : auto size = seek(0, io::Seek::End);
84 0 : if (pos != maxOf<size_t>()) {
85 0 : seek(pos, io::Seek::Set);
86 : }
87 0 : _size = (size != maxOf<size_t>())?size:0;
88 : }
89 0 : }
90 :
91 0 : File::File(void *f, size_t s) : _isBundled(true), _size(s), _platformFile(f) { }
92 :
93 450 : File::File(File && f) : _isBundled(f._isBundled), _size(f._size) {
94 450 : if (_isBundled) {
95 0 : _platformFile = f._platformFile;
96 0 : f._platformFile = nullptr;
97 : } else {
98 450 : _nativeFile = f._nativeFile;
99 450 : f._nativeFile = nullptr;
100 : }
101 450 : f._size = 0;
102 450 : if (f._buf[0] != 0) {
103 425 : memcpy(_buf, f._buf, 256);
104 : }
105 450 : }
106 :
107 175 : File & File::operator=(File && f) {
108 175 : _isBundled = f._isBundled;
109 175 : _size = f._size;
110 175 : if (_isBundled) {
111 0 : _platformFile = f._platformFile;
112 0 : f._platformFile = nullptr;
113 : } else {
114 175 : _nativeFile = f._nativeFile;
115 175 : f._nativeFile = nullptr;
116 : }
117 175 : f._size = 0;
118 175 : if (f._buf[0] != 0) {
119 175 : memcpy(_buf, f._buf, 256);
120 : }
121 175 : return *this;
122 : }
123 :
124 23634 : File::~File() {
125 23634 : close();
126 23634 : }
127 :
128 660809 : size_t File::read(uint8_t *buf, size_t nbytes) {
129 660809 : if (is_open()) {
130 660809 : if (!_isBundled) {
131 660809 : size_t remains = _size - ftell(_nativeFile);
132 660809 : if (nbytes > remains) {
133 975 : nbytes = remains;
134 : }
135 660809 : if (fread(buf, nbytes, 1, _nativeFile) == 1) {
136 660809 : return nbytes;
137 : }
138 : } else {
139 0 : return filesystem::platform::_read(_platformFile, buf, nbytes);
140 : }
141 : }
142 0 : return 0;
143 : }
144 :
145 92511 : size_t File::seek(int64_t offset, io::Seek s) {
146 92511 : if (is_open()) {
147 92511 : if (!_isBundled) {
148 92511 : int whence = SEEK_SET;
149 92511 : switch (s) {
150 46793 : case io::Seek::Set: whence = SEEK_SET; break;
151 22859 : case io::Seek::Current: whence = SEEK_CUR; break;
152 22859 : case io::Seek::End: whence = SEEK_END; break;
153 : }
154 :
155 92511 : if (offset != 0 || s != io::Seek::Current) {
156 69652 : if (fseek(_nativeFile, long(offset), whence) != 0) {
157 0 : return maxOf<size_t>();
158 : }
159 : }
160 92511 : auto p = ftell(_nativeFile);
161 92511 : if (p >= 0) {
162 92511 : return static_cast<size_t>(p);
163 : } else {
164 0 : return maxOf<size_t>();
165 : }
166 : } else {
167 0 : return filesystem::platform::_seek(_platformFile, offset, s);
168 : }
169 : }
170 0 : return maxOf<size_t>();
171 : }
172 :
173 0 : size_t File::tell() const {
174 0 : if (!_isBundled) {
175 0 : auto p = ftell(_nativeFile);
176 0 : if (p >= 0) {
177 0 : return static_cast<size_t>(p);
178 : } else {
179 0 : return maxOf<size_t>();
180 : }
181 : } else {
182 0 : return filesystem::platform::_tell(_platformFile);
183 : }
184 : }
185 :
186 19490 : size_t File::size() const {
187 19490 : return _size;
188 : }
189 :
190 0 : typename File::int_type File::xsgetc() {
191 0 : int_type ret = traits_type::eof();
192 0 : if (is_open()) {
193 0 : if (!_isBundled) {
194 0 : ret = fgetc(_nativeFile);
195 : } else {
196 0 : uint8_t buf = 0;
197 0 : if (read(&buf, 1) == 1) {
198 0 : ret = buf;
199 : }
200 : }
201 : }
202 0 : return ret;
203 : }
204 :
205 0 : typename File::int_type File::xsputc(int_type c) {
206 0 : int_type ret = traits_type::eof();
207 0 : if (is_open() && !_isBundled) {
208 0 : ret = fputc(c, _nativeFile);
209 : }
210 0 : ++ _size;
211 0 : return ret;
212 : }
213 :
214 7850 : typename File::streamsize File::xsputn(const char* s, streamsize n) {
215 7850 : streamsize ret = -1;
216 7850 : if (is_open() && !_isBundled) {
217 7850 : if (fwrite(s, n, 1, _nativeFile) == 1) {
218 7850 : ret = n;
219 : }
220 : }
221 7850 : _size += n;
222 7850 : return ret;
223 : }
224 :
225 50 : typename File::streamsize File::xsgetn(char* s, streamsize n) {
226 50 : streamsize ret = -1;
227 50 : if (is_open()) {
228 50 : ret = read(reinterpret_cast<uint8_t *>(s), n);
229 : }
230 50 : return ret;
231 : }
232 :
233 0 : bool File::eof() const {
234 0 : if (is_open()) {
235 0 : if (!_isBundled) {
236 0 : return feof(_nativeFile) != 0;
237 : } else {
238 0 : return filesystem::platform::_eof(_platformFile);
239 : }
240 : }
241 0 : return true;
242 : }
243 :
244 43224 : void File::close() {
245 43224 : if (is_open()) {
246 22684 : if (!_isBundled) {
247 22684 : fclose(_nativeFile);
248 22684 : if (_flags != Flags::DelOnClose && _buf[0] != 0) {
249 125 : native::unlink_fn(_buf);
250 : }
251 22684 : memset(_buf, 0, 256);
252 22684 : _nativeFile = nullptr;
253 : } else {
254 0 : filesystem::platform::_close(_platformFile);
255 0 : _platformFile = nullptr;
256 : }
257 : }
258 43224 : }
259 :
260 475 : void File::close_remove() {
261 475 : if (is_open()) {
262 75 : if (!_isBundled) {
263 75 : fclose(_nativeFile);
264 75 : if (_buf[0] != 0) {
265 75 : native::unlink_fn(_buf);
266 : }
267 75 : memset(_buf, 0, 256);
268 75 : _nativeFile = nullptr;
269 : } else {
270 0 : filesystem::platform::_close(_platformFile);
271 0 : _platformFile = nullptr;
272 : }
273 : }
274 475 : }
275 :
276 100 : bool File::close_rename(StringView path) {
277 100 : if (is_open()) {
278 100 : if (!_isBundled && _buf[0] != 0) {
279 100 : fclose(_nativeFile);
280 100 : _nativeFile = nullptr;
281 100 : if (move(_buf, path)) {
282 100 : memset(_buf, 0, 256);
283 100 : return true;
284 : } else {
285 0 : _nativeFile = native::fopen_fn(_buf, "wb+");
286 : }
287 : }
288 : }
289 0 : return false;
290 : }
291 :
292 848443 : bool File::is_open() const {
293 848443 : return _nativeFile != nullptr || _platformFile != nullptr;
294 : }
295 :
296 325 : const char *File::path() const {
297 325 : if (_buf[0] == 0) {
298 0 : return nullptr;
299 : } else {
300 325 : return _buf;
301 : }
302 : }
303 :
304 300 : void File::set_tmp_path(const char *buf) {
305 300 : memcpy(_buf, buf, 256);
306 300 : }
307 :
308 1463 : bool exists(StringView ipath) {
309 1463 : if (filepath::isAbsolute(ipath)) {
310 1369 : return filesystem::native::access_fn(ipath, Access::Exists);
311 94 : } else if (filepath::isBundled(ipath)) {
312 0 : return filesystem::platform::_exists(ipath);
313 94 : } else if (!filepath::isAboveRoot(ipath) && filesystem::platform::_exists(ipath)) {
314 0 : return true;
315 : } else {
316 94 : auto path = filepath::absolute<memory::StandartInterface>(ipath);
317 94 : return filesystem::native::access_fn(path, Access::Exists);
318 94 : }
319 : }
320 :
321 950 : bool stat(StringView ipath, Stat &stat) {
322 950 : if (filepath::isAbsolute(ipath)) {
323 950 : return filesystem::native::stat_fn(ipath, stat);
324 0 : } else if (filepath::isBundled(ipath)) {
325 0 : return filesystem::platform::_stat(ipath, stat);
326 0 : } else if (!filepath::isAboveRoot(ipath) && filesystem::platform::_stat(ipath, stat)) {
327 0 : return true;
328 : } else {
329 0 : auto path = filepath::absolute<memory::StandartInterface>(ipath);
330 0 : return filesystem::native::stat_fn(path, stat);
331 0 : }
332 : }
333 :
334 2320 : bool remove(StringView ipath, bool recursive, bool withDirs) {
335 2320 : if (filepath::inAppBundle(ipath)) {
336 0 : return false;
337 : }
338 :
339 2320 : auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
340 2320 : if (!recursive) {
341 2245 : return filesystem::native::remove_fn(path);
342 : } else {
343 150 : return ftw_b(path, [withDirs] (const StringView &fpath, bool isFile) -> bool {
344 570 : if (isFile || withDirs) {
345 570 : return remove(fpath);
346 : }
347 0 : return true;
348 75 : });
349 : }
350 2320 : }
351 :
352 50 : bool touch(StringView ipath) {
353 50 : auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
354 50 : if (filepath::isBundled(path)) {
355 0 : return false;
356 : }
357 50 : return filesystem::native::touch_fn(path);
358 50 : }
359 :
360 475 : bool mkdir(StringView ipath) {
361 475 : auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
362 950 : return filesystem::native::mkdir_fn(path);
363 475 : }
364 :
365 25 : bool mkdir_recursive(StringView ipath, bool appWide) {
366 25 : auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
367 :
368 25 : memory::StandartInterface::StringType appWideLimit;
369 25 : if (appWide) {
370 : do {
371 0 : auto testPath = cachesPath<memory::StandartInterface>();
372 0 : if (path.compare(0, std::min(path.size(), testPath.size()), testPath) == 0) {
373 0 : appWideLimit = std::move(testPath);
374 0 : break;
375 : }
376 :
377 0 : testPath = writablePath<memory::StandartInterface>();
378 0 : if (path.compare(0, std::min(path.size(), testPath.size()), testPath) == 0) {
379 0 : appWideLimit = std::move(testPath);
380 0 : break;
381 : }
382 :
383 0 : testPath = documentsPath<memory::StandartInterface>();
384 0 : if (path.compare(0, std::min(path.size(), testPath.size()), testPath) == 0) {
385 0 : appWideLimit = std::move(testPath);
386 0 : break;
387 : }
388 :
389 0 : testPath = currentDir<memory::StandartInterface>();
390 0 : if (path.compare(0, std::min(path.size(), testPath.size()), testPath) == 0) {
391 0 : appWideLimit = std::move(testPath);
392 0 : break;
393 : }
394 0 : } while (0);
395 :
396 0 : if (appWideLimit.empty()) {
397 0 : return false;
398 : }
399 : }
400 :
401 25 : auto components = filepath::split<memory::StandartInterface>(path);
402 25 : if (!components.empty()) {
403 25 : bool control = false;
404 25 : memory::StandartInterface::StringType construct("/");
405 325 : for (auto &it : components) {
406 300 : construct.append(it.data(), it.size());
407 300 : if (!appWide || construct.compare(0, std::min(construct.size(), appWideLimit.size()), appWideLimit) == 0) {
408 300 : Stat stat;
409 300 : if (control || !filesystem::native::stat_fn(path, stat)) {
410 0 : control = true;
411 0 : filesystem::native::mkdir_fn(construct);
412 300 : } else if (!stat.isDir) {
413 0 : return false;
414 : }
415 : }
416 300 : construct.append("/");
417 : }
418 25 : }
419 25 : return true;
420 25 : }
421 :
422 275 : void ftw(StringView ipath, const Callback<void(StringView path, bool isFile)> &callback, int depth, bool dir_first) {
423 275 : auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
424 275 : if (filepath::isBundled(path)) {
425 0 : filesystem::platform::_ftw(path, callback, depth, dir_first);
426 : } else {
427 275 : filesystem::native::ftw_fn(path, callback, depth, dir_first);
428 : }
429 275 : }
430 :
431 75 : bool ftw_b(StringView ipath, const Callback<bool(StringView path, bool isFile)> &callback, int depth, bool dir_first) {
432 75 : auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
433 75 : if (filepath::isBundled(path)) {
434 0 : return filesystem::platform::_ftw_b(path, callback, depth, dir_first);
435 : } else {
436 75 : return filesystem::native::ftw_b_fn(path, callback, depth, dir_first);
437 : }
438 75 : }
439 :
440 225 : bool move(StringView isource, StringView idest) {
441 225 : auto source = filepath::absolute<memory::StandartInterface>(isource, true);
442 225 : auto dest = filepath::absolute<memory::StandartInterface>(idest, true);
443 :
444 225 : if (!filesystem::native::rename_fn(source, dest)) {
445 225 : if (copy(source, dest)) {
446 225 : return remove(source);
447 : }
448 0 : return false;
449 : }
450 0 : return true;
451 225 : }
452 :
453 225 : static bool performCopy(StringView source, StringView dest) {
454 225 : if (stappler::filesystem::exists(dest)) {
455 0 : stappler::filesystem::remove(dest);
456 : }
457 225 : if (!stappler::filesystem::exists(dest)) {
458 225 : std::ofstream destStream(dest.data(), std::ios::binary);
459 225 : auto f = openForReading(source);
460 225 : if (f && destStream.is_open()) {
461 225 : if (io::read(f, io::Consumer(destStream)) > 0) {
462 225 : return true;
463 : }
464 : }
465 0 : destStream.close();
466 450 : }
467 0 : return false;
468 : }
469 :
470 450 : static bool isdir(StringView path) {
471 450 : Stat stat;
472 450 : native::stat_fn(path, stat);
473 450 : return stat.isDir;
474 : }
475 :
476 225 : bool copy(StringView isource, StringView idest, bool stopOnError) {
477 225 : auto source = filepath::absolute<memory::StandartInterface>(isource, true);
478 225 : auto dest = filepath::absolute<memory::StandartInterface>(idest, true);
479 225 : if (dest.back() == '/') {
480 0 : dest = filepath::merge<memory::StandartInterface>(dest, filepath::lastComponent(source));
481 225 : } else if (isdir(dest) && filepath::lastComponent(source) != filepath::lastComponent(dest)) {
482 0 : dest = filepath::merge<memory::StandartInterface>(dest, filepath::lastComponent(source));
483 : }
484 225 : if (!isdir(source)) {
485 225 : return performCopy(source, dest);
486 : } else {
487 0 : return ftw_b(source, [source, dest, stopOnError] (StringView path, bool isFile) -> bool {
488 0 : if (!isFile) {
489 0 : if (path == source) {
490 0 : mkdir(filepath::replace<memory::StandartInterface>(path, source, dest));
491 0 : return true;
492 : }
493 0 : bool ret = mkdir(filepath::replace<memory::StandartInterface>(path, source, dest));
494 0 : if (stopOnError) {
495 0 : return ret;
496 : } else {
497 0 : return true;
498 : }
499 : } else {
500 0 : bool ret = performCopy(path, filepath::replace<memory::StandartInterface>(path, source, dest));
501 0 : if (stopOnError) {
502 0 : return ret;
503 : } else {
504 0 : return true;
505 : }
506 : }
507 0 : }, -1, true);
508 : }
509 225 : }
510 :
511 275 : bool write(StringView ipath, const unsigned char *data, size_t len) {
512 275 : auto path = filepath::absolute<memory::StandartInterface>(ipath, true);
513 550 : return filesystem::native::write_fn(path, data, len);
514 275 : }
515 :
516 22684 : File openForReading(StringView ipath) {
517 22684 : if (filepath::inAppBundle(ipath)) {
518 0 : return filesystem::platform::_openForReading(ipath);
519 : }
520 22684 : auto path = filepath::absolute<memory::StandartInterface>(ipath);
521 22684 : auto f = filesystem::native::fopen_fn(path, "rb");
522 22684 : if (f) {
523 22559 : return File(f);
524 : }
525 125 : return File();
526 22684 : }
527 :
528 250 : bool readIntoBuffer(uint8_t *buf, StringView ipath, size_t off, size_t size) {
529 250 : auto f = openForReading(ipath);
530 250 : if (f) {
531 250 : size_t fsize = f.size();
532 250 : if (fsize <= off) {
533 0 : f.close();
534 0 : return false;
535 : }
536 250 : if (fsize - off < size) {
537 250 : size = fsize - off;
538 : }
539 :
540 250 : bool ret = true;
541 250 : f.seek(off, io::Seek::Set);
542 250 : if (f.read(buf, size) != size) {
543 0 : ret = false;
544 : }
545 250 : f.close();
546 250 : return ret;
547 : }
548 0 : return false;
549 250 : }
550 :
551 0 : bool readWithConsumer(const io::Consumer &stream, uint8_t *buf, size_t bsize, StringView ipath, size_t off, size_t size) {
552 0 : auto f = openForReading(ipath);
553 0 : if (f) {
554 0 : size_t fsize = f.size();
555 0 : if (fsize <= off) {
556 0 : f.close();
557 0 : return false;
558 : }
559 0 : if (fsize - off < size) {
560 0 : size = fsize - off;
561 : }
562 :
563 0 : bool ret = true;
564 0 : f.seek(off, io::Seek::Set);
565 0 : while (size > 0) {
566 0 : auto read = min(size, bsize);
567 0 : if (f.read(buf, read) == read) {
568 0 : stream.write(buf, read);
569 : } else {
570 0 : ret = false;
571 0 : break;
572 : }
573 0 : size -= read;
574 : }
575 0 : f.close();
576 0 : return ret;
577 : }
578 0 : return false;
579 0 : }
580 :
581 : }
|