|
1 | 1 | #import "SentryFileManager.h"
|
| 2 | +#import "FileUtils.h" |
2 | 3 | #import "SentryAppState.h"
|
3 | 4 | #import "SentryDataCategoryMapper.h"
|
4 | 5 | #import "SentryDateUtils.h"
|
|
22 | 23 |
|
23 | 24 | #pragma mark - Helper Methods
|
24 | 25 |
|
25 |
| -BOOL |
26 |
| -isErrorPathTooLong(NSError *error) |
27 |
| -{ |
28 |
| - NSError *underlyingError = NULL; |
29 |
| - if (@available(macOS 11.3, iOS 14.5, watchOS 7.4, tvOS 14.5, *)) { |
30 |
| - underlyingError = error.underlyingErrors.firstObject; |
31 |
| - } |
32 |
| - if (underlyingError == NULL) { |
33 |
| - id errorInUserInfo = [error.userInfo valueForKey:NSUnderlyingErrorKey]; |
34 |
| - if (errorInUserInfo && [errorInUserInfo isKindOfClass:[NSError class]]) { |
35 |
| - underlyingError = errorInUserInfo; |
36 |
| - } |
37 |
| - } |
38 |
| - if (underlyingError == NULL) { |
39 |
| - underlyingError = error; |
40 |
| - } |
41 |
| - BOOL isEnameTooLong |
42 |
| - = underlyingError.domain == NSPOSIXErrorDomain && underlyingError.code == ENAMETOOLONG; |
43 |
| - // On older OS versions the error code is NSFileWriteUnknown |
44 |
| - // Reference: https://developer.apple.com/forums/thread/128927?answerId=631839022#631839022 |
45 |
| - BOOL isUnknownError = underlyingError.domain == NSCocoaErrorDomain |
46 |
| - && underlyingError.code == NSFileWriteUnknownError; |
47 |
| - |
48 |
| - return isEnameTooLong || isUnknownError; |
49 |
| -} |
50 |
| - |
51 |
| -BOOL |
52 |
| -createDirectoryIfNotExists(NSString *path, NSError **error) |
53 |
| -{ |
54 |
| - BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:path |
55 |
| - withIntermediateDirectories:YES |
56 |
| - attributes:nil |
57 |
| - error:error]; |
58 |
| - if (success) { |
59 |
| - return YES; |
60 |
| - } |
61 |
| - |
62 |
| - if (isErrorPathTooLong(*error)) { |
63 |
| - SENTRY_LOG_FATAL(@"Failed to create directory, path is too long: %@", path); |
64 |
| - } |
65 |
| - *error = NSErrorFromSentryErrorWithUnderlyingError(kSentryErrorFileIO, |
66 |
| - [NSString stringWithFormat:@"Failed to create the directory at path %@.", path], *error); |
67 |
| - return NO; |
68 |
| -} |
69 |
| - |
70 | 26 | /**
|
71 | 27 | * @warning This is called from a `@synchronized` context in instance methods, but doesn't require
|
72 | 28 | * that when calling from other static functions. Make sure you pay attention to where this is used
|
@@ -634,116 +590,6 @@ - (BOOL)isDirectory:(NSString *)path
|
634 | 590 | return [NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isDir] && isDir;
|
635 | 591 | }
|
636 | 592 |
|
637 |
| -/** |
638 |
| - * @note This method must be statically accessible because it will be called during app launch, |
639 |
| - * before any instance of ``SentryFileManager`` exists, and so wouldn't be able to access this path |
640 |
| - * from an objc property on it like the other paths. |
641 |
| - */ |
642 |
| -NSString *_Nullable sentryStaticCachesPath(void) |
643 |
| -{ |
644 |
| - static NSString *_Nullable sentryStaticCachesPath = nil; |
645 |
| - static dispatch_once_t onceToken; |
646 |
| - dispatch_once(&onceToken, ^{ |
647 |
| - // We request the users cache directory from Foundation. |
648 |
| - // For iOS apps and macOS apps with sandboxing, this path will be scoped for the current |
649 |
| - // app. For macOS apps without sandboxing, this path is not scoped and will be shared |
650 |
| - // between all apps. |
651 |
| - NSString *_Nullable cachesDirectory |
652 |
| - = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) |
653 |
| - .firstObject; |
654 |
| - if (cachesDirectory == nil) { |
655 |
| - SENTRY_LOG_WARN(@"No caches directory location reported."); |
656 |
| - return; |
657 |
| - } |
658 |
| - |
659 |
| - // We need to ensure our own scoped directory so that this path is not shared between other |
660 |
| - // apps on the same system. |
661 |
| - NSString *_Nullable scopedCachesDirectory = sentryGetScopedCachesDirectory(cachesDirectory); |
662 |
| - if (!scopedCachesDirectory) { |
663 |
| - SENTRY_LOG_WARN(@"Failed to get scoped static caches directory."); |
664 |
| - return; |
665 |
| - } |
666 |
| - sentryStaticCachesPath = scopedCachesDirectory; |
667 |
| - SENTRY_LOG_DEBUG(@"Using static cache directory: %@", sentryStaticCachesPath); |
668 |
| - }); |
669 |
| - return sentryStaticCachesPath; |
670 |
| -} |
671 |
| - |
672 |
| -NSString *_Nullable sentryGetScopedCachesDirectory(NSString *cachesDirectory) |
673 |
| -{ |
674 |
| -#if !TARGET_OS_OSX |
675 |
| - // iOS apps are always sandboxed, therefore we can just early-return with the provided caches |
676 |
| - // directory. |
677 |
| - return cachesDirectory; |
678 |
| -#else |
679 |
| - |
680 |
| - // For macOS apps, we need to ensure our own sandbox so that this path is not shared between |
681 |
| - // all apps that ship the SDK. |
682 |
| - |
683 |
| - // We can not use the SentryNSProcessInfoWrapper here because this method is called before |
684 |
| - // the SentryDependencyContainer is initialized. |
685 |
| - NSProcessInfo *processInfo = [NSProcessInfo processInfo]; |
686 |
| - |
687 |
| - // Only apps running in a sandboxed environment have the `APP_SANDBOX_CONTAINER_ID` set as a |
688 |
| - // process environment variable. Reference implementation: |
689 |
| - // https://github.com/realm/realm-js/blob/a03127726939f08f608edbdb2341605938f25708/packages/realm/binding/apple/platform.mm#L58-L74 |
690 |
| - BOOL isSandboxed = processInfo.environment[@"APP_SANDBOX_CONTAINER_ID"] != nil; |
691 |
| - |
692 |
| - // The bundle identifier is used to create a unique cache directory for the app. |
693 |
| - // If the bundle identifier is not available, we use the name of the executable. |
694 |
| - // Note: `SentryCrash.getBundleName` is using `CFBundleName` to create a scoped directory. |
695 |
| - // That value can be absent, therefore we use a more stable approach here. |
696 |
| - NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; |
697 |
| - NSString *lastPathComponent = [[[NSBundle mainBundle] executablePath] lastPathComponent]; |
698 |
| - |
699 |
| - // Due to `NSProcessInfo` and `NSBundle` not being mockable in unit tests, we extract only the |
700 |
| - // logic to a separate function. |
701 |
| - return sentryBuildScopedCachesDirectoryPath( |
702 |
| - cachesDirectory, isSandboxed, bundleIdentifier, lastPathComponent); |
703 |
| -#endif |
704 |
| -} |
705 |
| - |
706 |
| -NSString *_Nullable sentryBuildScopedCachesDirectoryPath(NSString *cachesDirectory, |
707 |
| - BOOL isSandboxed, NSString *_Nullable bundleIdentifier, NSString *_Nullable lastPathComponent) |
708 |
| -{ |
709 |
| - // If the app is sandboxed, we can just use the provided caches directory. |
710 |
| - if (isSandboxed) { |
711 |
| - return cachesDirectory; |
712 |
| - } |
713 |
| - |
714 |
| - // If the macOS app is not sandboxed, we need to manually create a scoped cache |
715 |
| - // directory. The cache path must be unique an stable over app launches, therefore we |
716 |
| - // can not use any changing identifier. |
717 |
| - SENTRY_LOG_DEBUG( |
718 |
| - @"App is not sandboxed, extending default cache directory with bundle identifier."); |
719 |
| - NSString *_Nullable identifier = bundleIdentifier; |
720 |
| - if (identifier == nil) { |
721 |
| - SENTRY_LOG_WARN(@"No bundle identifier found, using main bundle executable name."); |
722 |
| - identifier = lastPathComponent; |
723 |
| - } else if (identifier.length == 0) { |
724 |
| - SENTRY_LOG_WARN(@"Bundle identifier exists but is zero length, using main bundle " |
725 |
| - @"executable name."); |
726 |
| - identifier = lastPathComponent; |
727 |
| - } |
728 |
| - |
729 |
| - // If neither the bundle identifier nor the executable name are available, we can't |
730 |
| - // create a unique and stable cache directory. |
731 |
| - // We do not fall back to any default path, because it could be shared with other apps |
732 |
| - // and cause leaks impacting other apps. |
733 |
| - if (identifier == nil) { |
734 |
| - SENTRY_LOG_ERROR(@"No bundle identifier found, cannot create cache directory."); |
735 |
| - return nil; |
736 |
| - } |
737 |
| - |
738 |
| - // It's unlikely that the executable name will be zero length, but we'll cover this case anyways |
739 |
| - if (identifier.length == 0) { |
740 |
| - SENTRY_LOG_ERROR(@"Executable name was zero length."); |
741 |
| - return nil; |
742 |
| - } |
743 |
| - |
744 |
| - return [cachesDirectory stringByAppendingPathComponent:identifier]; |
745 |
| -} |
746 |
| - |
747 | 593 | NSString *_Nullable sentryStaticBasePath(void)
|
748 | 594 | {
|
749 | 595 | static NSString *_Nullable sentryStaticBasePath = nil;
|
|
0 commit comments