From 987726df6ce200f28f83a085ab5a3edba0c4ce3c Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 26 Mar 2019 17:45:12 -0400 Subject: [PATCH 1/2] Temp files. --- Signal/src/AppDelegate.m | 3 + SignalServiceKit/src/Util/OWSFileSystem.h | 3 +- SignalServiceKit/src/Util/OWSFileSystem.m | 70 +++++++++++++++++++++-- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index c626266ba..26cbab7ae 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -636,6 +636,9 @@ static NSTimeInterval launchStartedAt; // be called _before_ we become active. [self clearAllNotificationsAndRestoreBadgeCount]; + // On every activation, clear old temp directories. + ClearOldTemporaryDirectories(); + OWSLogInfo(@"applicationDidBecomeActive completed."); } diff --git a/SignalServiceKit/src/Util/OWSFileSystem.h b/SignalServiceKit/src/Util/OWSFileSystem.h index 0bbad4c92..aa4dcc90d 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.h +++ b/SignalServiceKit/src/Util/OWSFileSystem.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN // unless the temp data may need to be accessed while the device is locked. NSString *OWSTemporaryDirectory(void); NSString *OWSTemporaryDirectoryAccessibleAfterFirstAuth(void); +void ClearOldTemporaryDirectories(void); @interface OWSFileSystem : NSObject diff --git a/SignalServiceKit/src/Util/OWSFileSystem.m b/SignalServiceKit/src/Util/OWSFileSystem.m index 183e9edb6..327a04d28 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.m +++ b/SignalServiceKit/src/Util/OWSFileSystem.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "OWSFileSystem.h" @@ -336,12 +336,26 @@ NS_ASSUME_NONNULL_BEGIN @end +#pragma mark - + NSString *OWSTemporaryDirectory(void) { - NSString *dirName = @"ows_temp"; - NSString *dirPath = [NSTemporaryDirectory() stringByAppendingPathComponent:dirName]; - BOOL success = [OWSFileSystem ensureDirectoryExists:dirPath fileProtectionType:NSFileProtectionComplete]; - OWSCAssert(success); + static NSString *dirPath; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *dirName = [NSString stringWithFormat:@"ows_temp_%@", NSUUID.UUID.UUIDString]; + dirPath = [NSTemporaryDirectory() stringByAppendingPathComponent:dirName]; + BOOL success = [OWSFileSystem ensureDirectoryExists:dirPath fileProtectionType:NSFileProtectionComplete]; + OWSCAssert(success); + + // On launch, clear old temp directories. + // + // NOTE: ClearOldTemporaryDirectoriesSync() will call this function + // OWSTemporaryDirectory(), but there's no risk of deadlock; + // ClearOldTemporaryDirectories() calls ClearOldTemporaryDirectoriesSync() + // after a long delay. + ClearOldTemporaryDirectories(); + }); return dirPath; } @@ -354,4 +368,50 @@ NSString *OWSTemporaryDirectoryAccessibleAfterFirstAuth(void) return dirPath; } +void ClearOldTemporaryDirectoriesSync(void) +{ + // Ignore the "current" temp directory. + NSString *currentTempDirName = OWSTemporaryDirectory().lastPathComponent; + + NSString *dirPath = NSTemporaryDirectory(); + NSError *error; + NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:&error]; + if (error) { + OWSCFailDebug(@"contentsOfDirectoryAtPath error: %@", error); + return; + } + for (NSString *fileName in fileNames) { + if (!CurrentAppContext().isAppForegroundAndActive) { + // Abort if app not active. + return; + } + if ([fileName isEqualToString:currentTempDirName]) { + continue; + } + if (![fileName hasPrefix:@"ows_temp"]) { + continue; + } + NSString *filePath = [dirPath stringByAppendingPathComponent:fileName]; + OWSLogVerbose(@"Clearing old temp directory: %@", filePath); + if (![OWSFileSystem deleteFile:filePath]) { + // This can happen if the app launches before the phone is unlocked. + // Clean up will occur when app becomes active. + OWSLogWarn(@"Could not delete old temp directory: %@", filePath); + } + } +} + +// NOTE: We need to call this method on launch _and_ every time the app becomes active, +// since file protection may prevent it from succeeding in the background. +void ClearOldTemporaryDirectories(void) +{ + // We use the lowest priority queue for this, and wait N seconds + // to avoid interfering with app startup. + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.f * NSEC_PER_SEC)), + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), + ^{ + ClearOldTemporaryDirectoriesSync(); + }); +} + NS_ASSUME_NONNULL_END From dd54b40bedbf7731cf88016c7a973afb0dedcf32 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Wed, 27 Mar 2019 13:15:26 -0400 Subject: [PATCH 2/2] Respond to CR. --- SignalServiceKit/src/Util/OWSFileSystem.m | 29 +++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/SignalServiceKit/src/Util/OWSFileSystem.m b/SignalServiceKit/src/Util/OWSFileSystem.m index 327a04d28..36d4a8f07 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.m +++ b/SignalServiceKit/src/Util/OWSFileSystem.m @@ -5,6 +5,7 @@ #import "OWSFileSystem.h" #import "OWSError.h" #import "TSConstants.h" +#import NS_ASSUME_NONNULL_BEGIN @@ -373,6 +374,7 @@ void ClearOldTemporaryDirectoriesSync(void) // Ignore the "current" temp directory. NSString *currentTempDirName = OWSTemporaryDirectory().lastPathComponent; + NSDate *thresholdDate = CurrentAppContext().appLaunchTime; NSString *dirPath = NSTemporaryDirectory(); NSError *error; NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:&error]; @@ -388,11 +390,30 @@ void ClearOldTemporaryDirectoriesSync(void) if ([fileName isEqualToString:currentTempDirName]) { continue; } + + NSString *filePath = [dirPath stringByAppendingPathComponent:fileName]; + + // Delete files with either: + // + // a) "ows_temp" name prefix. + // b) modified time before app launch time. if (![fileName hasPrefix:@"ows_temp"]) { - continue; + NSError *error; + NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error]; + if (!attributes || error) { + // This is fine; the file may have been deleted since we found it. + OWSLogError(@"Could not get attributes of file or directory at: %@", filePath); + continue; + } + // Don't delete files which were created in the last N minutes. + NSDate *creationDate = attributes.fileModificationDate; + if ([creationDate isAfterDate:thresholdDate]) { + OWSLogInfo(@"Skipping file due to age: %f", fabs([creationDate timeIntervalSinceNow])); + continue; + } } - NSString *filePath = [dirPath stringByAppendingPathComponent:fileName]; - OWSLogVerbose(@"Clearing old temp directory: %@", filePath); + + OWSLogVerbose(@"Removing temp file or directory: %@", filePath); if (![OWSFileSystem deleteFile:filePath]) { // This can happen if the app launches before the phone is unlocked. // Clean up will occur when app becomes active. @@ -407,7 +428,7 @@ void ClearOldTemporaryDirectories(void) { // We use the lowest priority queue for this, and wait N seconds // to avoid interfering with app startup. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.f * NSEC_PER_SEC)), + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.f * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ ClearOldTemporaryDirectoriesSync();