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