Issue #305 ... produce NSError with 516 code on copy failure because destination already exists.

This commit is contained in:
rfm 2025-01-03 16:05:09 +00:00
parent bc3d2508ee
commit 1108135232
3 changed files with 131 additions and 75 deletions

View file

@ -24,6 +24,7 @@
#import "common.h" #import "common.h"
#define EXPOSE_NSError_IVARS 1 #define EXPOSE_NSError_IVARS 1
#import "Foundation/NSDictionary.h" #import "Foundation/NSDictionary.h"
#import "Foundation/NSException.h"
#import "Foundation/NSError.h" #import "Foundation/NSError.h"
#import "Foundation/NSCoder.h" #import "Foundation/NSCoder.h"
#import "Foundation/NSArray.h" #import "Foundation/NSArray.h"
@ -32,10 +33,28 @@
@implementation NSError @implementation NSError
/* For NSFileManager we have a private method which produces an error
* with mutable userInfo so that information can be added before the
* file manager returns the error to higher level code.
*/
+ (NSError*) _error: (NSInteger)aCode
description: (NSString*)description
{
NSError *e = [self allocWithZone: NSDefaultMallocZone()];
NSMutableDictionary *m;
e = [e initWithDomain: NSCocoaErrorDomain code: aCode userInfo: nil];
m = [NSMutableDictionary allocWithZone: NSDefaultMallocZone()];
e->_userInfo = [m initWithCapacity: 3];
[m setObject: description forKey: NSLocalizedDescriptionKey];
return AUTORELEASE(e);
}
+ (id) errorWithDomain: (NSErrorDomain)aDomain + (id) errorWithDomain: (NSErrorDomain)aDomain
code: (NSInteger)aCode code: (NSInteger)aCode
userInfo: (NSDictionary*)aDictionary userInfo: (NSDictionary*)aDictionary
{ {
NSError *e = [self allocWithZone: NSDefaultMallocZone()]; NSError *e = [self allocWithZone: NSDefaultMallocZone()];
e = [e initWithDomain: aDomain code: aCode userInfo: aDictionary]; e = [e initWithDomain: aDomain code: aCode userInfo: aDictionary];

View file

@ -43,6 +43,7 @@
#import "common.h" #import "common.h"
#define EXPOSE_NSFileManager_IVARS 1 #define EXPOSE_NSFileManager_IVARS 1
#define EXPOSE_NSDirectoryEnumerator_IVARS 1 #define EXPOSE_NSDirectoryEnumerator_IVARS 1
#import "Foundation/FoundationErrors.h"
#import "Foundation/NSArray.h" #import "Foundation/NSArray.h"
#import "Foundation/NSAutoreleasePool.h" #import "Foundation/NSAutoreleasePool.h"
#import "Foundation/NSData.h" #import "Foundation/NSData.h"
@ -177,6 +178,11 @@
#define GSBINIO 0 #define GSBINIO 0
#endif #endif
@interface NSError (NSFileManager)
+ (NSError*) _error: (NSInteger)aCode
description: (NSString*)description;
@end
@interface NSDirectoryEnumerator (Local) @interface NSDirectoryEnumerator (Local)
- (id) initWithDirectoryPath: (NSString*)path - (id) initWithDirectoryPath: (NSString*)path
recurseIntoSubdirectories: (BOOL)recurse recurseIntoSubdirectories: (BOOL)recurse
@ -322,8 +328,10 @@ exitedThread(void *lastErrorPtr)
toPath: (NSString*) toPath; toPath: (NSString*) toPath;
/* Thread-local last error variable. */ /* Thread-local last error variable. */
- (NSString*) _lastError; - (NSError*) _lastError;
- (void) _setLastError: (NSString*)error; - (NSString*) _lastErrorText;
- (void) _setLastError: (NSString*)msg code: (int)code;
- (void) _setLastError: (NSString*)msg;
/* A convenience method to return an NSError object. /* A convenience method to return an NSError object.
* If the _lastError message is set, this creates an NSError using * If the _lastError message is set, this creates an NSError using
@ -388,7 +396,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
- (void) dealloc - (void) dealloc
{ {
[self _setLastError: nil]; [self _setLastError: nil code: 0];
[super dealloc]; [super dealloc];
} }
@ -831,7 +839,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
NSDirectoryEnumerator *direnum; NSDirectoryEnumerator *direnum;
NSString *path; NSString *path;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
if (![[url scheme] isEqualToString: @"file"]) if (![[url scheme] isEqualToString: @"file"])
{ {
@ -952,7 +960,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
BOOL shouldRecurse; BOOL shouldRecurse;
BOOL shouldSkipHidden; BOOL shouldSkipHidden;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
if (![[url scheme] isEqualToString: @"file"]) if (![[url scheme] isEqualToString: @"file"])
{ {
@ -996,7 +1004,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
{ {
NSArray *result; NSArray *result;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
result = [self directoryContentsAtPath: path]; result = [self directoryContentsAtPath: path];
@ -1026,7 +1034,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
{ {
BOOL result = NO; BOOL result = NO;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
if (YES == flag) if (YES == flag)
{ {
@ -1344,13 +1352,15 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
if ([self fileExistsAtPath: destination] == YES) if ([self fileExistsAtPath: destination] == YES)
{ {
[self _setLastError: @"Could not copy - destination already exists"]; [self _setLastError: @"Could not copy - destination already exists"
code: NSFileWriteFileExistsError];
return NO; return NO;
} }
attrs = [self fileAttributesAtPath: source traverseLink: NO]; attrs = [self fileAttributesAtPath: source traverseLink: NO];
if (attrs == nil) if (attrs == nil)
{ {
[self _setLastError: @"Cound not copy - destination is not readable"]; [self _setLastError: @"Could not copy - source is not available"
code: NSFileReadNoSuchFileError];
return NO; return NO;
} }
fileType = [attrs fileType]; fileType = [attrs fileType];
@ -1386,7 +1396,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
if ([self createDirectoryAtPath: destination attributes: attrs] == NO) if ([self createDirectoryAtPath: destination attributes: attrs] == NO)
{ {
if (NO == [self _proceedAccordingToHandler: handler if (NO == [self _proceedAccordingToHandler: handler
forError: [self _lastError] forError: [self _lastErrorText]
inPath: destination inPath: destination
fromPath: source fromPath: source
toPath: destination]) toPath: destination])
@ -1458,7 +1468,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
{ {
BOOL result; BOOL result;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
result = [self copyPath: src toPath: dst handler: nil]; result = [self copyPath: src toPath: dst handler: nil];
@ -1578,7 +1588,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
{ {
BOOL result; BOOL result;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
result = [self movePath: src toPath: dst handler: nil]; result = [self movePath: src toPath: dst handler: nil];
@ -1657,7 +1667,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
if ([self createDirectoryAtPath: destination attributes: attrs] == NO) if ([self createDirectoryAtPath: destination attributes: attrs] == NO)
{ {
return [self _proceedAccordingToHandler: handler return [self _proceedAccordingToHandler: handler
forError: [self _lastError] forError: [self _lastErrorText]
inPath: destination inPath: destination
fromPath: source fromPath: source
toPath: destination]; toPath: destination];
@ -1821,7 +1831,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
{ {
BOOL result; BOOL result;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
result = [self removeFileAtPath: path handler: nil]; result = [self removeFileAtPath: path handler: nil];
@ -1848,7 +1858,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
{ {
BOOL result; BOOL result;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
result = [self createSymbolicLinkAtPath: path pathContent: destPath]; result = [self createSymbolicLinkAtPath: path pathContent: destPath];
@ -2309,7 +2319,7 @@ static gs_mutex_t classLock = GS_MUTEX_INIT_STATIC;
{ {
NSDictionary *d; NSDictionary *d;
[self _setLastError: nil]; [self _setLastError: nil code: 0];
d = [GSAttrDictionaryClass attributesAt: path traverseLink: NO]; d = [GSAttrDictionaryClass attributesAt: path traverseLink: NO];
@ -3429,7 +3439,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
if (dirOK == NO) if (dirOK == NO)
{ {
if (![self _proceedAccordingToHandler: handler if (![self _proceedAccordingToHandler: handler
forError: [self _lastError] forError: [self _lastErrorText]
inPath: destinationFile inPath: destinationFile
fromPath: sourceFile fromPath: sourceFile
toPath: destinationFile]) toPath: destinationFile])
@ -3535,7 +3545,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
attributes: attributes] == NO) attributes: attributes] == NO)
{ {
if ([self _proceedAccordingToHandler: handler if ([self _proceedAccordingToHandler: handler
forError: [self _lastError] forError: [self _lastErrorText]
inPath: destinationFile inPath: destinationFile
fromPath: sourceFile fromPath: sourceFile
toPath: destinationFile] == NO) toPath: destinationFile] == NO)
@ -3646,80 +3656,109 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
return NO; return NO;
} }
- (NSString*) _lastError - (NSError*) _lastError
{ {
#if __has_feature(objc_arc) #if __has_feature(objc_arc)
return (__bridge NSString*)GS_THREAD_KEY_GET(thread_last_error_key); return (__bridge NSError*)GS_THREAD_KEY_GET(thread_last_error_key);
#else #else
return (NSString*)GS_THREAD_KEY_GET(thread_last_error_key); return (NSError*)GS_THREAD_KEY_GET(thread_last_error_key);
#endif #endif
} }
- (void) _setLastError: (NSString*)error - (NSString*) _lastErrorText
{ {
return [[[self _lastError] userInfo] objectForKey: NSLocalizedDescriptionKey];
}
- (void) _setLastError: (NSString*)msg code: (int)aCode
{
NSError *oldError;
NSError *error;
if (msg)
{
error = [NSError _error: aCode description: msg];
}
else
{
error = nil;
}
#if __has_feature(objc_arc) #if __has_feature(objc_arc)
NSString *oldError = (__bridge_transfer NSString*)GS_THREAD_KEY_GET(thread_last_error_key); oldError = (__bridge_transfer NSError*)GS_THREAD_KEY_GET(thread_last_error_key);
GS_THREAD_KEY_SET(thread_last_error_key, (__bridge_retained void*)error); GS_THREAD_KEY_SET(thread_last_error_key, (__bridge_retained void*)error);
#pragma unused(oldError) #pragma unused(oldError)
#else #else
NSString *oldError = (NSString*)GS_THREAD_KEY_GET(thread_last_error_key); oldError = (NSError*)GS_THREAD_KEY_GET(thread_last_error_key);
GS_THREAD_KEY_SET(thread_last_error_key, RETAIN(error)); GS_THREAD_KEY_SET(thread_last_error_key, RETAIN(error));
RELEASE(oldError); RELEASE(oldError);
#endif #endif
} }
- (void) _setLastError: (NSString*)msg
{
[self _setLastError: msg code: 0];
}
- (NSError*) _errorFrom: (NSString *)fromPath to: (NSString *)toPath - (NSError*) _errorFrom: (NSString *)fromPath to: (NSString *)toPath
{ {
NSError *error; NSError *error = [self _lastError];
NSDictionary *errorInfo;
NSString *message;
NSString *domain;
NSInteger code;
NSString *lastError = [self _lastError];
if (lastError) if (error)
{ {
message = lastError; NSMutableDictionary *m;
domain = NSCocoaErrorDomain;
code = 0; /* The last error was created using a private method which produces
* an error instance with a mutable dictionary as its userInfo so we
* can efficiently use that error to return additional information.
*/
error = AUTORELEASE(RETAIN(error));
[self _setLastError: nil];
m = (NSMutableDictionary*)[error userInfo];
if (fromPath && toPath)
{
[m setObject: fromPath forKey: @"FromPath"];
[m setObject: toPath forKey: @"ToPath"];
}
else if (fromPath)
{
[m setObject: fromPath forKey: NSFilePathErrorKey];
}
} }
else else
{ {
NSDictionary *errorInfo;
NSString *message;
NSString *domain;
NSInteger code;
error = [NSError _last]; error = [NSError _last];
message = [error localizedDescription]; message = [error localizedDescription];
domain = [error domain]; domain = [error domain];
code = [error code]; code = [error code];
} if (fromPath && toPath)
{
if (fromPath && toPath) errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
{ fromPath, @"FromPath",
errorInfo = [NSDictionary dictionaryWithObjectsAndKeys: toPath, @"ToPath",
fromPath, @"FromPath", message, NSLocalizedDescriptionKey,
toPath, @"ToPath", nil];
message, NSLocalizedDescriptionKey, }
nil]; else if (fromPath)
} {
else if (fromPath) errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
{ fromPath, NSFilePathErrorKey,
errorInfo = [NSDictionary dictionaryWithObjectsAndKeys: message, NSLocalizedDescriptionKey,
fromPath, NSFilePathErrorKey, nil];
message, NSLocalizedDescriptionKey, }
nil]; else
} {
else errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
{ message, NSLocalizedDescriptionKey,
errorInfo = [NSDictionary dictionaryWithObjectsAndKeys: nil];
message, NSLocalizedDescriptionKey, }
nil]; error = [NSError errorWithDomain: domain
} code: code
userInfo: errorInfo];
error = [NSError errorWithDomain: domain
code: code
userInfo: errorInfo];
if (lastError)
{
[self _setLastError: nil];
} }
return error; return error;

