diff options
author | Leon Zandman <lzandman> | 2021-06-14 12:16:42 +0300 |
---|---|---|
committer | Brecht Van Lommel <brecht@blender.org> | 2021-06-14 13:26:07 +0300 |
commit | 5add6f2ed93773e1231e3d52dc9fd963b3bfb9e7 (patch) | |
tree | 9398faaa87e12ac1e1e73ec879a4f480d24dc1ec /source/blender/blenlib | |
parent | c9dc55301cd7903b7ef7c045d337ada29aa809a1 (diff) |
Fix T87867: file open dialog triggers OneDrive file downloads on macOS
Until OneDrive supports macOS's native placeholder file implementation, detect
the com.microsoft.OneDrive.RecallOnOpen extended file attribute.
Differential Revision: https://developer.blender.org/D11466
Diffstat (limited to 'source/blender/blenlib')
-rw-r--r-- | source/blender/blenlib/intern/storage_apple.mm | 90 |
1 files changed, 86 insertions, 4 deletions
diff --git a/source/blender/blenlib/intern/storage_apple.mm b/source/blender/blenlib/intern/storage_apple.mm index 2a4bbffa60e..8af98d61ecb 100644 --- a/source/blender/blenlib/intern/storage_apple.mm +++ b/source/blender/blenlib/intern/storage_apple.mm @@ -24,10 +24,15 @@ */ #import <Foundation/Foundation.h> +#include <string> +#include <sys/xattr.h> #include "BLI_fileops.h" #include "BLI_path_util.h" +/* Extended file attribute used by OneDrive to mark placeholder files. */ +static const char *ONEDRIVE_RECALLONOPEN_ATTRIBUTE = "com.microsoft.OneDrive.RecallOnOpen"; + /** * \param r_targetpath: Buffer for the target path an alias points to. * \return Whether the file at the input path is an alias. @@ -66,6 +71,67 @@ bool BLI_file_alias_target(const char *filepath, char r_targetpath[FILE_MAXDIR]) return true; } +/** + * Checks if the given string of listxattr() attributes contains a specific attribute. + * + * \param attributes: a string of null-terminated listxattr() attributes. + * \param search_attribute: the attribute to search for. + * \return 'true' when the attribute is found, otherwise 'false'. + */ +static bool find_attribute(const std::string &attributes, const char *search_attribute) +{ + /* Attributes is a list of consecutive null-terminated strings. */ + const char *end = attributes.data() + attributes.size(); + for (const char *item = attributes.data(); item < end; item += strlen(item) + 1) { + if (STREQ(item, search_attribute)) { + return true; + } + } + + return false; +} + +/** + * Checks if the file is merely a placeholder for a OneDrive file that hasn't yet been downloaded. + * + * \param path: the path of the file. + * \return 'true' when the file is a OneDrive placeholder, otherwise 'false'. + */ +static bool test_onedrive_file_is_placeholder(const char *path) +{ + /* Note: Currently only checking for the "com.microsoft.OneDrive.RecallOnOpen" extended file + * attribute. In theory this attribute can also be set on files that aren't located inside a + * OneDrive folder. Maybe additional checks are required? */ + + /* Get extended file attributes */ + ssize_t size = listxattr(path, nullptr, 0, XATTR_NOFOLLOW); + if (size < 1) { + return false; + } + + std::string attributes(size, '\0'); + size = listxattr(path, attributes.data(), size, XATTR_NOFOLLOW); + /* In case listxattr() has failed the second time it's called. */ + if (size < 1) { + return false; + } + + /* Check for presence of 'com.microsoft.OneDrive.RecallOnOpen' attribute. */ + return find_attribute(attributes, ONEDRIVE_RECALLONOPEN_ATTRIBUTE); +} + +/** + * Checks if the file is marked as offline and not immediately available. + * + * \param path: the path of the file. + * \return 'true' when the file is a placeholder, otherwise 'false'. + */ +static bool test_file_is_offline(const char *path) +{ + /* Logic for additional cloud storage providers could be added in the future. */ + return test_onedrive_file_is_placeholder(path); +} + eFileAttributes BLI_file_attributes(const char *path) { int ret = 0; @@ -76,15 +142,28 @@ eFileAttributes BLI_file_attributes(const char *path) const NSURL *fileURL = [[NSURL alloc] initFileURLWithFileSystemRepresentation:path isDirectory:NO relativeToURL:nil]; - NSArray *resourceKeys = - @[ NSURLIsAliasFileKey, NSURLIsHiddenKey, NSURLIsReadableKey, NSURLIsWritableKey ]; + + /* Querying NSURLIsReadableKey and NSURLIsWritableKey keys for OneDrive placeholder files + * triggers their unwanted download. */ + NSArray *resourceKeys = nullptr; + const bool is_offline = test_file_is_offline(path); + + if (is_offline) { + resourceKeys = @[ NSURLIsAliasFileKey, NSURLIsHiddenKey ]; + } + else { + resourceKeys = + @[ NSURLIsAliasFileKey, NSURLIsHiddenKey, NSURLIsReadableKey, NSURLIsWritableKey ]; + } const NSDictionary *resourceKeyValues = [fileURL resourceValuesForKeys:resourceKeys error:nil]; const bool is_alias = [resourceKeyValues[(void)(@"@%"), NSURLIsAliasFileKey] boolValue]; const bool is_hidden = [resourceKeyValues[(void)(@"@%"), NSURLIsHiddenKey] boolValue]; - const bool is_readable = [resourceKeyValues[(void)(@"@%"), NSURLIsReadableKey] boolValue]; - const bool is_writable = [resourceKeyValues[(void)(@"@%"), NSURLIsWritableKey] boolValue]; + const bool is_readable = is_offline || + [resourceKeyValues[(void)(@"@%"), NSURLIsReadableKey] boolValue]; + const bool is_writable = is_offline || + [resourceKeyValues[(void)(@"@%"), NSURLIsWritableKey] boolValue]; if (is_alias) { ret |= FILE_ATTR_ALIAS; @@ -98,6 +177,9 @@ eFileAttributes BLI_file_attributes(const char *path) if (!is_readable) { ret |= FILE_ATTR_SYSTEM; } + if (is_offline) { + ret |= FILE_ATTR_OFFLINE; + } } return (eFileAttributes)ret; |