/* $Id: fakewrite.c 4022 2008-03-31 06:11:07Z rra $
 *
 * Fake write and writev functions for testing xwrite and xwritev.
 *
 * Copyright (c) 2004, 2005, 2006
 *     by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
 *     2002, 2003 by The Internet Software Consortium and Rich Salz
 *
 * This code is derived from software contributed to the Internet Software
 * Consortium by Rich Salz.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include <config.h>
#include <portable/system.h>
#include <portable/uio.h>

#include <errno.h>

#include <util/util.h>

ssize_t fake_write(int, const void *, size_t);
ssize_t fake_pwrite(int, const void *, size_t, off_t);
ssize_t fake_writev(int, const struct iovec *, int);

/*
 * All the data is actually written into this buffer.  We use write_offset
 * to track how far we've written.
 */
char write_buffer[256];
size_t write_offset = 0;

/*
 * If write_interrupt is non-zero, then half of the calls to write or writev
 * will fail, returning -1 with errno set to EINTR.
 */
int write_interrupt = 0;

/*
 * If write_fail is non-zero, all writes or writevs will return 0, indicating
 * no progress in writing out the buffer.
 */
int write_fail = 0;


/*
 * Accept a write request and write only the first 32 bytes of it into
 * write_buffer (or as much as will fit), returning the amount written.
 */
ssize_t
fake_write(int fd UNUSED, const void *data, size_t n)
{
    size_t total;

    if (write_fail)
        return 0;
    if (write_interrupt && (write_interrupt++ % 2) == 0) {
        errno = EINTR;
        return -1;
    }
    total = (n < 32) ? n : 32;
    if (256 - write_offset < total)
        total = 256 - write_offset;
    memcpy(write_buffer + write_offset, data, total);
    write_offset += total;
    return total;
}


/*
 * Accept a pwrite request and write only the first 32 bytes of it into
 * write_buffer at the specified offset (or as much as will fit), returning
 * the amount written.
 */
ssize_t
fake_pwrite(int fd UNUSED, const void *data, size_t n, off_t offset)
{
    size_t total;

    if (write_fail)
        return 0;
    if (write_interrupt && (write_interrupt++ % 2) == 0) {
        errno = EINTR;
        return -1;
    }
    total = (n < 32) ? n : 32;
    if (offset > 256) {
        errno = ENOSPC;
        return -1;
    }
    if ((size_t) (256 - offset) < total)
        total = 256 - offset;
    memcpy(write_buffer + offset, data, total);
    return total;
}


/*
 * Accept an xwrite request and write only the first 32 bytes of it into
 * write_buffer (or as much as will fit), returning the amount written.
 */
ssize_t
fake_writev(int fd UNUSED, const struct iovec *iov, int iovcnt)
{
    int total, i;
    size_t left, n;

    if (write_fail)
        return 0;
    if (write_interrupt && (write_interrupt++ % 2) == 0) {
        errno = EINTR;
        return -1;
    }
    left = 256 - write_offset;
    if (left > 32)
        left = 32;
    total = 0;
    for (i = 0; i < iovcnt && left != 0; i++) {
        n = ((size_t) iov[i].iov_len < left) ? (size_t) iov[i].iov_len : left;
        memcpy(write_buffer + write_offset, iov[i].iov_base, n);
        write_offset += n;
        total += n;
        left -= n;
    }
    return total;
}
