1 /**
2 * @file filesystem.cpp
3 * @brief Generic host filesystem access interfaces
4 *
5 * (c) 2013-2014 by Mega Limited, Auckland, New Zealand
6 *
7 * This file is part of the MEGA SDK - Client Access Engine.
8 *
9 * Applications using the MEGA API must present a valid application key
10 * and comply with the the rules set forth in the Terms of Service.
11 *
12 * The MEGA SDK is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 *
16 * @copyright Simplified (2-clause) BSD License.
17 *
18 * You should have received a copy of the license along with this
19 * program.
20 */
21 #include "mega/filesystem.h"
22 #include "mega/node.h"
23 #include "mega/megaclient.h"
24 #include "mega/logging.h"
25 #include "mega/mega_utf8proc.h"
26
27 namespace mega {
FileSystemAccess()28 FileSystemAccess::FileSystemAccess()
29 : waiter(NULL)
30 , skip_errorreport(false)
31 , transient_error(false)
32 , notifyerr(false)
33 , notifyfailed(false)
34 , target_exists(false)
35 , client(NULL)
36 {
37 }
38
captimestamp(m_time_t * t)39 void FileSystemAccess::captimestamp(m_time_t* t)
40 {
41 // FIXME: remove upper bound before the year 2100 and upgrade server-side timestamps to BIGINT
42 if (*t > (uint32_t)-1) *t = (uint32_t)-1;
43 else if (*t < 0) *t = 0;
44 }
45
islchex(char c) const46 bool FileSystemAccess::islchex(char c) const
47 {
48 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
49 }
50
fstypetostring(FileSystemType type) const51 const char *FileSystemAccess::fstypetostring(FileSystemType type) const
52 {
53 switch (type)
54 {
55 case FS_NTFS:
56 return "NTFS";
57 case FS_EXFAT:
58 return "EXFAT";
59 case FS_FAT32:
60 return "FAT32";
61 case FS_EXT:
62 return "EXT";
63 case FS_HFS:
64 return "HFS";
65 case FS_APFS:
66 return "APFS";
67 case FS_FUSE:
68 return "FUSE";
69 case FS_SDCARDFS:
70 return "SDCARDFS";
71 case FS_F2FS:
72 return "F2FS";
73 case FS_UNKNOWN: // fall through
74 return "UNKNOWN FS";
75 }
76
77 return "UNKNOWN FS";
78 }
79
getlocalfstype(const LocalPath & dstPath) const80 FileSystemType FileSystemAccess::getlocalfstype(const LocalPath& dstPath) const
81 {
82 if (dstPath.empty())
83 {
84 return FS_UNKNOWN;
85 }
86
87 #if defined (__linux__) && !defined (__ANDROID__)
88 // Filesystem detection for Linux
89 struct statfs fileStat;
90 if (!statfs(dstPath.editStringDirect()->c_str(), &fileStat))
91 {
92 switch (fileStat.f_type)
93 {
94 case EXT2_SUPER_MAGIC:
95 return FS_EXT;
96 case MSDOS_SUPER_MAGIC:
97 return FS_FAT32;
98 case HFS_SUPER_MAGIC:
99 return FS_HFS;
100 case NTFS_SB_MAGIC:
101 return FS_NTFS;
102 default:
103 return FS_UNKNOWN;
104 }
105 }
106 #elif defined (__ANDROID__)
107 // Filesystem detection for Android
108 struct statfs fileStat;
109 if (!statfs(dstPath.editStringDirect()->c_str(), &fileStat))
110 {
111 switch (fileStat.f_type)
112 {
113 case EXT2_SUPER_MAGIC:
114 return FS_EXT;
115 case MSDOS_SUPER_MAGIC:
116 return FS_FAT32;
117 case HFS_SUPER_MAGIC:
118 return FS_HFS;
119 case NTFS_SB_MAGIC:
120 return FS_NTFS;
121 case SDCARDFS_SUPER_MAGIC:
122 return FS_SDCARDFS;
123 case FUSEBLK_SUPER_MAGIC:
124 case FUSECTL_SUPER_MAGIC:
125 return FS_FUSE;
126 case F2FS_SUPER_MAGIC:
127 return FS_F2FS;
128 default:
129 return FS_UNKNOWN;
130 }
131 }
132 #elif defined (__APPLE__) || defined (USE_IOS)
133 // Filesystem detection for Apple and iOS
134 struct statfs fileStat;
135 if (!statfs(dstPath.editStringDirect()->c_str(), &fileStat))
136 {
137 if (!strcmp(fileStat.f_fstypename, "apfs"))
138 {
139 return FS_APFS;
140 }
141 if (!strcmp(fileStat.f_fstypename, "hfs"))
142 {
143 return FS_HFS;
144 }
145 if (!strcmp(fileStat.f_fstypename, "ntfs"))
146 {
147 return FS_NTFS;
148 }
149 if (!strcmp(fileStat.f_fstypename, "msdos"))
150 {
151 return FS_FAT32;
152 }
153 }
154 #elif defined(_WIN32) || defined(WINDOWS_PHONE)
155 // Filesystem detection for Windows
156
157 auto tmpPath = dstPath;
158 tmpPath.editStringDirect()->append("", 1); // make sure of 2 byte terminator as LPCTWSTR (later we'll make it wstring for windows)
159
160 std::wstring volMountPoint;
161 volMountPoint.resize(MAX_PATH);
162 DWORD mountLen = static_cast<DWORD>(volMountPoint.size());
163 if (!(GetVolumePathNameW((LPCWSTR)tmpPath.editStringDirect()->data(), &volMountPoint[0], mountLen)))
164 {
165 return FS_UNKNOWN;
166 }
167
168 LPCWSTR auxMountPoint = volMountPoint.c_str();
169 WCHAR volumeName[MAX_PATH + 1] = { 0 };
170 WCHAR fileSystemName[MAX_PATH + 1] = { 0 };
171 DWORD serialNumber = 0;
172 DWORD maxComponentLen = 0;
173 DWORD fileSystemFlags = 0;
174
175 if (GetVolumeInformationW(auxMountPoint, volumeName, sizeof(volumeName),
176 &serialNumber, &maxComponentLen, &fileSystemFlags,
177 fileSystemName, sizeof(fileSystemName)))
178 {
179 if (!wcscmp(fileSystemName, L"NTFS"))
180 {
181 return FS_NTFS;
182 }
183 if (!wcscmp(fileSystemName, L"exFAT"))
184 {
185 return FS_EXFAT;
186 }
187 if (!wcscmp(fileSystemName, L"FAT32"))
188 {
189 return FS_FAT32;
190 }
191 }
192 #endif
193 return FS_UNKNOWN;
194 }
195
isControlChar(unsigned char c) const196 bool FileSystemAccess::isControlChar(unsigned char c) const
197 {
198 return (c <= '\x1F' || c == '\x7F');
199 }
200
201 // Group different filesystems types in families, according to its restricted charsets
islocalfscompatible(unsigned char c,bool,FileSystemType) const202 bool FileSystemAccess::islocalfscompatible(unsigned char c, bool, FileSystemType) const
203 {
204 return c >= ' ' && !strchr("\\/:?\"<>|*", c);
205 }
206
getFilesystemType(const LocalPath & dstPath) const207 FileSystemType FileSystemAccess::getFilesystemType(const LocalPath& dstPath) const
208 {
209 // first get "valid" path (no last leaf name, in case it is not in the FS?)
210 LocalPath validPath = dstPath;
211
212 if (!validPath.endsInSeparator(*this))
213 {
214 size_t leafIndex = validPath.getLeafnameByteIndex(*this);
215 if (leafIndex > 0)
216 validPath.truncate(leafIndex);
217 }
218
219 return getlocalfstype(validPath);
220 }
221
222 // replace characters that are not allowed in local fs names with a %xx escape sequence
escapefsincompatible(string * name,FileSystemType fileSystemType) const223 void FileSystemAccess::escapefsincompatible(string* name, FileSystemType fileSystemType) const
224 {
225 if (!name->compare(".."))
226 {
227 name->replace(0, 2, "%2e%2e");
228 return;
229 }
230 if (!name->compare("."))
231 {
232 name->replace(0, 1, "%2e");
233 return;
234 }
235
236 char buf[4];
237 size_t utf8seqsize = 0;
238 size_t i = 0;
239 unsigned char c = '0';
240 while (i < name->size())
241 {
242 c = static_cast<unsigned char>((*name)[i]);
243 utf8seqsize = Utils::utf8SequenceSize(c);
244 assert (utf8seqsize);
245 if (utf8seqsize == 1 && !islocalfscompatible(c, true, fileSystemType))
246 {
247 const char incompatibleChar = name->at(i);
248 sprintf(buf, "%%%02x", c);
249 name->replace(i, 1, buf);
250 LOG_debug << "Escape incompatible character for filesystem type "
251 << fstypetostring(fileSystemType)
252 << ", replace '" << std::string(&incompatibleChar, 1) << "' by '" << buf << "'\n";
253 }
254 i += utf8seqsize;
255 }
256 }
257
unescapefsincompatible(string * name,FileSystemType fileSystemType) const258 void FileSystemAccess::unescapefsincompatible(string *name, FileSystemType fileSystemType) const
259 {
260 if (!name->compare("%2e%2e"))
261 {
262 name->replace(0, 6, "..");
263 return;
264 }
265 if (!name->compare("%2e"))
266 {
267 name->replace(0, 3, ".");
268 return;
269 }
270
271 for (int i = int(name->size()) - 2; i-- > 0; )
272 {
273 // conditions for unescaping: %xx must be well-formed
274 if ((*name)[i] == '%' && islchex((*name)[i + 1]) && islchex((*name)[i + 2]))
275 {
276 char c = static_cast<char>((MegaClient::hexval((*name)[i + 1]) << 4) + MegaClient::hexval((*name)[i + 2]));
277
278 if (!islocalfscompatible(static_cast<unsigned char>(c), false, fileSystemType))
279 {
280 std::string incompatibleChar = name->substr(i, 3);
281 name->replace(i, 3, &c, 1);
282 LOG_debug << "Unescape incompatible character for filesystem type "
283 << fstypetostring(fileSystemType)
284 << ", replace '" << incompatibleChar << "' by '" << name->substr(i, 1) << "'\n";
285 }
286 }
287 }
288 }
289
getPathSeparator()290 const char *FileSystemAccess::getPathSeparator()
291 {
292 #if defined (__linux__) || defined (__ANDROID__) || defined (__APPLE__) || defined (USE_IOS)
293 return "/";
294 #elif defined(_WIN32) || defined(WINDOWS_PHONE)
295 return "\\";
296 #else
297 // Default case
298 LOG_warn << "No path separator found";
299 return "\\/";
300 #endif
301 }
302
303 // escape forbidden characters, then convert to local encoding
name2local(string * filename,FileSystemType fsType) const304 void FileSystemAccess::name2local(string* filename, FileSystemType fsType) const
305 {
306 assert(filename);
307
308 escapefsincompatible(filename, fsType);
309
310 string t = *filename;
311
312 path2local(&t, filename);
313 }
314
normalize(string * filename) const315 void FileSystemAccess::normalize(string* filename) const
316 {
317 if (!filename) return;
318
319 const char* cfilename = filename->c_str();
320 size_t fnsize = filename->size();
321 string result;
322
323 for (size_t i = 0; i < fnsize; )
324 {
325 // allow NUL bytes between valid UTF-8 sequences
326 if (!cfilename[i])
327 {
328 result.append("", 1);
329 i++;
330 continue;
331 }
332
333 const char* substring = cfilename + i;
334 char* normalized = (char*)utf8proc_NFC((uint8_t*)substring);
335
336 if (!normalized)
337 {
338 filename->clear();
339 return;
340 }
341
342 result.append(normalized);
343 free(normalized);
344
345 i += strlen(substring);
346 }
347
348 *filename = std::move(result);
349 }
350
351 // convert from local encoding, then unescape escaped forbidden characters
local2name(string * filename,FileSystemType fsType) const352 void FileSystemAccess::local2name(string *filename, FileSystemType fsType) const
353 {
354 assert(filename);
355
356 string t = *filename;
357
358 local2path(&t, filename);
359
360 unescapefsincompatible(filename, fsType);
361 }
362
fsShortname(LocalPath & localname)363 std::unique_ptr<LocalPath> FileSystemAccess::fsShortname(LocalPath& localname)
364 {
365 LocalPath s;
366 if (getsname(localname, s))
367 {
368 return ::mega::make_unique<LocalPath>(std::move(s));
369 }
370 return nullptr;
371 }
372
373 // default DirNotify: no notification available
DirNotify(const LocalPath & clocalbasepath,const LocalPath & cignore)374 DirNotify::DirNotify(const LocalPath& clocalbasepath, const LocalPath& cignore)
375 {
376 localbasepath = clocalbasepath;
377 ignore = cignore;
378
379 mFailed = 1;
380 mFailReason = "Not initialized";
381 mErrorCount = 0;
382 sync = NULL;
383 }
384
385
setFailed(int errCode,const string & reason)386 void DirNotify::setFailed(int errCode, const string& reason)
387 {
388 std::lock_guard<std::mutex> g(mMutex);
389 mFailed = errCode;
390 mFailReason = reason;
391 }
392
getFailed(string & reason)393 int DirNotify::getFailed(string& reason)
394 {
395 if (mFailed)
396 {
397 reason = mFailReason;
398 }
399 return mFailed;
400 }
401
402
403 // notify base LocalNode + relative path/filename
notify(notifyqueue q,LocalNode * l,LocalPath && path,bool immediate)404 void DirNotify::notify(notifyqueue q, LocalNode* l, LocalPath&& path, bool immediate)
405 {
406 // We may be executing on a thread here so we can't access the LocalNode data structures. Queue everything, and
407 // filter when the notifications are processed. Also, queueing it here is faster than logging the decision anyway.
408
409 Notification n;
410 n.timestamp = immediate ? 0 : Waiter::ds;
411 n.localnode = l;
412 n.path = std::move(path);
413 notifyq[q].pushBack(std::move(n));
414
415 #ifdef ENABLE_SYNC
416 if (q == DirNotify::DIREVENTS || q == DirNotify::EXTRA)
417 {
418 sync->client->syncactivity = true;
419 }
420 #endif
421
422 }
423
424 // default: no fingerprint
fsfingerprint() const425 fsfp_t DirNotify::fsfingerprint() const
426 {
427 return 0;
428 }
429
fsstableids() const430 bool DirNotify::fsstableids() const
431 {
432 return true;
433 }
434
newdirnotify(LocalPath & localpath,LocalPath & ignore,Waiter *)435 DirNotify* FileSystemAccess::newdirnotify(LocalPath& localpath, LocalPath& ignore, Waiter*)
436 {
437 return new DirNotify(localpath, ignore);
438 }
439
FileAccess(Waiter * waiter)440 FileAccess::FileAccess(Waiter *waiter)
441 {
442 this->waiter = waiter;
443 this->isAsyncOpened = false;
444 this->numAsyncReads = 0;
445 }
446
~FileAccess()447 FileAccess::~FileAccess()
448 {
449 // All AsyncIOContext objects must be deleted before
450 assert(!numAsyncReads && !isAsyncOpened);
451 }
452
453 // open file for reading
fopen(LocalPath & name)454 bool FileAccess::fopen(LocalPath& name)
455 {
456 nonblocking_localname.editStringDirect()->resize(1);
457 updatelocalname(name);
458
459 return sysstat(&mtime, &size);
460 }
461
isfolder(LocalPath & name)462 bool FileAccess::isfolder(LocalPath& name)
463 {
464 fopen(name);
465 return (type == FOLDERNODE);
466 }
467
468 // check if size and mtime are unchanged, then open for reading
openf()469 bool FileAccess::openf()
470 {
471 if (nonblocking_localname.empty())
472 {
473 // file was not opened in nonblocking mode
474 return true;
475 }
476
477 m_time_t curr_mtime;
478 m_off_t curr_size;
479 if (!sysstat(&curr_mtime, &curr_size))
480 {
481 LOG_warn << "Error opening sync file handle (sysstat) "
482 << curr_mtime << " - " << mtime
483 << curr_size << " - " << size;
484 return false;
485 }
486
487 if (curr_mtime != mtime || curr_size != size)
488 {
489 mtime = curr_mtime;
490 size = curr_size;
491 retry = false;
492 return false;
493 }
494
495 return sysopen();
496 }
497
closef()498 void FileAccess::closef()
499 {
500 if (!nonblocking_localname.empty())
501 {
502 sysclose();
503 }
504 }
505
asyncopfinished(void * param)506 void FileAccess::asyncopfinished(void *param)
507 {
508 Waiter *waiter = (Waiter *)param;
509 if (waiter)
510 {
511 waiter->notify();
512 }
513 }
514
asyncfopen(LocalPath & f)515 AsyncIOContext *FileAccess::asyncfopen(LocalPath& f)
516 {
517 nonblocking_localname.editStringDirect()->resize(1);
518 updatelocalname(f);
519
520 LOG_verbose << "Async open start";
521 AsyncIOContext *context = newasynccontext();
522 context->op = AsyncIOContext::OPEN;
523 context->access = AsyncIOContext::ACCESS_READ;
524
525 context->buffer = (byte *)f.editStringDirect()->data();
526 context->len = static_cast<unsigned>(f.editStringDirect()->size());
527 context->waiter = waiter;
528 context->userCallback = asyncopfinished;
529 context->userData = waiter;
530 context->pos = size;
531 context->fa = this;
532
533 context->failed = !sysstat(&mtime, &size);
534 context->retry = this->retry;
535 context->finished = true;
536 context->userCallback(context->userData);
537 return context;
538 }
539
asyncopenf()540 bool FileAccess::asyncopenf()
541 {
542 numAsyncReads++;
543 if (nonblocking_localname.empty())
544 {
545 return true;
546 }
547
548 if (isAsyncOpened)
549 {
550 return true;
551 }
552
553 m_time_t curr_mtime = 0;
554 m_off_t curr_size = 0;
555 if (!sysstat(&curr_mtime, &curr_size))
556 {
557 LOG_warn << "Error opening async file handle (sysstat) "
558 << curr_mtime << " - " << mtime
559 << curr_size << " - " << size;
560 return false;
561 }
562
563 if (curr_mtime != mtime || curr_size != size)
564 {
565 mtime = curr_mtime;
566 size = curr_size;
567 retry = false;
568 return false;
569 }
570
571 LOG_debug << "Opening async file handle for reading";
572 bool result = sysopen(true);
573 if (result)
574 {
575 isAsyncOpened = true;
576 }
577 else
578 {
579 LOG_warn << "Error opening async file handle (sysopen)";
580 }
581 return result;
582 }
583
asyncclosef()584 void FileAccess::asyncclosef()
585 {
586 numAsyncReads--;
587 if (isAsyncOpened && !numAsyncReads)
588 {
589 LOG_debug << "Closing async file handle";
590 isAsyncOpened = false;
591 sysclose();
592 }
593 }
594
asyncfopen(LocalPath & f,bool read,bool write,m_off_t pos)595 AsyncIOContext *FileAccess::asyncfopen(LocalPath& f, bool read, bool write, m_off_t pos)
596 {
597 LOG_verbose << "Async open start";
598 AsyncIOContext *context = newasynccontext();
599 context->op = AsyncIOContext::OPEN;
600 context->access = AsyncIOContext::ACCESS_NONE
601 | (read ? AsyncIOContext::ACCESS_READ : 0)
602 | (write ? AsyncIOContext::ACCESS_WRITE : 0);
603
604 context->buffer = (byte *)f.editStringDirect()->data();
605 context->len = static_cast<unsigned>(f.editStringDirect()->size());
606 context->waiter = waiter;
607 context->userCallback = asyncopfinished;
608 context->userData = waiter;
609 context->pos = pos;
610 context->fa = this;
611
612 asyncsysopen(context);
613 return context;
614 }
615
asyncsysopen(AsyncIOContext * context)616 void FileAccess::asyncsysopen(AsyncIOContext *context)
617 {
618 context->failed = true;
619 context->retry = false;
620 context->finished = true;
621 if (context->userCallback)
622 {
623 context->userCallback(context->userData);
624 }
625 }
626
asyncfread(string * dst,unsigned len,unsigned pad,m_off_t pos)627 AsyncIOContext *FileAccess::asyncfread(string *dst, unsigned len, unsigned pad, m_off_t pos)
628 {
629 LOG_verbose << "Async read start";
630 dst->resize(len + pad);
631
632 AsyncIOContext *context = newasynccontext();
633 context->op = AsyncIOContext::READ;
634 context->pos = pos;
635 context->len = len;
636 context->pad = pad;
637 context->buffer = (byte *)dst->data();
638 context->waiter = waiter;
639 context->userCallback = asyncopfinished;
640 context->userData = waiter;
641 context->fa = this;
642
643 if (!asyncopenf())
644 {
645 LOG_err << "Error in asyncopenf";
646 context->failed = true;
647 context->retry = this->retry;
648 context->finished = true;
649 context->userCallback(context->userData);
650 return context;
651 }
652
653 asyncsysread(context);
654 return context;
655 }
656
asyncsysread(AsyncIOContext * context)657 void FileAccess::asyncsysread(AsyncIOContext *context)
658 {
659 context->failed = true;
660 context->retry = false;
661 context->finished = true;
662 if (context->userCallback)
663 {
664 context->userCallback(context->userData);
665 }
666 }
667
asyncfwrite(const byte * data,unsigned len,m_off_t pos)668 AsyncIOContext *FileAccess::asyncfwrite(const byte* data, unsigned len, m_off_t pos)
669 {
670 LOG_verbose << "Async write start";
671
672 AsyncIOContext *context = newasynccontext();
673 context->op = AsyncIOContext::WRITE;
674 context->pos = pos;
675 context->len = len;
676 context->buffer = (byte *)data;
677 context->waiter = waiter;
678 context->userCallback = asyncopfinished;
679 context->userData = waiter;
680 context->fa = this;
681
682 asyncsyswrite(context);
683 return context;
684 }
685
asyncsyswrite(AsyncIOContext * context)686 void FileAccess::asyncsyswrite(AsyncIOContext *context)
687 {
688 context->failed = true;
689 context->retry = false;
690 context->finished = true;
691 if (context->userCallback)
692 {
693 context->userCallback(context->userData);
694 }
695 }
696
newasynccontext()697 AsyncIOContext *FileAccess::newasynccontext()
698 {
699 return new AsyncIOContext();
700 }
701
fread(string * dst,unsigned len,unsigned pad,m_off_t pos)702 bool FileAccess::fread(string* dst, unsigned len, unsigned pad, m_off_t pos)
703 {
704 if (!openf())
705 {
706 return false;
707 }
708
709 bool r;
710
711 dst->resize(len + pad);
712
713 if ((r = sysread((byte*)dst->data(), len, pos)))
714 {
715 memset((char*)dst->data() + len, 0, pad);
716 }
717
718 closef();
719
720 return r;
721 }
722
frawread(byte * dst,unsigned len,m_off_t pos,bool caller_opened)723 bool FileAccess::frawread(byte* dst, unsigned len, m_off_t pos, bool caller_opened)
724 {
725 if (!caller_opened && !openf())
726 {
727 return false;
728 }
729
730 bool r = sysread(dst, len, pos);
731
732 if (!caller_opened)
733 {
734 closef();
735 }
736
737 return r;
738 }
739
AsyncIOContext()740 AsyncIOContext::AsyncIOContext()
741 {
742 op = NONE;
743 pos = 0;
744 len = 0;
745 pad = 0;
746 buffer = NULL;
747 waiter = NULL;
748 access = ACCESS_NONE;
749
750 userCallback = NULL;
751 userData = NULL;
752 finished = false;
753 failed = false;
754 retry = false;
755 }
756
~AsyncIOContext()757 AsyncIOContext::~AsyncIOContext()
758 {
759 finish();
760
761 // AsyncIOContext objects must be deleted before the FileAccess object
762 if (op == AsyncIOContext::READ)
763 {
764 fa->asyncclosef();
765 }
766 }
767
finish()768 void AsyncIOContext::finish()
769 {
770 if (!finished)
771 {
772 while (!finished)
773 {
774 waiter->init(NEVER);
775 waiter->wait();
776 }
777
778 // We could have been consumed and external event
779 waiter->notify();
780 }
781 }
782
FileInputStream(FileAccess * fileAccess)783 FileInputStream::FileInputStream(FileAccess *fileAccess)
784 {
785 this->fileAccess = fileAccess;
786 this->offset = 0;
787 }
788
size()789 m_off_t FileInputStream::size()
790 {
791 return fileAccess->size;
792 }
793
read(byte * buffer,unsigned size)794 bool FileInputStream::read(byte *buffer, unsigned size)
795 {
796 if (!buffer)
797 {
798 if ((offset + size) <= fileAccess->size)
799 {
800 offset += size;
801 return true;
802 }
803
804 LOG_warn << "Invalid seek on FileInputStream";
805 return false;
806 }
807
808 if (fileAccess->frawread(buffer, size, offset, true))
809 {
810 offset += size;
811 return true;
812 }
813
814 LOG_warn << "Invalid read on FileInputStream";
815 return false;
816 }
817
editStringDirect() const818 const std::string* LocalPath::editStringDirect() const
819 {
820 // this function for compatibiltiy while converting to use LocalPath class. TODO: phase out this function
821 return const_cast<std::string*>(&localpath);
822 }
823
editStringDirect()824 std::string* LocalPath::editStringDirect()
825 {
826 // this function for compatibiltiy while converting to use LocalPath class. TODO: phase out this function
827 return const_cast<std::string*>(&localpath);
828 }
829
empty() const830 bool LocalPath::empty() const
831 {
832 return localpath.empty();
833 }
834
lastpartlocal(const FileSystemAccess & fsaccess) const835 size_t LocalPath::lastpartlocal(const FileSystemAccess& fsaccess) const
836 {
837 return fsaccess.lastpartlocal(&localpath);
838 }
839
append(const LocalPath & additionalPath)840 void LocalPath::append(const LocalPath& additionalPath)
841 {
842 localpath.append(additionalPath.localpath);
843 }
844
appendWithSeparator(const LocalPath & additionalPath,bool separatorAlways,const string & localseparator)845 void LocalPath::appendWithSeparator(const LocalPath& additionalPath, bool separatorAlways, const string& localseparator)
846 {
847 if (separatorAlways || localpath.size())
848 {
849 // still have to be careful about appending a \ to F:\ for example, on windows, which produces an invalid path
850 if ( localpath.size() < localseparator.size() ||
851 memcmp(localpath.data() + localpath.size() - localseparator.size(),
852 localseparator.data(), localseparator.size()) )
853 {
854 localpath.append(localseparator);
855 }
856 }
857
858 localpath.append(additionalPath.localpath);
859 }
860
prependWithSeparator(const LocalPath & additionalPath,const string & localseparator)861 void LocalPath::prependWithSeparator(const LocalPath& additionalPath, const string& localseparator)
862 {
863 // no additional separator if there is already one after
864 if (localpath.size() >= localseparator.size() && memcmp(localpath.data(), localseparator.data(), localseparator.size()))
865 {
866 // no additional separator if there is already one before
867 if (additionalPath.editStringDirect()->size() < localseparator.size() ||
868 memcmp(additionalPath.editStringDirect()->data() + additionalPath.editStringDirect()->size() - localseparator.size(), localseparator.data(), localseparator.size()))
869 {
870 localpath.insert(0, localseparator);
871 }
872 }
873 localpath.insert(0, additionalPath.localpath);
874 }
875
trimNonDriveTrailingSeparator(const FileSystemAccess & fsaccess)876 void LocalPath::trimNonDriveTrailingSeparator(const FileSystemAccess& fsaccess)
877 {
878 if (endsInSeparator(fsaccess))
879 {
880 // ok so the last character is a directory separator. But don't remove it for eg. F:\ on windows
881 #ifdef WIN32
882 if (localpath.size() > 2 * fsaccess.localseparator.size() && !memcmp(localpath.data() + localpath.size() - 2 * fsaccess.localseparator.size(), L":", fsaccess.localseparator.size()))
883 {
884 return;
885 }
886 #endif
887
888 localpath.resize((int(localpath.size()) & -int(fsaccess.localseparator.size())) - fsaccess.localseparator.size());
889 }
890 }
891
findNextSeparator(size_t & separatorBytePos,const FileSystemAccess & fsaccess) const892 bool LocalPath::findNextSeparator(size_t& separatorBytePos, const FileSystemAccess& fsaccess) const
893 {
894 for (;;)
895 {
896 separatorBytePos = localpath.find(fsaccess.localseparator, separatorBytePos);
897 if (separatorBytePos == string::npos) return false;
898 if (separatorBytePos % fsaccess.localseparator.size() == 0) return true;
899 separatorBytePos++;
900 }
901 }
902
findPrevSeparator(size_t & separatorBytePos,const FileSystemAccess & fsaccess) const903 bool LocalPath::findPrevSeparator(size_t& separatorBytePos, const FileSystemAccess& fsaccess) const
904 {
905 for (;;)
906 {
907 separatorBytePos = localpath.rfind(fsaccess.localseparator, separatorBytePos);
908 if (separatorBytePos == string::npos) return false;
909 if (separatorBytePos % fsaccess.localseparator.size() == 0) return true;
910 separatorBytePos--;
911 }
912 }
913
endsInSeparator(const FileSystemAccess & fsaccess) const914 bool LocalPath::endsInSeparator(const FileSystemAccess& fsaccess) const
915 {
916 return localpath.size() >= fsaccess.localseparator.size()
917 && !memcmp(localpath.data() + (int(localpath.size()) & -int(fsaccess.localseparator.size())) - fsaccess.localseparator.size(),
918 fsaccess.localseparator.data(),
919 fsaccess.localseparator.size());
920 }
921
getLeafnameByteIndex(const FileSystemAccess & fsaccess) const922 size_t LocalPath::getLeafnameByteIndex(const FileSystemAccess& fsaccess) const
923 {
924 // todo: take utf8 two byte characters into account
925 size_t p = localpath.size();
926 p -= localpath.size() % fsaccess.localseparator.size(); // just in case on windows
927 while (p && (p -= fsaccess.localseparator.size()))
928 {
929 if (!memcmp(localpath.data() + p, fsaccess.localseparator.data(), fsaccess.localseparator.size()))
930 {
931 p += fsaccess.localseparator.size();
932 break;
933 }
934 }
935 return p;
936 }
937
backEqual(size_t bytePos,const LocalPath & compareTo) const938 bool LocalPath::backEqual(size_t bytePos, const LocalPath& compareTo) const
939 {
940 auto n = compareTo.localpath.size();
941 return bytePos + n == localpath.size() && !memcmp(compareTo.localpath.data(), localpath.data() + bytePos, n);
942 }
943
subpathFrom(size_t bytePos) const944 LocalPath LocalPath::subpathFrom(size_t bytePos) const
945 {
946 return LocalPath::fromLocalname(localpath.substr(bytePos));
947 }
948
949
ensureWinExtendedPathLenPrefix()950 void LocalPath::ensureWinExtendedPathLenPrefix()
951 {
952 #if defined(_WIN32) && !defined(WINDOWS_PHONE)
953 localpath.append("", 1);
954 if (!PathIsRelativeW((LPWSTR)localpath.c_str()) && ((localpath.size() < 4) || memcmp(localpath.data(), L"\\\\", 4)))
955 localpath.insert(0, (const char*)(const wchar_t*)L"\\\\?\\", 8);
956 localpath.resize(localpath.size() - 1);
957 #endif
958 }
959
substrTo(size_t bytePos) const960 string LocalPath::substrTo(size_t bytePos) const
961 {
962 return localpath.substr(0, bytePos);
963 }
964
toPath(const FileSystemAccess & fsaccess) const965 string LocalPath::toPath(const FileSystemAccess& fsaccess) const
966 {
967 string path;
968 fsaccess.local2path(const_cast<string*>(&localpath), &path); // todo: const correctness for local2path etc
969 return path;
970 }
971
toName(const FileSystemAccess & fsaccess,FileSystemType fsType) const972 string LocalPath::toName(const FileSystemAccess& fsaccess, FileSystemType fsType) const
973 {
974 string name = localpath;
975 fsaccess.local2name(&name, fsType);
976 return name;
977 }
978
979
fromPath(const string & path,const FileSystemAccess & fsaccess)980 LocalPath LocalPath::fromPath(const string& path, const FileSystemAccess& fsaccess)
981 {
982 LocalPath p;
983 fsaccess.path2local(&path, &p.localpath);
984 return p;
985 }
986
fromName(string path,const FileSystemAccess & fsaccess,FileSystemType fsType)987 LocalPath LocalPath::fromName(string path, const FileSystemAccess& fsaccess, FileSystemType fsType)
988 {
989 fsaccess.name2local(&path, fsType);
990 return fromLocalname(path);
991 }
992
fromLocalname(string path)993 LocalPath LocalPath::fromLocalname(string path)
994 {
995 LocalPath p;
996 p.localpath = std::move(path);
997 return p;
998 }
999
tmpNameLocal(const FileSystemAccess & fsaccess)1000 LocalPath LocalPath::tmpNameLocal(const FileSystemAccess& fsaccess)
1001 {
1002 LocalPath lp;
1003 fsaccess.tmpnamelocal(lp);
1004 return lp;
1005 }
1006
isContainingPathOf(const LocalPath & path,const FileSystemAccess & fsaccess)1007 bool LocalPath::isContainingPathOf(const LocalPath& path, const FileSystemAccess& fsaccess)
1008 {
1009 return path.localpath.size() >= localpath.size()
1010 && !memcmp(path.localpath.data(), localpath.data(), localpath.size())
1011 && (path.localpath.size() == localpath.size() ||
1012 !memcmp(path.localpath.data() + localpath.size(), fsaccess.localseparator.data(), fsaccess.localseparator.size()) ||
1013 (localpath.size() >= fsaccess.localseparator.size() &&
1014 !memcmp(path.localpath.data() + localpath.size() - fsaccess.localseparator.size(), fsaccess.localseparator.data(), fsaccess.localseparator.size())));
1015 }
1016
ScopedLengthRestore(LocalPath & p)1017 ScopedLengthRestore::ScopedLengthRestore(LocalPath& p)
1018 : path(p)
1019 , length(path.getLength())
1020 {
1021 }
~ScopedLengthRestore()1022 ScopedLengthRestore::~ScopedLengthRestore()
1023 {
1024 path.setLength(length);
1025 };
1026
1027 } // namespace
1028