mirror of
https://github.com/troglobit/libite.git
synced 2026-11-14 19:03:05 +00:00
Preserve binary data when copying file streams
Copy the bytes returned by fread instead of treating each input chunk as a NUL-terminated string. Preserve the public stream positions and report read and write failures rather than returning success after a partial copy. Add binary boundary, current-position, native pipe error and full-device regression coverage.
This commit is contained in:
1 parent
b69d8a6013
commit
2dfc02e67d
4 files changed
+110
-5
No files matched your search
@@ -0,0 +1,92 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include "check.h"
|
||||
|
||||
static void copy_binary(size_t len)
|
||||
{
|
||||
unsigned char data[3 * BUFSIZ + 37], copied[sizeof(data)];
|
||||
const char prefix[] = "destination prefix";
|
||||
FILE *src = tmpfile(), *dst = tmpfile();
|
||||
size_t i;
|
||||
|
||||
fail_unless(src && dst);
|
||||
for (i = 0; i < sizeof(data); i++)
|
||||
data[i] = i % 253;
|
||||
|
||||
/* Begin at the current stream positions, preserving the destination prefix. */
|
||||
fail_unless(fwrite(prefix, 1, sizeof(prefix), src) == sizeof(prefix));
|
||||
fail_unless(fwrite(data, 1, len, src) == len);
|
||||
fail_unless(fseek(src, sizeof(prefix), SEEK_SET) == 0);
|
||||
fail_unless(fwrite(prefix, 1, sizeof(prefix), dst) == sizeof(prefix));
|
||||
|
||||
fail_unless(fcopyfile(src, dst) == 0);
|
||||
fail_unless(ftell(src) == (long)(sizeof(prefix) + len));
|
||||
fail_unless(ftell(dst) == (long)(sizeof(prefix) + len));
|
||||
fail_unless(fseek(dst, 0, SEEK_SET) == 0);
|
||||
fail_unless(fread(copied, 1, sizeof(prefix), dst) == sizeof(prefix));
|
||||
fail_unless(memcmp(copied, prefix, sizeof(prefix)) == 0);
|
||||
fail_unless(fread(copied, 1, sizeof(copied), dst) == len);
|
||||
fail_unless(memcmp(copied, data, len) == 0);
|
||||
fail_unless(fgetc(dst) == EOF && feof(dst));
|
||||
fail_unless(fclose(src) == 0);
|
||||
fail_unless(fclose(dst) == 0);
|
||||
}
|
||||
|
||||
static void read_error(int partial)
|
||||
{
|
||||
const char data[] = "bytes before the read error";
|
||||
char copied[sizeof(data)];
|
||||
int fd[2];
|
||||
FILE *src, *dst = tmpfile();
|
||||
|
||||
fail_unless(dst && pipe(fd) == 0);
|
||||
fail_unless(fcntl(fd[0], F_SETFL, O_NONBLOCK) == 0);
|
||||
if (partial)
|
||||
fail_unless(write(fd[1], data, sizeof(data)) == sizeof(data));
|
||||
src = fdopen(fd[0], "r");
|
||||
fail_unless(src);
|
||||
errno = 0;
|
||||
fail_unless(fcopyfile(src, dst) == -1);
|
||||
fail_unless(errno == EAGAIN || errno == EWOULDBLOCK);
|
||||
fail_unless(ferror(src));
|
||||
fail_unless(ftell(dst) == (long)(partial ? sizeof(data) : 0));
|
||||
if (partial) {
|
||||
rewind(dst);
|
||||
fail_unless(fread(copied, 1, sizeof(copied), dst) == sizeof(data));
|
||||
fail_unless(memcmp(copied, data, sizeof(data)) == 0);
|
||||
}
|
||||
fclose(src);
|
||||
fclose(dst);
|
||||
close(fd[1]);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const size_t lengths[] = { 0, 1, BUFSIZ - 1, BUFSIZ, BUFSIZ + 1, 3 * BUFSIZ + 37 };
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMS(lengths); i++)
|
||||
copy_binary(lengths[i]);
|
||||
read_error(0);
|
||||
read_error(1);
|
||||
|
||||
#ifdef __linux__
|
||||
FILE *src = tmpfile();
|
||||
FILE *dst = fopen("/dev/full", "w");
|
||||
fail_unless(src && dst);
|
||||
fail_unless(fputs("data", src) >= 0);
|
||||
rewind(src);
|
||||
fail_unless(setvbuf(dst, NULL, _IONBF, 0) == 0);
|
||||
errno = 0;
|
||||
fail_unless(fcopyfile(src, dst) == -1);
|
||||
fail_unless(errno == ENOSPC);
|
||||
fail_unless(ferror(dst));
|
||||
fclose(src);
|
||||
fclose(dst);
|
||||
#endif
|
||||
|
||||
errno = 0;
|
||||
fail_unless(fcopyfile(NULL, NULL) == -1 && errno == EINVAL);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user