1 /**
2  * @file mega/filesystem.h
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 
22 #ifndef MEGA_FILESYSTEM_H
23 #define MEGA_FILESYSTEM_H 1
24 
25 #if defined (__linux__) && !defined (__ANDROID__)
26 #include <linux/magic.h>
27 #endif
28 
29 #if defined (__linux__) || defined (__ANDROID__) // __ANDROID__ is always included in __linux__
30 #include <sys/vfs.h>
31 #elif defined  (__APPLE__) || defined (USE_IOS)
32 #include <sys/mount.h>
33 #include <sys/param.h>
34 #elif defined(_WIN32) || defined(WINDOWS_PHONE)
35 #include <winsock2.h>
36 #include <Windows.h>
37 #endif
38 
39 #include "types.h"
40 #include "utils.h"
41 #include "waiter.h"
42 
43 #if defined (__linux__) && !defined (__ANDROID__)
44 // Define magic constants (for linux), in case they are not defined in headers
45 #ifndef HFS_SUPER_MAGIC
46 #define HFS_SUPER_MAGIC 0x4244
47 #endif
48 
49 #ifndef NTFS_SB_MAGIC
50 #define NTFS_SB_MAGIC   0x5346544e
51 #endif
52 
53 #elif defined (__ANDROID__)
54 // Define magic constants (for Android), in case they are not defined in headers
55 #ifndef SDCARDFS_SUPER_MAGIC
56 #define SDCARDFS_SUPER_MAGIC 0x5DCA2DF5
57 #endif
58 
59 #ifndef FUSEBLK_SUPER_MAGIC
60 #define FUSEBLK_SUPER_MAGIC  0x65735546
61 #endif
62 
63 #ifndef FUSECTL_SUPER_MAGIC
64 #define FUSECTL_SUPER_MAGIC  0x65735543
65 #endif
66 
67 #ifndef F2FS_SUPER_MAGIC
68 #define F2FS_SUPER_MAGIC 0xF2F52010
69 #endif
70 #endif
71 
72 namespace mega {
73 
74 // Enumeration for filesystem families
75 enum FileSystemType {FS_UNKNOWN = -1, FS_APFS = 0, FS_HFS = 1, FS_EXT = 2, FS_FAT32 = 3,
76                      FS_EXFAT = 4, FS_NTFS = 5, FS_FUSE = 6, FS_SDCARDFS = 7, FS_F2FS = 8};
77 
78 // generic host filesystem node ID interface
79 struct MEGA_API FsNodeId
80 {
81     virtual bool isequalto(FsNodeId*) = 0;
82 };
83 
84 typedef void (*asyncfscallback)(void *);
85 
86 struct MEGA_API AsyncIOContext
87 {
88     enum {
89         NONE, READ, WRITE, OPEN
90     };
91 
92     enum {
93         ACCESS_NONE     = 0x00,
94         ACCESS_READ     = 0x01,
95         ACCESS_WRITE    = 0x02
96     };
97 
98     AsyncIOContext();
99     virtual ~AsyncIOContext();
100     virtual void finish();
101 
102     // results
103     asyncfscallback userCallback;
104     void *userData;
105     bool finished;
106     bool failed;
107     bool retry;
108 
109     // parameters
110     int op;
111     int access;
112     m_off_t pos;
113     unsigned len;
114     unsigned pad;
115     byte *buffer;
116     Waiter *waiter;
117     FileAccess *fa;
118 };
119 
120 
121 
122 // LocalPath represents a path in the local filesystem, and wraps up common operations in a convenient fashion.
123 // On mac/linux, local paths are in utf8 but in windows local paths are utf16, that is wrapped up here.
124 
125 struct MEGA_API FileSystemAccess;
126 class MEGA_API LocalPath;
127 
128 class ScopedLengthRestore {
129     LocalPath& path;
130     size_t length;
131 public:
132     // On destruction, puts the LocalPath length back to what it was on construction of this class
133     ScopedLengthRestore(LocalPath&);
134     ~ScopedLengthRestore();
135 };
136 
137 class MEGA_API LocalPath
138 {
139     std::string localpath;
140 
141     friend class ScopedLengthRestore;
getLength()142     size_t getLength() { return localpath.size(); }
setLength(size_t length)143     void setLength(size_t length) { localpath.resize(length); }
144 
145 public:
146 
LocalPath()147     LocalPath() {}
LocalPath(std::string && s)148     explicit LocalPath(std::string&& s) : localpath(std::move(s)) {}
149 
150     std::string* editStringDirect();
151     const std::string* editStringDirect() const;
152     bool empty() const;
clear()153     void clear() { localpath.clear(); }
154     void erase(size_t pos = 0, size_t count = std::string::npos) { localpath.erase(pos, count); }
truncate(size_t bytePos)155     void truncate(size_t bytePos) { localpath.resize(bytePos); }
156     size_t lastpartlocal(const FileSystemAccess& fsaccess) const;
157     void append(const LocalPath& additionalPath);
158     void appendWithSeparator(const LocalPath& additionalPath, bool separatorAlways, const std::string& localseparator);
159     void prependWithSeparator(const LocalPath& additionalPath, const std::string& localseparator);
160     void trimNonDriveTrailingSeparator(const FileSystemAccess& fsaccess);
161     bool findNextSeparator(size_t& separatorBytePos, const FileSystemAccess& fsaccess) const;
162     bool findPrevSeparator(size_t& separatorBytePos, const FileSystemAccess& fsaccess) const;
163     bool endsInSeparator(const FileSystemAccess& fsaccess) const;
164 
165     // get the index of the leaf name.  A trailing separator is considered part of the leaf.
166     size_t getLeafnameByteIndex(const FileSystemAccess& fsaccess) const;
167     bool backEqual(size_t bytePos, const LocalPath& compareTo) const;
168     LocalPath subpathFrom(size_t bytePos) const;
169     std::string substrTo(size_t bytePos) const;
170 
171     void ensureWinExtendedPathLenPrefix();
172 
173     bool isContainingPathOf(const LocalPath& path, const FileSystemAccess& fsaccess);
174 
175     // Return a utf8 representation of the LocalPath (fsaccess is used to do the conversion)
176     // No escaping or unescaping is done.
177     std::string toPath(const FileSystemAccess& fsaccess) const;
178 
179     // Return a utf8 representation of the LocalPath, taking into account that the LocalPath
180     // may contain escaped characters that are disallowed for the filesystem.
181     // Those characters are converted back (unescaped).  fsaccess is used to do the conversion.
182     std::string toName(const FileSystemAccess& fsaccess, FileSystemType fsType = FS_UNKNOWN) const;
183 
184     // Create a Localpath from a utf8 string where no character conversions or escaping is necessary.
185     static LocalPath fromPath(const std::string& path, const FileSystemAccess& fsaccess);
186 
187     // Create a LocalPath from a utf8 string, making any character conversions (escaping) necessary
188     // for characters that are disallowed on that filesystem.  fsaccess is used to do the conversion.
189     static LocalPath fromName(std::string path, const FileSystemAccess& fsaccess, FileSystemType fsType);
190 
191     // Create a LocalPath from a string that was already converted to be appropriate for a local file path.
192     static LocalPath fromLocalname(std::string localname);
193 
194     // Generates a name for a temporary file
195     static LocalPath tmpNameLocal(const FileSystemAccess& fsaccess);
196 
197     bool operator==(const LocalPath& p) const { return localpath == p.localpath; }
198     bool operator!=(const LocalPath& p) const { return localpath != p.localpath; }
199     bool operator<(const LocalPath& p) const { return localpath < p.localpath; }
200 };
201 
202 inline LocalPath operator+(LocalPath& a, LocalPath& b)
203 {
204     LocalPath result = a;
205     result.append(b);
206     return result;
207 }
208 
209 // map a request tag with pending paths of temporary files
210 typedef map<int, vector<LocalPath> > pendingfiles_map;
211 
212 struct MEGA_API DirAccess;
213 
214 // generic host file/directory access interface
215 struct MEGA_API FileAccess
216 {
217     // file size
218     m_off_t size = 0;
219 
220     // mtime of a file opened for reading
221     m_time_t mtime = 0;
222 
223     // local filesystem record id (survives renames & moves)
224     handle fsid = 0;
225     bool fsidvalid = false;
226 
227     // type of opened path
228     nodetype_t type = TYPE_UNKNOWN;
229 
230     // if opened path is a symlink
231     bool mIsSymLink = false;
232 
233     // if the open failed, retry indicates a potentially transient reason
234     bool retry = false;
235 
236     //error code related to the last call to fopen() without parameters
237     int errorcode = 0;
238 
239     // for files "opened" in nonblocking mode, the current local filename
240     LocalPath nonblocking_localname;
241 
242     // waiter to notify on filesystem events
243     Waiter *waiter;
244 
245     // blocking mode: open for reading, writing or reading and writing.
246     // This one really does open the file, and openf(), closef() will have no effect
247     // If iteratingDir is supplied, this fopen() call must be for the directory entry being iterated by dopen()/dnext()
248     virtual bool fopen(LocalPath&, bool read, bool write, DirAccess* iteratingDir = nullptr, bool ignoreAttributes = false) = 0;
249 
250     // nonblocking open: Only prepares for opening.  Actually stats the file/folder, getting mtime, size, type.
251     // Call openf() afterwards to actually open it if required.  For folders, returns false with type==FOLDERNODE.
252     bool fopen(LocalPath&);
253 
254     // check if a local path is a folder
255     bool isfolder(LocalPath&);
256 
257     // update localname (only has an effect if operating in by-name mode)
258     virtual void updatelocalname(LocalPath&) = 0;
259 
260     // absolute position read, with NUL padding
261     bool fread(string *, unsigned, unsigned, m_off_t);
262 
263     // absolute position read to byte buffer
264     bool frawread(byte *, unsigned, m_off_t, bool caller_opened = false);
265 
266     // After a successful nonblocking fopen(), call openf() to really open the file (by localname)
267     // (this is a lazy-type approach in case we don't actually need to open the file after finding out type/size/mtime).
268     // If the size or mtime changed, it will fail.
269     bool openf();
270 
271     // After calling openf(), make sure to close the file again quickly with closef().
272     void closef();
273 
274     // absolute position write
275     virtual bool fwrite(const byte *, unsigned, m_off_t) = 0;
276 
277     FileAccess(Waiter *waiter);
278     virtual ~FileAccess();
279 
asyncavailableFileAccess280     virtual bool asyncavailable() { return false; }
281 
282     AsyncIOContext *asyncfopen(LocalPath&);
283 
284     // non-locking ops: open/close temporary hFile
285     bool asyncopenf();
286     void asyncclosef();
287 
288     AsyncIOContext *asyncfopen(LocalPath&, bool, bool, m_off_t = 0);
289     AsyncIOContext* asyncfread(string *, unsigned, unsigned, m_off_t);
290     AsyncIOContext* asyncfwrite(const byte *, unsigned, m_off_t);
291 
292 
293 protected:
294     virtual AsyncIOContext* newasynccontext();
295     static void asyncopfinished(void *param);
296     bool isAsyncOpened;
297     int numAsyncReads;
298 
299     // system-specific raw read/open/close to be provided by platform implementation.   fopen / openf / fread etc are implemented by calling these.
300     virtual bool sysread(byte *, unsigned, m_off_t) = 0;
301     virtual bool sysstat(m_time_t*, m_off_t*) = 0;
302     virtual bool sysopen(bool async = false) = 0;
303     virtual void sysclose() = 0;
304     virtual void asyncsysopen(AsyncIOContext*);
305     virtual void asyncsysread(AsyncIOContext*);
306     virtual void asyncsyswrite(AsyncIOContext*);
307 };
308 
309 struct MEGA_API InputStreamAccess
310 {
311     virtual m_off_t size() = 0;
312     virtual bool read(byte *, unsigned) = 0;
~InputStreamAccessInputStreamAccess313     virtual ~InputStreamAccess() { }
314 };
315 
316 class MEGA_API FileInputStream : public InputStreamAccess
317 {
318     FileAccess *fileAccess;
319     m_off_t offset;
320 
321 public:
322     FileInputStream(FileAccess *fileAccess);
323 
324     m_off_t size() override;
325     bool read(byte *buffer, unsigned size) override;
326 };
327 
328 // generic host directory enumeration
329 struct MEGA_API DirAccess
330 {
331     // open for scanning
332     virtual bool dopen(LocalPath*, FileAccess*, bool) = 0;
333 
334     // get next record
335     virtual bool dnext(LocalPath&, LocalPath&, bool = true, nodetype_t* = NULL) = 0;
336 
~DirAccessDirAccess337     virtual ~DirAccess() { }
338 };
339 
340 struct Notification
341 {
342     dstime timestamp;
343     LocalPath path;
344     LocalNode* localnode;
345 };
346 
347 struct NotificationDeque : ThreadSafeDeque<Notification>
348 {
replaceLocalNodePointersNotificationDeque349     void replaceLocalNodePointers(LocalNode* check, LocalNode* newvalue)
350     {
351         std::lock_guard<std::mutex> g(m);
352         for (auto& n : mNotifications)
353         {
354             if (n.localnode == check)
355             {
356                 n.localnode = newvalue;
357             }
358         }
359     }
360 };
361 
362 // generic filesystem change notification
363 struct MEGA_API DirNotify
364 {
365     typedef enum { EXTRA, DIREVENTS, RETRY, NUMQUEUES } notifyqueue;
366 
367     // notifyq[EXTRA] is like DIREVENTS, but delays its processing (for network filesystems)
368     // notifyq[DIREVENTS] is fed with filesystem changes
369     // notifyq[RETRY] receives transient errors that need to be retried
370     // Thread safe so that a separate thread can listen for filesystem notifications (for windows for now, maybe more platforms later)
371     NotificationDeque notifyq[NUMQUEUES];
372 
373 private:
374     // these next few fields may be updated by notification-reading threads
375     std::mutex mMutex;
376 
377     // set if no notification available on this platform or a permanent failure
378     // occurred
379     int mFailed;
380 
381     // reason of the permanent failure of filesystem notifications
382     string mFailReason;
383 
384 public:
385     // set if a temporary error occurred.  May be set from a thread.
386     std::atomic<int> mErrorCount;
387 
388     // thread safe setter/getters
389     void setFailed(int errCode, const string& reason);
390     int  getFailed(string& reason);
391 
392     // base path
393     LocalPath localbasepath;
394 
addnotifyDirNotify395     virtual void addnotify(LocalNode*, string*) { }
delnotifyDirNotify396     virtual void delnotify(LocalNode*) { }
397 
398     void notify(notifyqueue, LocalNode *, LocalPath&&, bool = false);
399 
400     // filesystem fingerprint
401     virtual fsfp_t fsfingerprint() const;
402 
403     // Returns true if the filesystem's IDs are stable (e.g. never change between mounts).
404     // This should return false for any FAT filesystem.
405     virtual bool fsstableids() const;
406 
407     // ignore this (debris folder)
408     LocalPath ignore;
409 
410     Sync *sync;
411 
412     DirNotify(const LocalPath&, const LocalPath&);
~DirNotifyDirNotify413     virtual ~DirNotify() {}
414 };
415 
416 // generic host filesystem access interface
417 struct MEGA_API FileSystemAccess : public EventTrigger
418 {
419     // local path separator, e.g. "/"
420     string localseparator;
421 
422     // waiter to notify on filesystem events
423     Waiter *waiter;
424 
425     // indicate error reports are not necessary on this call as it'll be retried in a moment if there is a continuing problem
426     bool skip_errorreport;
427 
428     /**
429      * @brief instantiate FileAccess object
430      * @param followSymLinks whether symlinks should be followed when opening a path (default: true)
431      * @return
432      */
433     virtual std::unique_ptr<FileAccess> newfileaccess(bool followSymLinks = true) = 0;
434 
435     // instantiate DirAccess object
436     virtual DirAccess* newdiraccess() = 0;
437 
438     // instantiate DirNotify object (default to periodic scanning handler if no
439     // notification configured) with given root path
440     virtual DirNotify* newdirnotify(LocalPath&, LocalPath&, Waiter*);
441 
442     // check if character is lowercase hex ASCII
443     bool islchex(char) const;
444     bool isControlChar(unsigned char c) const;
445     bool islocalfscompatible(unsigned char, bool isEscape, FileSystemType = FS_UNKNOWN) const;
446     void escapefsincompatible(string*, FileSystemType fileSystemType) const;
447 
448     FileSystemType getFilesystemType(const LocalPath& dstPath) const;
449     const char *fstypetostring(FileSystemType type) const;
450     FileSystemType getlocalfstype(const LocalPath& dstPath) const;
451     void unescapefsincompatible(string*,FileSystemType) const;
452 
453     // convert MEGA path (UTF-8) to local format
454     virtual void path2local(const string*, string*) const = 0;
455     virtual void local2path(const string*, string*) const = 0;
456 
457     // convert MEGA-formatted filename (UTF-8) to local filesystem name; escape
458     // forbidden characters using urlencode
459     void local2name(string*, FileSystemType) const;
460 
461     // convert local path to MEGA format (UTF-8) with unescaping
462     void name2local(string*, FileSystemType) const;
463 
464     // returns a const char pointer that contains the separator character for the target system
465     static const char *getPathSeparator();
466 
467     //Normalize UTF-8 string
468     void normalize(string *) const;
469 
470     // generate local temporary file name
471     virtual void tmpnamelocal(LocalPath&) const = 0;
472 
473     // obtain local secondary name
474     virtual bool getsname(LocalPath&, LocalPath&) const = 0;
475 
476     // rename file, overwrite target
477     virtual bool renamelocal(LocalPath&, LocalPath&, bool = true) = 0;
478 
479     // copy file, overwrite target, set mtime
480     virtual bool copylocal(LocalPath&, LocalPath&, m_time_t) = 0;
481 
482     // delete file
483     virtual bool unlinklocal(LocalPath&) = 0;
484 
485     // delete empty directory
486     virtual bool rmdirlocal(LocalPath&) = 0;
487 
488     // create directory, optionally hidden
489     virtual bool mkdirlocal(LocalPath&, bool = false) = 0;
490 
491     // make sure that we stay within the range of timestamps supported by the server data structures (unsigned 32-bit)
492     static void captimestamp(m_time_t*);
493 
494     // set mtime
495     virtual bool setmtimelocal(LocalPath&, m_time_t) = 0;
496 
497     // change working directory
498     virtual bool chdirlocal(LocalPath&) const = 0;
499 
500     // locate byte offset of last path component
501     virtual size_t lastpartlocal(const string*) const = 0;
502 
503     // obtain lowercased extension
504     virtual bool getextension(const LocalPath&, char*, size_t) const = 0;
505 
506     // check if synchronization is supported for a specific path
507     virtual bool issyncsupported(LocalPath&, bool* = NULL) { return true; }
508 
509     // add notification (has to be called for all directories in tree for full crossplatform support)
addnotifyFileSystemAccess510     virtual void addnotify(LocalNode*, string*) { }
511 
512     // delete notification
delnotifyFileSystemAccess513     virtual void delnotify(LocalNode*) { }
514 
515     // get the absolute path corresponding to a path
516     virtual bool expanselocalpath(LocalPath& path, LocalPath& absolutepath) = 0;
517 
518     // default permissions for new files
getdefaultfilepermissionsFileSystemAccess519     int getdefaultfilepermissions() { return 0600; }
setdefaultfilepermissionsFileSystemAccess520     void setdefaultfilepermissions(int) { }
521 
522     // default permissions for new folder
getdefaultfolderpermissionsFileSystemAccess523     int getdefaultfolderpermissions() { return 0700; }
setdefaultfolderpermissionsFileSystemAccess524     void setdefaultfolderpermissions(int) { }
525 
526     // convenience function for getting filesystem shortnames
527     std::unique_ptr<LocalPath> fsShortname(LocalPath& localpath);
528 
529     // set whenever an operation fails due to a transient condition (e.g. locking violation)
530     bool transient_error;
531 
532     // set whenever there was a global file notification error or permanent failure
533     // (this is in addition to the DirNotify-local error)
534     bool notifyerr;
535     bool notifyfailed;
536 
537     // set whenever an operation fails because the target already exists
538     bool target_exists;
539 
540     // append local operating system version information to string.
541     // Set includeArchExtraInfo to know if the app is 32 bit running on 64 bit (on windows, that is via the WOW subsystem)
osversionFileSystemAccess542     virtual void osversion(string*, bool includeArchExtraInfo) const { }
543 
544     // append id for stats
statsidFileSystemAccess545     virtual void statsid(string*) const { }
546 
547     MegaClient* client;
548 
549     FileSystemAccess();
~FileSystemAccessFileSystemAccess550     virtual ~FileSystemAccess() { }
551 };
552 } // namespace
553 
554 #endif
555