Index: src/OFFile.m ================================================================== --- src/OFFile.m +++ src/OFFile.m @@ -394,42 +394,58 @@ { if (_handle == OF_INVALID_FILE_HANDLE) @throw [OFNotOpenException exceptionWithObject: self]; #if defined(OF_WINDOWS) + int bytesWritten; + if (length > INT_MAX) @throw [OFOutOfRangeException exception]; - if (write(_handle, buffer, (int)length) != (int)length) + if ((bytesWritten = write(_handle, buffer, (int)length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: errno]; #elif defined(OF_MORPHOS) + LONG bytesWritten; + if (length > LONG_MAX) @throw [OFOutOfRangeException exception]; if (_handle->append) { if (Seek64(_handle->handle, 0, OFFSET_END) == -1) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: EIO]; } - if (Write(_handle->handle, (void *)buffer, length) != (LONG)length) + if ((bytesWritten = Write(_handle->handle, (void *)buffer, length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: EIO]; #else + ssize_t bytesWritten; + if (length > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - if (write(_handle, buffer, length) != (ssize_t)length) + if ((bytesWritten = write(_handle, buffer, length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: errno]; #endif + + if ((size_t)bytesWritten != length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + bytesWritten: bytesWritten + errNo: 0]; } - (of_offset_t)lowlevelSeekToOffset: (of_offset_t)offset whence: (int)whence { Index: src/OFProcess.m ================================================================== --- src/OFProcess.m +++ src/OFProcess.m @@ -486,41 +486,51 @@ - (void)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { #ifndef OF_WINDOWS + ssize_t bytesWritten; + if (_writePipe[1] == -1) @throw [OFNotOpenException exceptionWithObject: self]; if (length > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - if (write(_writePipe[1], buffer, length) != (ssize_t)length) + if ((bytesWritten = write(_writePipe[1], buffer, length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: errno]; #else - DWORD ret; + DWORD bytesWritten; if (length > UINT32_MAX) @throw [OFOutOfRangeException exception]; if (_writePipe[1] == NULL) @throw [OFNotOpenException exceptionWithObject: self]; - if (!WriteFile(_writePipe[1], buffer, (DWORD)length, &ret, NULL) || - ret != (DWORD)length) { + if (!WriteFile(_writePipe[1], buffer, (DWORD)length, &bytesWritten, + NULL)) { int errNo = EIO; if (GetLastError() == ERROR_BROKEN_PIPE) errNo = EPIPE; @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: errNo]; } #endif + + if ((size_t)bytesWritten != length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + bytesWritten: bytesWritten + errNo: 0]; } - (int)fileDescriptorForReading { #ifndef OF_WINDOWS Index: src/OFStdIOStream.m ================================================================== --- src/OFStdIOStream.m +++ src/OFStdIOStream.m @@ -224,38 +224,53 @@ #ifndef OF_MORPHOS if (_fd == -1) @throw [OFNotOpenException exceptionWithObject: self]; # ifndef OF_WINDOWS + ssize_t bytesWritten; + if (length > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - if (write(_fd, buffer, length) != (ssize_t)length) + if ((bytesWritten = write(_fd, buffer, length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: errno]; # else + int bytesWritten; + if (length > INT_MAX) @throw [OFOutOfRangeException exception]; - if (write(_fd, buffer, (int)length) != (int)length) + if ((bytesWritten = write(_fd, buffer, (int)length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: errno]; # endif #else + LONG bytesWritten; + if (_handle == 0) @throw [OFNotOpenException exceptionWithObject: self]; if (length > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - if (Write(_handle, (void *)buffer, length) != (LONG)length) + if ((bytesWritten = Write(_handle, (void *)buffer, length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: EIO]; #endif + + if ((size_t)bytesWritten != length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + bytesWritten: bytesWritten + errNo: 0]; } #if !defined(OF_WINDOWS) && !defined(OF_MORPHOS) - (int)fileDescriptorForReading { Index: src/OFStdIOStream_Win32Console.m ================================================================== --- src/OFStdIOStream_Win32Console.m +++ src/OFStdIOStream_Win32Console.m @@ -231,11 +231,11 @@ if (_incompleteUTF8SurrogateLen > 0) { of_unichar_t c; char16_t UTF16[2]; ssize_t UTF8Len; size_t toCopy; - DWORD UTF16Len, written; + DWORD UTF16Len, bytesWritten; UTF8Len = -of_string_utf8_decode( _incompleteUTF8Surrogate, _incompleteUTF8SurrogateLen, &c); OF_ENSURE(UTF8Len > 0); @@ -269,25 +269,33 @@ UTF16[0] = c; UTF16Len = 1; } } - if (!WriteConsoleW(_handle, UTF16, UTF16Len, &written, NULL) || - written != UTF16Len) + if (!WriteConsoleW(_handle, UTF16, UTF16Len, &bytesWritten, + NULL)) + @throw [OFWriteFailedException + exceptionWithObject: self + requestedLength: UTF16Len * 2 + bytesWritten: 0 + errNo: EIO]; + + if (bytesWritten != UTF16Len) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: UTF16Len * 2 - errNo: EIO]; + bytesWritten: bytesWritten * 2 + errNo: 0]; _incompleteUTF8SurrogateLen = 0; i += toCopy; } tmp = [self allocMemoryWithSize: sizeof(char16_t) count: length * 2]; @try { - DWORD written; + DWORD bytesWritten; while (i < length) { of_unichar_t c; ssize_t UTF8Len; @@ -321,16 +329,23 @@ } if (j > UINT32_MAX) @throw [OFOutOfRangeException exception]; - if (!WriteConsoleW(_handle, tmp, (DWORD)j, &written, NULL) || - written != j) + if (!WriteConsoleW(_handle, tmp, (DWORD)j, &bytesWritten, NULL)) + @throw [OFWriteFailedException + exceptionWithObject: self + requestedLength: j * 2 + bytesWritten: 0 + errNo: EIO]; + + if (bytesWritten != j) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: j * 2 - errNo: EIO]; + bytesWritten: bytesWritten * 2 + errNo: 0]; } @finally { [self freeMemory: tmp]; } } @end Index: src/OFStreamSocket.m ================================================================== --- src/OFStreamSocket.m +++ src/OFStreamSocket.m @@ -92,28 +92,40 @@ { if (_socket == INVALID_SOCKET) @throw [OFNotOpenException exceptionWithObject: self]; #ifndef OF_WINDOWS + ssize_t bytesWritten; + if (length > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - if (send(_socket, buffer, length, 0) != (ssize_t)length) + if ((bytesWritten = send(_socket, buffer, length, 0)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: of_socket_errno()]; #else + int bytesWritten; + if (length > INT_MAX) @throw [OFOutOfRangeException exception]; - if (send(_socket, buffer, (int)length, 0) != (int)length) + if ((bytesWritten = send(_socket, buffer, (int)length, 0)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: of_socket_errno()]; #endif + + if ((size_t)bytesWritten != length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + bytesWritten: bytesWritten + errNo: 0]; } #ifdef OF_WINDOWS - (void)setBlocking: (bool)enable { Index: src/OFTCPSocket+SOCKS5.m ================================================================== --- src/OFTCPSocket+SOCKS5.m +++ src/OFTCPSocket+SOCKS5.m @@ -33,15 +33,28 @@ static void send_or_exception(OFTCPSocket *self, of_socket_t socket, char *buffer, int length) { - if (send(socket, (const void *)buffer, length, 0) != length) +#ifndef OF_WINDOWS + ssize_t bytesWritten; +#else + int bytesWritten; +#endif + + if ((bytesWritten = send(socket, (const void *)buffer, length, 0)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: of_socket_errno()]; + + if ((int)bytesWritten != length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + bytesWritten: bytesWritten + errNo: 0]; } static void recv_exact(OFTCPSocket *self, of_socket_t socket, char *buffer, int length) { Index: src/OFUDPSocket.m ================================================================== --- src/OFUDPSocket.m +++ src/OFUDPSocket.m @@ -586,32 +586,43 @@ { if (_socket == INVALID_SOCKET) @throw [OFNotOpenException exceptionWithObject: self]; #ifndef OF_WINDOWS + ssize_t bytesWritten; + if (length > SSIZE_MAX) @throw [OFOutOfRangeException exception]; - if (sendto(_socket, buffer, length, 0, - (struct sockaddr *)&receiver->address, - receiver->length) != (ssize_t)length) + if ((bytesWritten = sendto(_socket, buffer, length, 0, + (struct sockaddr *)&receiver->address, receiver->length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: of_socket_errno()]; #else + int bytesWritten; + if (length > INT_MAX) @throw [OFOutOfRangeException exception]; - if (sendto(_socket, buffer, (int)length, 0, + if ((bytesWritten = sendto(_socket, buffer, (int)length, 0, (struct sockaddr *)&receiver->address, - receiver->length) != (int)length) + receiver->length)) < 0) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length + bytesWritten: 0 errNo: of_socket_errno()]; #endif + + if ((size_t)bytesWritten != length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length + bytesWritten: bytesWritten + errNo: 0]; } - (void)cancelAsyncRequests { [OFRunLoop of_cancelAsyncRequestsForObject: self]; Index: src/exceptions/OFWriteFailedException.h ================================================================== --- src/exceptions/OFWriteFailedException.h +++ src/exceptions/OFWriteFailedException.h @@ -23,8 +23,59 @@ * OFWriteFailedException.h ObjFW/OFWriteFailedException.h * * @brief An exception indicating that writing to an object failed. */ @interface OFWriteFailedException: OFReadOrWriteFailedException +{ + size_t _bytesWritten; +} + +/*! + * The number of bytes already written before the write failed. + * + * This can be used to make sure that a retry does not write data already + * written before. + */ +@property (readonly, nonatomic) size_t bytesWritten; + ++ (instancetype)exceptionWithObject: (id)object + requestedLength: (size_t)requestedLength + errNo: (int)errNo OF_UNAVAILABLE; + +/*! + * @brief Creates a new, autoreleased write failed exception. + * + * @param object The object from which reading or to which writing failed + * @param requestedLength The requested length of the data that could not be + * read / written + * @param bytesWritten The amount of bytes already written before the write + * failed + * @param errNo The errno of the error that occurred + * @return A new, autoreleased write failed exception + */ ++ (instancetype)exceptionWithObject: (id)object + requestedLength: (size_t)requestedLength + bytesWritten: (size_t)bytesWritten + errNo: (int)errNo; + +- initWithObject: (id)object + requestedLength: (size_t)requestedLength + errNo: (int)errNo OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated write failed exception. + * + * @param object The object from which reading or to which writing failed + * @param requestedLength The requested length of the data that could not be + * read / written + * @param bytesWritten The amount of bytes already written before the write + * failed + * @param errNo The errno of the error that occurred + * @return A new open file failed exception + */ +- initWithObject: (id)object + requestedLength: (size_t)requestedLength + bytesWritten: (size_t)bytesWritten + errNo: (int)errNo OF_DESIGNATED_INITIALIZER; @end OF_ASSUME_NONNULL_END Index: src/exceptions/OFWriteFailedException.m ================================================================== --- src/exceptions/OFWriteFailedException.m +++ src/exceptions/OFWriteFailedException.m @@ -18,12 +18,57 @@ #import "OFWriteFailedException.h" #import "OFString.h" @implementation OFWriteFailedException +@synthesize bytesWritten = _bytesWritten; + ++ (instancetype)exceptionWithObject: (id)object + requestedLength: (size_t)requestedLength + errNo: (int)errNo +{ + return [[[self alloc] initWithObject: object + requestedLength: requestedLength + errNo: errNo] autorelease]; +} + ++ (instancetype)exceptionWithObject: (id)object + requestedLength: (size_t)requestedLength + bytesWritten: (size_t)bytesWritten + errNo: (int)errNo +{ + return [[[self alloc] initWithObject: object + requestedLength: requestedLength + bytesWritten: bytesWritten + errNo: errNo] autorelease]; +} + +- initWithObject: (id)object + requestedLength: (size_t)requestedLength + errNo: (int)errNo +{ + OF_INVALID_INIT_METHOD +} + +- initWithObject: (id)object + requestedLength: (size_t)requestedLength + bytesWritten: (size_t)bytesWritten + errNo: (int)errNo +{ + self = [super initWithObject: object + requestedLength: requestedLength + errNo: errNo]; + + _bytesWritten = bytesWritten; + + return self; +} + - (OFString *)description { return [OFString stringWithFormat: - @"Failed to write %zu bytes to an object of type %@: %@", - _requestedLength, [_object class], of_strerror(_errNo)]; + @"Failed to write %zu bytes (after %zu bytes written) to an " + @"object of type %@: %@", + _requestedLength, _bytesWritten, [_object class], + of_strerror(_errNo)]; } @end