Hash :
5265b31c
Author :
Date :
2019-01-23T15:00:20
streams: fix callers potentially only writing partial data Similar to the write(3) function, implementations of `git_stream_write` do not guarantee that all bytes are written. Instead, they return the number of bytes that actually have been written, which may be smaller than the total number of bytes. Furthermore, due to an interface design issue, we cannot ever write more than `SSIZE_MAX` bytes at once, as otherwise we cannot represent the number of bytes written to the caller. Unfortunately, no caller of `git_stream_write` ever checks the return value, except to verify that no error occurred. Due to this, they are susceptible to the case where only partial data has been written. Fix this by introducing a new function `git_stream__write_full`. In contrast to `git_stream_write`, it will always return either success or failure, without returning the number of bytes written. Thus, it is able to write all `SIZE_MAX` bytes and loop around `git_stream_write` until all data has been written. Adjust all callers except the BIO callbacks in our mbedtls and OpenSSL streams, which already do the right thing and require the amount of bytes written.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_stream_h__
#define INCLUDE_stream_h__
#include "common.h"
#include "git2/sys/stream.h"
GIT_INLINE(int) git_stream_connect(git_stream *st)
{
return st->connect(st);
}
GIT_INLINE(int) git_stream_is_encrypted(git_stream *st)
{
return st->encrypted;
}
GIT_INLINE(int) git_stream_certificate(git_cert **out, git_stream *st)
{
if (!st->encrypted) {
git_error_set(GIT_ERROR_INVALID, "an unencrypted stream does not have a certificate");
return -1;
}
return st->certificate(out, st);
}
GIT_INLINE(int) git_stream_supports_proxy(git_stream *st)
{
return st->proxy_support;
}
GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const git_proxy_options *proxy_opts)
{
if (!st->proxy_support) {
git_error_set(GIT_ERROR_INVALID, "proxy not supported on this stream");
return -1;
}
return st->set_proxy(st, proxy_opts);
}
GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len)
{
return st->read(st, data, len);
}
GIT_INLINE(ssize_t) git_stream_write(git_stream *st, const char *data, size_t len, int flags)
{
return st->write(st, data, len, flags);
}
GIT_INLINE(int) git_stream__write_full(git_stream *st, const char *data, size_t len, int flags)
{
size_t total_written = 0;
while (total_written < len) {
ssize_t written = git_stream_write(st, data + total_written, len - total_written, flags);
if (written <= 0)
return -1;
total_written += written;
}
return 0;
}
GIT_INLINE(int) git_stream_close(git_stream *st)
{
return st->close(st);
}
GIT_INLINE(void) git_stream_free(git_stream *st)
{
if (!st)
return;
st->free(st);
}
#endif