Skip to content
1 change: 1 addition & 0 deletions FirebaseDatabase/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# 12.7.0
- [fixed] Fix `Fatal Exception: FirebaseDatabasePersistenceFailure`. (#4493)
- [fixed] Concurrency crash in FView. (#15514)

# 11.9.0
Expand Down
38 changes: 29 additions & 9 deletions FirebaseDatabase/Sources/Persistence/FLevelDBStorageEngine.m
Original file line number Diff line number Diff line change
Expand Up @@ -977,17 +977,40 @@ - (id)deserializePrimitive:(NSData *)data {
}

+ (void)ensureDir:(NSString *)path markAsDoNotBackup:(BOOL)markAsDoNotBackup {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
BOOL success =
[[NSFileManager defaultManager] createDirectoryAtPath:path
withIntermediateDirectories:YES
attributes:nil
error:&error];

// Create the directory if it doesn't exist. This call is a no-op if it
// already exists.
BOOL success = [fileManager createDirectoryAtPath:path
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (!success) {
@throw [NSException
exceptionWithName:@"FailedToCreatePersistenceDir"
reason:@"Failed to create persistence directory."
userInfo:@{@"path" : path}];
userInfo:@{
@"path" : path,
@"error" : error ?: [NSNull null]
}];
}

// Now, ensure the file protection attribute is set. This will apply it
// whether the directory was just created or already existed. Note, this
// attribute has no effect on simulators.
NSDictionary *attributes = @{
NSFileProtectionKey :
NSFileProtectionCompleteUntilFirstUserAuthentication
};
success = [fileManager setAttributes:attributes
ofItemAtPath:path
error:&error];
if (!success) {
FFWarn(@"I-RDB076036",
@"Failed to set file protection attribute on persistence "
@"directory: %@",
error);
}

if (markAsDoNotBackup) {
Expand All @@ -1000,9 +1023,6 @@ + (void)ensureDir:(NSString *)path markAsDoNotBackup:(BOOL)markAsDoNotBackup {
@"I-RDB076035",
@"Failed to mark firebase database folder as do not backup: %@",
error);
[NSException raise:@"Error marking as do not backup"
format:@"Failed to mark folder %@ as do not backup",
firebaseDirURL];
}
}
}
Expand Down
50 changes: 49 additions & 1 deletion FirebaseDatabase/Tests/Unit/FLevelDBStorageEngineTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
#import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
#import "FirebaseDatabase/Tests/Helpers/FTestHelpers.h"

@interface FLevelDBStorageEngineTests : XCTestCase
@interface FLevelDBStorageEngine (Tests)
+ (void)ensureDir:(NSString *)path markAsDoNotBackup:(BOOL)markAsDoNotBackup;
@end

@interface FLevelDBStorageEngineTests : XCTestCase
@end

@implementation FLevelDBStorageEngineTests
Expand Down Expand Up @@ -685,4 +688,49 @@ - (void)testRemoveTrackedQueryRemovesTrackedQueryKeys {
([NSSet setWithArray:@[ @"b", @"c" ]]));
}

- (void)testEnsureDirSetsCorrectFileProtection {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't do anything on simulator, and I was unsuccessful at testing on a real device (ran into signing issues with CocoaPods and SwiftPM target needs a host app).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No action needed.

NSString *testDirName =
[NSString stringWithFormat:@"fdb_persistence_test_%lu", (unsigned long)arc4random()];
NSString *testPath = [NSTemporaryDirectory() stringByAppendingPathComponent:testDirName];
NSFileManager *fileManager = [NSFileManager defaultManager];

// --- Test creation ---
[fileManager removeItemAtPath:testPath error:nil];
[FLevelDBStorageEngine ensureDir:testPath markAsDoNotBackup:NO];

NSError *error = nil;
NSDictionary<NSFileAttributeKey, id> *attributes = [fileManager attributesOfItemAtPath:testPath
error:&error];
XCTAssertNil(error, @"Failed to get attributes of directory: %@", error);

#if !TARGET_OS_SIMULATOR
// On a physical device, file protection should be set.
XCTAssertEqualObjects(attributes[NSFileProtectionKey],
NSFileProtectionCompleteUntilFirstUserAuthentication);
#else
XCTAssertNil(attributes[NSFileProtectionKey]);
#endif

// --- Test update on existing directory ---
#if !TARGET_OS_SIMULATOR
// This part of the test is only relevant on devices where file protection is supported.
[fileManager removeItemAtPath:testPath error:nil];
NSDictionary *initialAttributes = @{NSFileProtectionKey : NSFileProtectionNone};
XCTAssertTrue([fileManager createDirectoryAtPath:testPath
withIntermediateDirectories:YES
attributes:initialAttributes
error:&error],
@"Failed to create directory for update test: %@", error);

[FLevelDBStorageEngine ensureDir:testPath markAsDoNotBackup:NO];

attributes = [fileManager attributesOfItemAtPath:testPath error:&error];
XCTAssertNil(error, @"Failed to get attributes after update: %@", error);
XCTAssertEqualObjects(attributes[NSFileProtectionKey],
NSFileProtectionCompleteUntilFirstUserAuthentication);
#endif // !TARGET_OS_SIMULATOR

// Clean up
[fileManager removeItemAtPath:testPath error:nil];
}
@end
Loading