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