View file

@ -1,13 +1,6 @@
#import "Testing.h" #import "Testing.h"
#import "ObjectTesting.h" #import "ObjectTesting.h"
#import <Foundation/NSAutoreleasePool.h> #import <Foundation/Foundation.h>
#import <Foundation/NSError.h>
#import <Foundation/NSFileManager.h>
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSPathUtilities.h>
#import <Foundation/NSError.h>
#import <Foundation/NSThread.h>
#import <Foundation/NSURL.h>
#ifdef EQ #ifdef EQ
#undef EQ #undef EQ
@ -413,9 +406,14 @@ int main()
error: &err]; error: &err];
PASS([mgr fileExistsAtPath: @"sub2/sub1" isDirectory: &isDir] PASS([mgr fileExistsAtPath: @"sub2/sub1" isDirectory: &isDir]
&& isDir == YES, "NSFileManager copy item at URL"); && isDir == YES, "NSFileManager copy item at URL");
[mgr copyItemAtPath: @"sub2" toPath: @"sub1/sub2" error: &err]; PASS([mgr copyItemAtPath: @"sub2" toPath: @"sub1/sub2" error: &err] == YES
&& nil == err, "NSFileManager copy item at Path returns expected values")
PASS([mgr fileExistsAtPath: @"sub1/sub2/sub1" isDirectory: &isDir] PASS([mgr fileExistsAtPath: @"sub1/sub2/sub1" isDirectory: &isDir]
&& isDir == YES, "NSFileManager copy item at Path"); && isDir == YES, "NSFileManager copy item at Path actually works");
PASS([mgr copyItemAtPath: @"sub2" toPath: @"sub1/sub2" error: &err] == NO
&& nil != err, "NSFileManager copy item at Path fails when dest exists")
PASS([err code] == NSFileWriteFileExistsError, "expected error code")
[mgr moveItemAtURL: [NSURL fileURLWithPath: @"sub2/sub1"] [mgr moveItemAtURL: [NSURL fileURLWithPath: @"sub2/sub1"]
toURL: [NSURL fileURLWithPath: @"sub1/moved"] toURL: [NSURL fileURLWithPath: @"sub1/moved"]
error: &err]; error: &err];