Fix NSFileManager thread safety

This commit is contained in:
Frederik Seiffert 2023-07-19 12:33:15 +02:00 committed by Frederik Seiffert
parent d643dad9b3
commit f0e33a48d5
3 changed files with 106 additions and 48 deletions

View file

@ -1,3 +1,9 @@
2023-07-25 Frederik Seiffert <frederik@algoriddim.com>
* Headers/Foundation/NSFileManager.h:
* Source/NSFileManager.m:
Fixed NSFileManager thread safety.
2023-06-10 Riccardo Mottola <rm@gnu.org>
* Tests/base/NSURL/Helpers/Launch.h:

View file

@ -216,7 +216,6 @@ GS_EXPORT_CLASS
#if GS_EXPOSE(NSFileManager)
@private
id<NSFileManagerDelegate> _delegate;
NSString *_lastError;
#endif
#if GS_NONFRAGILE
#else

View file

@ -60,6 +60,7 @@
#import "Foundation/NSURL.h"
#import "Foundation/NSValue.h"
#import "GSPrivate.h"
#import "GSPThread.h"
#import "GNUstepBase/NSString+GNUstepBase.h"
#import "GNUstepBase/NSTask+GNUstepBase.h"
@ -269,6 +270,19 @@ static Class GSAttrDictionaryClass = 0;
@end
gs_thread_key_t thread_last_error_key;
static void GS_WINAPI
exitedThread(void *lastErrorPtr)
{
#if __has_feature(objc_arc)
NSString *oldError = (__bridge_transfer NSString*)lastErrorPtr;
#pragma unused(oldError)
#else
RELEASE(lastErrorPtr);
#endif
}
@interface NSFileManager (PrivateMethods)
@ -304,6 +318,10 @@ static Class GSAttrDictionaryClass = 0;
fromPath: (NSString*) fromPath
toPath: (NSString*) toPath;
/* Thread-local last error variable. */
- (NSString*) _lastError;
- (void) _setLastError: (NSString*)error;
/* A convenience method to return an NSError object.
* If the _lastError message is set, this creates an NSError using
* that message in the NSCocoaErrorDomain, otherwise it used the
@ -358,11 +376,12 @@ static NSStringEncoding defaultEncoding;
{
defaultEncoding = [NSString defaultCStringEncoding];
GSAttrDictionaryClass = [GSAttrDictionary class];
GS_THREAD_KEY_INIT(thread_last_error_key, exitedThread);
}
- (void) dealloc
{
TEST_RELEASE(_lastError);
[self _setLastError: nil];
[super dealloc];
}
@ -439,7 +458,7 @@ static NSStringEncoding defaultEncoding;
str = [NSString stringWithFormat:
@"Unable to change NSFileOwnerAccountID to '%"PRIuPTR"' - %@",
num, [NSError _last]];
ASSIGN(_lastError, str);
[self _setLastError: str];
}
}
else
@ -481,7 +500,7 @@ static NSStringEncoding defaultEncoding;
str = [NSString stringWithFormat:
@"Unable to change NSFileOwnerAccountName to '%@' - %@",
str, [NSError _last]];
ASSIGN(_lastError, str);
[self _setLastError: str];
}
}
}
@ -505,7 +524,7 @@ static NSStringEncoding defaultEncoding;
str = [NSString stringWithFormat:
@"Unable to change NSFileGroupOwnerAccountID to '%"PRIuPTR"' - %@",
num, [NSError _last]];
ASSIGN(_lastError, str);
[self _setLastError: str];
}
}
else if ((str = [attributes fileGroupOwnerAccountName]) != nil
@ -545,7 +564,7 @@ static NSStringEncoding defaultEncoding;
str = [NSString stringWithFormat:
@"Unable to change NSFileGroupOwnerAccountName to '%@' - %@",
str, [NSError _last]];
ASSIGN(_lastError, str);
[self _setLastError: str];
}
}
#endif /* _WIN32 */
@ -559,7 +578,7 @@ static NSStringEncoding defaultEncoding;
str = [NSString stringWithFormat:
@"Unable to change NSFilePosixPermissions to '%o' - %@",
(unsigned)num, [NSError _last]];
ASSIGN(_lastError, str);
[self _setLastError: str];
}
}
@ -629,7 +648,7 @@ static NSStringEncoding defaultEncoding;
str = [NSString stringWithFormat:
@"Unable to change NSFileCreationDate to '%@' - %@",
date, [NSError _last]];
ASSIGN(_lastError, str);
[self _setLastError: str];
}
}
@ -678,7 +697,7 @@ static NSStringEncoding defaultEncoding;
str = [NSString stringWithFormat:
@"Unable to change NSFileModificationDate to '%@' - %@",
date, [NSError _last]];
ASSIGN(_lastError, str);
[self _setLastError: str];
}
}
@ -805,7 +824,7 @@ static NSStringEncoding defaultEncoding;
NSDirectoryEnumerator *direnum;
NSString *path;
DESTROY(_lastError);
[self _setLastError: nil];
if (![[url scheme] isEqualToString: @"file"])
{
@ -908,8 +927,8 @@ static NSStringEncoding defaultEncoding;
NSString *path;
BOOL shouldRecurse;
BOOL shouldSkipHidden;
DESTROY(_lastError);
[self _setLastError: nil];
if (![[url scheme] isEqualToString: @"file"])
{
@ -953,7 +972,8 @@ static NSStringEncoding defaultEncoding;
{
NSArray *result;
DESTROY(_lastError);
[self _setLastError: nil];
result = [self directoryContentsAtPath: path];
if (error != NULL)
@ -982,7 +1002,8 @@ static NSStringEncoding defaultEncoding;
{
BOOL result = NO;
DESTROY(_lastError);
[self _setLastError: nil];
if (YES == flag)
{
NSEnumerator *paths = [[path pathComponents] objectEnumerator];
@ -1013,8 +1034,8 @@ static NSStringEncoding defaultEncoding;
}
else
{
result = NO;
ASSIGN(_lastError, @"Could not create directory - intermediate path did not exist or was not a directory");
result = NO;
[self _setLastError: @"Could not create directory - intermediate path did not exist or was not a directory"];
}
}
@ -1062,7 +1083,7 @@ static NSStringEncoding defaultEncoding;
/* This is consistent with MacOSX - just return NO for an invalid path. */
if ([path length] == 0)
{
ASSIGN(_lastError, @"no path given");
[self _setLastError: @"no path given"];
return NO;
}
@ -1080,7 +1101,7 @@ static NSStringEncoding defaultEncoding;
e = [NSString stringWithFormat:
@"path %@ exists ... cannot create", path];
}
ASSIGN(_lastError, e);
[self _setLastError: e];
return NO;
}
else
@ -1125,7 +1146,7 @@ static NSStringEncoding defaultEncoding;
e = [NSString stringWithFormat:
@"Could not create '%@' - '%@'",
path, [NSError _last]];
ASSIGN(_lastError, e);
[self _setLastError: e];
return NO;
}
}
@ -1156,7 +1177,7 @@ static NSStringEncoding defaultEncoding;
/* This is consistent with MacOSX - just return NO for an invalid path. */
if ([path length] == 0)
{
ASSIGN(_lastError, @"no path given");
[self _setLastError: @"no path given"];
return NO;
}
@ -1322,8 +1343,7 @@ static NSStringEncoding defaultEncoding;
if ([[destination stringByAppendingString: @"/"]
hasPrefix: [source stringByAppendingString: @"/"]])
{
ASSIGN(_lastError,
@"Could not copy - destination is a descendant of source");
[self _setLastError: @"Could not copy - destination is a descendant of source"];
return NO;
}
@ -1332,7 +1352,7 @@ static NSStringEncoding defaultEncoding;
if ([self createDirectoryAtPath: destination attributes: attrs] == NO)
{
return [self _proceedAccordingToHandler: handler
forError: _lastError
forError: [self _lastError]
inPath: destination
fromPath: source
toPath: destination];
@ -1385,7 +1405,8 @@ static NSStringEncoding defaultEncoding;
{
BOOL result;
DESTROY(_lastError);
[self _setLastError: nil];
result = [self copyPath: src toPath: dst handler: nil];
if (error != NULL)
@ -1455,7 +1476,7 @@ static NSStringEncoding defaultEncoding;
if (sourceIsDir && [[destination stringByAppendingString: @"/"]
hasPrefix: [source stringByAppendingString: @"/"]])
{
ASSIGN(_lastError, @"Could not move - destination is a descendant of source");
[self _setLastError: @"Could not move - destination is a descendant of source"];
return NO;
}
@ -1499,7 +1520,8 @@ static NSStringEncoding defaultEncoding;
{
BOOL result;
DESTROY(_lastError);
[self _setLastError: nil];
result = [self movePath: src toPath: dst handler: nil];
if (error != NULL)
@ -1570,14 +1592,14 @@ static NSStringEncoding defaultEncoding;
if ([[destination stringByAppendingString: @"/"]
hasPrefix: [source stringByAppendingString: @"/"]])
{
ASSIGN(_lastError, @"Could not link - destination is a descendant of source");
[self _setLastError: @"Could not link - destination is a descendant of source"];
return NO;
}
if ([self createDirectoryAtPath: destination attributes: attrs] == NO)
{
return [self _proceedAccordingToHandler: handler
forError: _lastError
forError: [self _lastError]
inPath: destination
fromPath: source
toPath: destination];
@ -1624,7 +1646,7 @@ static NSStringEncoding defaultEncoding;
[self changeFileAttributes: attrs atPath: destination];
return YES;
#else
ASSIGN(_lastError, @"Links not supported on this platform");
[self _setLastError: @"Links not supported on this platform"];
return NO;
#endif
}
@ -1646,7 +1668,7 @@ static NSStringEncoding defaultEncoding;
lpath = [self fileSystemRepresentationWithPath: path];
if (lpath == 0 || *lpath == 0)
{
ASSIGN(_lastError, @"Could not remove - no path");
[self _setLastError: @"Could not remove - no path"];
return NO;
}
else
@ -1741,7 +1763,8 @@ static NSStringEncoding defaultEncoding;
{
BOOL result;
DESTROY(_lastError);
[self _setLastError: nil];
result = [self removeFileAtPath: path handler: nil];
if (error != NULL)
@ -1767,7 +1790,8 @@ static NSStringEncoding defaultEncoding;
{
BOOL result;
DESTROY(_lastError);
[self _setLastError: nil];
result = [self createSymbolicLinkAtPath: path pathContent: destPath];
if (error != NULL)
@ -1797,7 +1821,7 @@ static NSStringEncoding defaultEncoding;
if (lpath == 0 || *lpath == _NUL)
{
ASSIGN(_lastError, @"no path given");
[self _setLastError: @"no path given"];
return NO;
}
@ -1875,7 +1899,7 @@ static NSStringEncoding defaultEncoding;
if (lpath == 0 || *lpath == _NUL)
{
ASSIGN(_lastError, @"no path given");
[self _setLastError: @"no path given"];
return NO;
}
@ -1932,7 +1956,7 @@ static NSStringEncoding defaultEncoding;
if (lpath == 0 || *lpath == _NUL)
{
ASSIGN(_lastError, @"no path given");
[self _setLastError: @"no path given"];
return NO;
}
@ -1974,7 +1998,7 @@ static NSStringEncoding defaultEncoding;
if (lpath == 0 || *lpath == _NUL)
{
ASSIGN(_lastError, @"no path given");
[self _setLastError: @"no path given"];
return NO;
}
@ -2033,7 +2057,7 @@ static NSStringEncoding defaultEncoding;
if (lpath == 0 || *lpath == _NUL)
{
ASSIGN(_lastError, @"no path given");
[self _setLastError: @"no path given"];
return NO;
}
@ -2227,7 +2251,8 @@ static NSStringEncoding defaultEncoding;
{
NSDictionary *d;
DESTROY(_lastError);
[self _setLastError: nil];
d = [GSAttrDictionaryClass attributesAt: path traverseLink: NO];
if (error != NULL)
@ -2368,7 +2393,7 @@ static NSStringEncoding defaultEncoding;
return [NSDictionary dictionaryWithObjects: values forKeys: keys count: 5];
#else
GSOnceMLog(@"NSFileManager", @"no support for filesystem attributes");
ASSIGN(_lastError, @"no support for filesystem attributes");
[self _setLastError: @"no support for filesystem attributes"];
return nil;
#endif
#endif /* _WIN32 */
@ -2522,7 +2547,7 @@ static NSStringEncoding defaultEncoding;
return (symlink(oldpath, newpath) == 0);
#else
ASSIGN(_lastError, @"symbolic links not supported on this system");
[self _setLastError: @"symbolic links not supported on this system"];
return NO;
#endif
}
@ -2547,7 +2572,7 @@ static NSStringEncoding defaultEncoding;
return nil;
}
#else
ASSIGN(_lastError, @"symbolic links not supported on this system");
[self _setLastError: @"symbolic links not supported on this system"];
return nil;
#endif
}
@ -3334,7 +3359,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
if (dirOK == NO)
{
if (![self _proceedAccordingToHandler: handler
forError: _lastError
forError: [self _lastError]
inPath: destinationFile
fromPath: sourceFile
toPath: destinationFile])
@ -3398,7 +3423,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
s = [NSString stringWithFormat: @"cannot copy file type '%@'",
fileType];
ASSIGN(_lastError, s);
[self _setLastError: s];
NSDebugLog(@"%@: %@", sourceFile, s);
continue;
}
@ -3441,7 +3466,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
attributes: attributes] == NO)
{
if ([self _proceedAccordingToHandler: handler
forError: _lastError
forError: [self _lastError]
inPath: destinationFile
fromPath: sourceFile
toPath: destinationFile] == NO)
@ -3502,7 +3527,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
LEAVE_POOL
return result;
#else
ASSIGN(_lastError, @"Links not supported on this platform");
[self _setLastError: @"Links not supported on this platform"];
return NO;
#endif
}
@ -3552,6 +3577,28 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
return NO;
}
- (NSString*) _lastError
{
#if __has_feature(objc_arc)
return (__bridge NSString*)GS_THREAD_KEY_GET(thread_last_error_key);
#else
return (NSString*)GS_THREAD_KEY_GET(thread_last_error_key);
#endif
}
- (void) _setLastError: (NSString*)error
{
#if __has_feature(objc_arc)
NSString *oldError = (__bridge_transfer NSString*)GS_THREAD_KEY_GET(thread_last_error_key);
GS_THREAD_KEY_SET(thread_last_error_key, (__bridge_retained void*)error);
#pragma unused(oldError)
#else
NSString *oldError = (NSString*)GS_THREAD_KEY_GET(thread_last_error_key);
GS_THREAD_KEY_SET(thread_last_error_key, RETAIN(error));
RELEASE(oldError);
#endif
}
- (NSError*) _errorFrom: (NSString *)fromPath to: (NSString *)toPath
{
NSError *error;
@ -3559,10 +3606,11 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
NSString *message;
NSString *domain;
NSInteger code;
NSString *lastError = [self _lastError];
if (_lastError)
if (lastError)
{
message = _lastError;
message = lastError;
domain = NSCocoaErrorDomain;
code = 0;
}
@ -3599,7 +3647,12 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
error = [NSError errorWithDomain: domain
code: code
userInfo: errorInfo];
DESTROY(_lastError);
if (lastError)
{
[self _setLastError: nil];
}
return error;
}