@@ -21,15 +21,16 @@ /* Work around a bug with Clang + glibc */ #ifdef __clang__ # define _HAVE_STRING_ARCH_strcmp #endif +#include +#include #include #include #include #include -#include /* Work around __block being used by glibc */ #ifdef __GLIBC__ # undef __USE_XOPEN #endif @@ -326,11 +327,12 @@ DIR_MODE) != 0) #else if (_wmkdir([path UTF16String]) != 0) #endif @throw [OFCreateDirectoryFailedException - exceptionWithPath: path]; + exceptionWithPath: path + errNo: errno]; } + (void)createDirectoryAtPath: (OFString*)path createParents: (bool)createParents { @@ -391,11 +393,12 @@ encoding = [OFSystemInfo native8BitEncoding]; if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) @throw [OFOpenFileFailedException exceptionWithPath: path - mode: @"r"]; + mode: @"r" + errNo: errno]; # if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) if (!of_mutex_lock(&mutex)) @throw [OFLockFailedException exception]; # endif @@ -411,17 +414,26 @@ # ifdef HAVE_READDIR_R if (readdir_r(dir, &buffer, &dirent) != 0) @throw [OFReadFailedException exceptionWithObject: self - requestedLength: 0]; -# else - dirent = readdir(dir); -# endif + requestedLength: 0 + errNo: errno]; if (dirent == NULL) break; +# else + if ((dirent = readdir(dir)) == NULL) { + if (errno == 0) + break; + else + @throw [OFReadFailedException + exceptionWithObject: self + requestedLength: 0 + errNo: errno]; + } +# endif if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) continue; @@ -446,13 +458,20 @@ WIN32_FIND_DATAW fd; path = [path stringByAppendingString: @"\\*"]; if ((handle = FindFirstFileW([path UTF16String], - &fd)) == INVALID_HANDLE_VALUE) + &fd)) == INVALID_HANDLE_VALUE) { + int errNo = 0; + + if (GetLastError() == ERROR_FILE_NOT_FOUND) + errNo = ENOENT; + @throw [OFOpenFileFailedException exceptionWithPath: path - mode: @"r"]; + mode: @"r" + errNo: errNo]; + } @try { do { void *pool2 = objc_autoreleasePoolPush(); OFString *file; @@ -464,10 +483,14 @@ file = [OFString stringWithUTF16String: fd.cFileName]; [files addObject: file]; objc_autoreleasePoolPop(pool2); } while (FindNextFileW(handle, &fd)); + + if (GetLastError() != ERROR_NO_MORE_FILES) + @throw [OFReadFailedException exceptionWithObject: self + requestedLength: 0]; } @finally { FindClose(handle); } objc_autoreleasePoolPop(pool); @@ -488,24 +511,26 @@ [OFSystemInfo native8BitEncoding]]) != 0) #else if (_wchdir([path UTF16String]) != 0) #endif @throw [OFChangeCurrentDirectoryPathFailedException - exceptionWithPath: path]; + exceptionWithPath: path + errNo: errno]; } + (of_offset_t)sizeOfFileAtPath: (OFString*)path { of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; - if (of_stat(path, &s) == -1) + if (of_stat(path, &s) != 0) /* FIXME: Maybe use another exception? */ @throw [OFOpenFileFailedException exceptionWithPath: path - mode: @"r"]; + mode: @"r" + errNo: errno]; return s.st_size; } + (OFDate*)modificationDateOfFileAtPath: (OFString*)path @@ -513,14 +538,15 @@ of_stat_t s; if (path == nil) @throw [OFInvalidArgumentException exception]; - if (of_stat(path, &s) == -1) + if (of_stat(path, &s) != 0) /* FIXME: Maybe use another exception? */ @throw [OFOpenFileFailedException exceptionWithPath: path - mode: @"r"]; + mode: @"r" + errNo: errno]; /* FIXME: We could be more precise on some OSes */ return [OFDate dateWithTimeIntervalSince1970: s.st_mtime]; } @@ -537,11 +563,12 @@ # else if (_wchmod([path UTF16String], permissions) != 0) # endif @throw [OFChangePermissionsFailedException exceptionWithPath: path - permissions: permissions]; + permissions: permissions + errNo: errno]; } #endif #ifdef OF_HAVE_CHOWN + (void)changeOwnerOfItemAtPath: (OFString*)path @@ -569,11 +596,12 @@ if ((passwd = getpwnam([owner cStringWithEncoding: encoding])) == NULL) @throw [OFChangeOwnerFailedException exceptionWithPath: path owner: owner - group: group]; + group: group + errNo: errno]; uid = passwd->pw_uid; } if (group != nil) { @@ -582,11 +610,12 @@ if ((group_ = getgrnam([group cStringWithEncoding: encoding])) == NULL) @throw [OFChangeOwnerFailedException exceptionWithPath: path owner: owner - group: group]; + group: group + errNo: errno]; gid = group_->gr_gid; } # ifdef OF_HAVE_THREADS } @finally { @@ -596,11 +625,12 @@ # endif if (chown([path cStringWithEncoding: encoding], uid, gid) != 0) @throw [OFChangeOwnerFailedException exceptionWithPath: path owner: owner - group: group]; + group: group + errNo: errno]; } #endif + (void)copyItemAtPath: (OFString*)source toPath: (OFString*)destination @@ -611,21 +641,21 @@ if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); - if (of_lstat(destination, &s) == 0) { - errno = EEXIST; + if (of_lstat(destination, &s) == 0) @throw [OFCopyItemFailedException exceptionWithSourcePath: source - destinationPath: destination]; - } + destinationPath: destination + errNo: EEXIST]; if (of_lstat(source, &s) != 0) @throw [OFCopyItemFailedException exceptionWithSourcePath: source - destinationPath: destination]; + destinationPath: destination + errNo: errno]; if (S_ISDIR(s.st_mode)) { OFArray *contents; OFEnumerator *enumerator; OFString *item; @@ -637,13 +667,23 @@ permissions: s.st_mode]; #endif contents = [OFFile contentsOfDirectoryAtPath: source]; } @catch (id e) { - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination]; + /* + * Only convert exceptions to OFCopyItemFailedException + * that have an errNo property. This covers all I/O + * related exceptions from the operations used to copy + * an item, all others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: [e errNo]]; + + @throw e; } enumerator = [contents objectEnumerator]; while ((item = [enumerator nextObject]) != nil) { void *pool2 = objc_autoreleasePoolPush(); @@ -687,13 +727,23 @@ #ifdef OF_HAVE_CHMOD [self changePermissionsOfItemAtPath: destination permissions: s.st_mode]; #endif } @catch (id e) { - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination]; + /* + * Only convert exceptions to OFCopyItemFailedException + * that have an errNo property. This covers all I/O + * related exceptions from the operations used to copy + * an item, all others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: [e errNo]]; + + @throw e; } @finally { [sourceFile close]; [destinationFile close]; free(buffer); } @@ -704,19 +754,30 @@ destinationOfSymbolicLinkAtPath: source]; [OFFile createSymbolicLinkAtPath: destination withDestinationPath: source]; } @catch (id e) { - @throw [OFCopyItemFailedException - exceptionWithSourcePath: source - destinationPath: destination]; + /* + * Only convert exceptions to OFCopyItemFailedException + * that have an errNo property. This covers all I/O + * related exceptions from the operations used to copy + * an item, all others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFCopyItemFailedException + exceptionWithSourcePath: source + destinationPath: destination + errNo: [e errNo]]; + + @throw e; } #endif } else @throw [OFCopyItemFailedException exceptionWithSourcePath: source - destinationPath: destination]; + destinationPath: destination + errNo: ENOTSUP]; objc_autoreleasePoolPop(pool); } + (void)moveItemAtPath: (OFString*)source @@ -731,16 +792,15 @@ if (source == nil || destination == nil) @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); - if (of_lstat(destination, &s) == 0) { - errno = EEXIST; + if (of_lstat(destination, &s) == 0) @throw [OFCopyItemFailedException exceptionWithSourcePath: source - destinationPath: destination]; - } + destinationPath: destination + errNo: EEXIST]; #ifndef _WIN32 encoding = [OFSystemInfo native8BitEncoding]; if (rename([source cStringWithEncoding: encoding], @@ -749,28 +809,32 @@ if (_wrename([source UTF16String], [destination UTF16String]) != 0) { #endif if (errno != EXDEV) @throw [OFMoveItemFailedException exceptionWithSourcePath: source - destinationPath: destination]; + destinationPath: destination + errNo: errno]; @try { [OFFile copyItemAtPath: source toPath: destination]; } @catch (OFCopyItemFailedException *e) { [OFFile removeItemAtPath: destination]; + @throw [OFMoveItemFailedException exceptionWithSourcePath: source - destinationPath: destination]; + destinationPath: destination + errNo: [e errNo]]; } @try { [OFFile removeItemAtPath: source]; } @catch (OFRemoveItemFailedException *e) { @throw [OFMoveItemFailedException exceptionWithSourcePath: source - destinationPath: destination]; + destinationPath: destination + errNo: [e errNo]]; } } objc_autoreleasePoolPop(pool); } @@ -784,22 +848,34 @@ @throw [OFInvalidArgumentException exception]; pool = objc_autoreleasePoolPush(); if (of_lstat(path, &s) != 0) - @throw [OFRemoveItemFailedException exceptionWithPath: path]; + @throw [OFRemoveItemFailedException exceptionWithPath: path + errNo: errno]; if (S_ISDIR(s.st_mode)) { OFArray *contents; OFEnumerator *enumerator; OFString *item; @try { contents = [OFFile contentsOfDirectoryAtPath: path]; } @catch (id e) { - @throw [OFRemoveItemFailedException - exceptionWithPath: path]; + /* + * Only convert exceptions to + * OFRemoveItemFailedException that have an errNo + * property. This covers all I/O related exceptions + * from the operations used to remove an item, all + * others should be left as is. + */ + if ([e respondsToSelector: @selector(errNo)]) + @throw [OFRemoveItemFailedException + exceptionWithPath: path + errNo: [e errNo]]; + + @throw e; } enumerator = [contents objectEnumerator]; while ((item = [enumerator nextObject]) != nil) { void *pool2 = objc_autoreleasePoolPush(); @@ -815,11 +891,12 @@ if (remove([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]]) != 0) #else if (_wremove([path UTF16String]) != 0) #endif - @throw [OFRemoveItemFailedException exceptionWithPath: path]; + @throw [OFRemoveItemFailedException exceptionWithPath: path + errNo: errno]; objc_autoreleasePoolPop(pool); } #ifdef OF_HAVE_LINK @@ -837,11 +914,12 @@ if (link([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding]) != 0) @throw [OFLinkFailedException exceptionWithSourcePath: source - destinationPath: destination]; + destinationPath: destination + errNo: errno]; objc_autoreleasePoolPop(pool); } #endif @@ -860,11 +938,12 @@ if (symlink([source cStringWithEncoding: encoding], [destination cStringWithEncoding: encoding]) != 0) @throw [OFCreateSymbolicLinkFailedException exceptionWithSourcePath: source - destinationPath: destination]; + destinationPath: destination + errNo: errno]; objc_autoreleasePoolPop(pool); } + (OFString*)destinationOfSymbolicLinkAtPath: (OFString*)path @@ -880,11 +959,12 @@ length = readlink([path cStringWithEncoding: encoding], destination, PATH_MAX); if (length < 0) @throw [OFOpenFileFailedException exceptionWithPath: path - mode: @"r"]; + mode: @"r" + errNo: errno]; return [OFString stringWithCString: destination encoding: encoding length: length]; } @@ -918,11 +998,12 @@ if ((_fd = open([path cStringWithEncoding: [OFSystemInfo native8BitEncoding]], flags, DEFAULT_MODE)) == -1) #endif @throw [OFOpenFileFailedException exceptionWithPath: path - mode: mode]; + mode: mode + errNo: errno]; } @catch (id e) { [self release]; @throw e; } @@ -949,23 +1030,28 @@ - (size_t)lowlevelReadIntoBuffer: (void*)buffer length: (size_t)length { ssize_t ret; -#ifndef _WIN32 - if (_fd == -1 || _atEndOfStream || - (ret = read(_fd, buffer, length)) < 0) + + if (_fd == -1 || _atEndOfStream) @throw [OFReadFailedException exceptionWithObject: self requestedLength: length]; + +#ifndef _WIN32 + if ((ret = read(_fd, buffer, length)) < 0) + @throw [OFReadFailedException exceptionWithObject: self + requestedLength: length + errNo: errno]; #else if (length > UINT_MAX) @throw [OFOutOfRangeException exception]; - if (_fd == -1 || _atEndOfStream || - (ret = read(_fd, buffer, (unsigned int)length)) < 0) + if ((ret = read(_fd, buffer, (unsigned int)length)) < 0) @throw [OFReadFailedException exceptionWithObject: self - requestedLength: length]; + requestedLength: length + errNo: errno]; #endif if (ret == 0) _atEndOfStream = true; @@ -973,22 +1059,27 @@ } - (void)lowlevelWriteBuffer: (const void*)buffer length: (size_t)length { -#ifndef _WIN32 - if (_fd == -1 || _atEndOfStream || write(_fd, buffer, length) < length) + if (_fd == -1 || _atEndOfStream) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; + +#ifndef _WIN32 + if (write(_fd, buffer, length) < length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + errNo: errno]; #else if (length > UINT_MAX) @throw [OFOutOfRangeException exception]; - if (_fd == -1 || _atEndOfStream || - write(_fd, buffer, (unsigned int)length) < length) + if (write(_fd, buffer, (unsigned int)length) < length) @throw [OFWriteFailedException exceptionWithObject: self - requestedLength: length]; + requestedLength: length + errNo: errno]; #endif } - (of_offset_t)lowlevelSeekToOffset: (of_offset_t)offset whence: (int)whence @@ -1002,11 +1093,12 @@ #endif if (ret == -1) @throw [OFSeekFailedException exceptionWithStream: self offset: offset - whence: whence]; + whence: whence + errNo: errno]; _atEndOfStream = false; return ret; }