/* vfdbuf.c - VU buffered I/O
*
****************************************************************
* Copyright (C) 1998, 2000 Thomas Lord
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "hackerlab/os/errno.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/mem/must-malloc.h"
#include "hackerlab/mem/talloc.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/vu/vu-sys.h"
#include "hackerlab/vu/vfdbuf.h"
/************************************************************************
*(h0 "Buffered File Descriptors")
*
* These functions allow you to perform buffered I/O on descriptors
* using the VU functions. (See xref:"A Virtual Unix File-System
* Interface".)
*
* Each buffered file is associated with these values:
*
* t_uchar * buffer;
* Storage for buffered characters.
* The location of the buffer in memory
* may change over time.
*
* long bufsize
* The size of the buffer.
*
* t_uchar * read_write_position;
* The read/write position, within the buffer.
* This corresponds to the file offset returned
* by:
* vu_lseek (&errn, fd, 0, SEEK_CUR)
*
* long buffered;
* The number of buffered characters relative
* to the read/write position.
*
* All bytes between `buffer' and
* `read_write_position + buffered' are
* buffered.
*
* Note that `buffered' may be negative if the
* `read_write_position' is beyond the current
* end of the file.
*
* Normal I/O functions like `vu_read' and `vu_write' operate
* in the expected way: they buffer characters or read characters
* from the buffer, flushing or refilling it as needed.
*
* For high-performance I/O, there is assistance for performing
* buffered I/O without copying characters to and from the buffer:
*
* vfdbuf_getbuf
* vfdbuf_takebuf
* vfdbuf_set_buffer
* vfdbuf_more
* vfdbuf_advance
*
* which are documented later in this chapter.
*
* Buffers can be designated `dont_flush' -- which means that the buffer
* grows rather than writing to an underlying file. In combination with
* pseudo-descriptors (see xref:"reserv_pseudo"), `dont_flush' buffers
* can be used to implement in-core files (files which are actually
* strings).
*
* Buffers can be designated `zero_byte' -- which means that an extra
* byte is added to the buffer (at `buffer + bufsize') and that byte
* is initialized to 0.
*/
/*(menu)
*/
struct vfdbuf_record
{
struct vu_handler sub_handler;
int flags;
int eof;
void (*free_buffer)(t_uchar * buffer, void * closure);
void * free_buffer_closure;
int dont_flush;
int is_pipe;
int zero_byte;
int auto_shift;
off_t fd_offset; /* the kernel's read/write position */
off_t buffer_offset; /* position in the file of the buffer */
long read_write_pos; /* offset within the buffer */
long buffered; /* number of valid characters in buffer */
long bufsize; /* total size of buffer */
t_uchar * buf; /* the buffer */
};
static union
{
struct vfdbuf_record * vfd; /* array of vfdbuf_record */
void * void_ptr; /* for passing to ar_* functions */
} bufs = {0};
#if 1
#define check_buffer_invariants(FD) \
check_buffer_invariants_internal (fd, __FILE__, __LINE__)
#else
#define check_buffer_invariants(FD)
#undef invariant
#define invariant(COND)
#endif
static void
check_buffer_invariants_internal (int fd, char * file, int line)
{
invariant_at_file_line (vfdbuf_is_buffered (fd), file, line);
/* temporary invariant since RDWR mode doesn't really work
*/
invariant_at_file_line ((bufs.vfd[fd].flags & O_ACCMODE) != O_RDWR, file, line);
invariant_at_file_line (bufs.vfd[fd].buffered >= 0, file, line);
invariant_at_file_line (bufs.vfd[fd].buffered <= bufs.vfd[fd].bufsize, file, line);
invariant_at_file_line (bufs.vfd[fd].read_write_pos >= 0, file, line);
invariant_at_file_line (bufs.vfd[fd].read_write_pos <= bufs.vfd[fd].bufsize, file, line);
invariant_at_file_line ((!bufs.vfd[fd].bufsize && !bufs.vfd[fd].zero_byte) || bufs.vfd[fd].buf, file, line);
/* invariant_at_file_line (bufs.vfd[fd].is_pipe || ((bufs.vfd[fd].flags & O_ACCMODE) != O_RDWR), file, line); */
/* Note that the RDWR case doesn't really work.
*/
invariant_at_file_line (( !bufs.vfd[fd].is_pipe
|| ( (((bufs.vfd[fd].flags & O_ACCMODE) == O_RDONLY) && (bufs.vfd[fd].fd_offset == (bufs.vfd[fd].buffer_offset + bufs.vfd[fd].buffered)))
|| (((bufs.vfd[fd].flags & O_ACCMODE) == O_WRONLY) && (bufs.vfd[fd].fd_offset == bufs.vfd[fd].buffer_offset))
|| (((bufs.vfd[fd].flags & O_ACCMODE) == O_RDWR) && 0))),
file, line);
}
/* static void realloc_buf (int fd, long bufsize);
*
* Make a buffer larger. (`bufsize' must be >= than
* `bufs.vfd[fd].bufsize'.)
*/
static void
realloc_buf (int fd, long bufsize)
{
long size;
t_uchar * oldbuf;
t_uchar * newbuf;
invariant (bufsize >= bufs.vfd[fd].bufsize);
check_buffer_invariants (fd);
oldbuf = bufs.vfd[fd].buf;
size = bufsize + (bufs.vfd[fd].zero_byte ? 1 : 0);
newbuf = (t_uchar *)must_malloc (size);
mem_move (newbuf, oldbuf, bufs.vfd[fd].buffered);
bufs.vfd[fd].buf = newbuf;
bufs.vfd[fd].bufsize = bufsize;
if (bufs.vfd[fd].free_buffer)
{
bufs.vfd[fd].free_buffer (oldbuf, bufs.vfd[fd].free_buffer_closure);
bufs.vfd[fd].free_buffer = 0;
}
else
{
must_free (oldbuf);
}
if (bufs.vfd[fd].zero_byte)
bufs.vfd[fd].buf[size - 1] = 0;
invariant (bufs.vfd[fd].buf != oldbuf);
check_buffer_invariants (fd);
}
void
vfdbuf_flush_all(void)
{
int x;
int size;
size = ar_size (bufs.void_ptr);
for (x = 0; x < size; ++x)
{
if (bufs.vfd[x].sub_handler.vtable)
{
int errn;
vfdbuf_flush (&errn, x);
}
}
}
/************************************************************************
*(h1 "Establishing Buffers")
*
*/
/*(c vfdbuf_buffer_fd)
* int vfdbuf_buffer_fd (int * errn,
* int fd,
* long bufsize,
* int flags,
* int buffer_flags);
*
* Arrange to buffer I/O on descriptor `fd'.
*
* `fd' is the descriptor to buffer.
*
* `bufsize' is the initial size of the buffer or 0 to use a buffer of
* the default size.
*
* `flags' indicates whether to buffer for input (`O_RDONLY'),
* output (`O_WRONLY') or both (`O_RDWR').
*
* \WARNING:/ I am fairly sure that there are bugs in the
* buffering code, especially for `O_RDWR' files. Some
* comprehensive unit tests are needed for this module.
*
* `buffer_flags' is a combination (`|') of any of the bits:
*
* vfdbuf_add_zero_byte
* vfdbuf_auto_shift
*
* If `vfdbuf_add_zero_byte' is set, the actual buffer size for this
* descriptor is one byte larger than `bufsize' (even if the buffer is
* reallocated). The extra byte is initialized to 0.
*
* If `vfdbuf_auto_shift' is set, the behavior of `vfdbuf_more' is
* modified. See xref:"vfdbuf_more".
*
* If `fd' is already buffered, this function attempts to reset the
* buffer size, flags, and buffer flags. If more than `bufsize'
* characters are already buffered, the resulting buffer will be
* larger than `bufsize'.
*
* Return 0 on success, -1 (and set `*errn') on error.
*/
int
vfdbuf_buffer_fd (int * errn, int fd, long bufsize, int flags, int buffer_flags)
{
off_t offset;
int is_pipe;
struct vu_handler * sub_handler;
if (bufsize <= 0)
bufsize = 4096;
if ((ar_size (bufs.void_ptr) > fd) && bufs.vfd[fd].sub_handler.vtable)
{
int had_zero_byte;
/* already buffered */
if (bufs.vfd[fd].flags != flags)
{
*errn = EINVAL;
return -1;
}
had_zero_byte = bufs.vfd[fd].zero_byte;
bufs.vfd[fd].auto_shift = !!(buffer_flags & vfdbuf_auto_shift);
bufs.vfd[fd].zero_byte = !!(buffer_flags & vfdbuf_add_zero_byte);
if (bufsize != bufs.vfd[fd].bufsize)
{
t_uchar * oldbuf;
t_uchar * newbuf;
if (bufsize < bufs.vfd[fd].buffered)
bufsize = bufs.vfd[fd].buffered;
newbuf = must_malloc (bufsize + bufs.vfd[fd].zero_byte);
oldbuf = bufs.vfd[fd].buf;
mem_move (newbuf, oldbuf, bufs.vfd[fd].buffered);
if (bufs.vfd[fd].free_buffer)
bufs.vfd[fd].free_buffer (oldbuf, bufs.vfd[fd].free_buffer_closure);
else
must_free (oldbuf);
bufs.vfd[fd].buf = newbuf;
if (bufs.vfd[fd].zero_byte)
bufs.vfd[fd].buf[bufsize] = 0;
}
check_buffer_invariants (fd);
return 0;
}
sub_handler = vu_fd_dispatch (fd);
offset = sub_handler->vtable->lseek (errn, fd, 0, SEEK_CUR, sub_handler->closure);
if (offset >= 0)
is_pipe = 0;
else
{
if ((*errn != ESPIPE) || ((flags & O_ACCMODE) == O_RDWR))
return -1;
else
{
is_pipe = 1;
offset = 0;
}
}
if (fd >= ar_size (bufs.void_ptr))
{
ar_ref (&bufs.void_ptr, fd, sizeof (struct vfdbuf_record));
}
bufs.vfd[fd].sub_handler = *sub_handler;
bufs.vfd[fd].flags = O_ACCMODE & flags;
bufs.vfd[fd].eof = 0;
bufs.vfd[fd].free_buffer = 0;
bufs.vfd[fd].free_buffer_closure = 0;
bufs.vfd[fd].dont_flush = 0;
bufs.vfd[fd].is_pipe = is_pipe;
bufs.vfd[fd].zero_byte = !!(buffer_flags & vfdbuf_add_zero_byte);
bufs.vfd[fd].auto_shift = !!(buffer_flags & vfdbuf_auto_shift);
bufs.vfd[fd].fd_offset = 0;
bufs.vfd[fd].buffer_offset = offset;
bufs.vfd[fd].read_write_pos = 0;
bufs.vfd[fd].buffered = 0;
bufs.vfd[fd].buf = must_malloc (bufsize + bufs.vfd[fd].zero_byte);
bufs.vfd[fd].bufsize = bufsize;
vu_set_fd_handler (fd, &vfdbuf_vtable, 0);
check_buffer_invariants (fd);
return 0;
}
/*(c vfdbuf_set_buffer)
* int
* vfdbuf_set_buffer (int * errn,
* int fd,
* t_uchar * buffer,
* long bufsize,
* long read_write_pos,
* long buffered,
* int add_zero_byte,
* void (*free_buffer)(t_uchar * buf, void * closure),
* void * free_buffer_closure)
*
* Establish a specific region of memory as the buffer for `fd'.
*
* See xref:"Buffered File Descriptors" and xref:"vfdbuf_buffer_fd".
*/
int
vfdbuf_set_buffer (int * errn,
int fd,
t_uchar * buffer,
long bufsize,
long read_write_pos,
long buffered,
int add_zero_byte,
void (*free_buffer)(t_uchar * buf, void * closure),
void * free_buffer_closure)
{
if (!((ar_size (bufs.void_ptr) > fd) && bufs.vfd[fd].sub_handler.vtable))
{
/* not buffered */
invalid:
*errn = EINVAL;
return -1;
}
if ((buffered > bufsize) || (read_write_pos > bufsize))
goto invalid;
if (bufs.vfd[fd].free_buffer)
bufs.vfd[fd].free_buffer (bufs.vfd[fd].buf, bufs.vfd[fd].free_buffer_closure);
bufs.vfd[fd].buf = buffer;
bufs.vfd[fd].bufsize = bufsize;
bufs.vfd[fd].read_write_pos = read_write_pos;
bufs.vfd[fd].buffered = buffered;
bufs.vfd[fd].zero_byte = !!add_zero_byte;
bufs.vfd[fd].free_buffer = free_buffer;
bufs.vfd[fd].free_buffer_closure = free_buffer_closure;
check_buffer_invariants (fd);
return 0;
}
int
vfdbuf_unbuffer_fd (int * errn, int fd)
{
check_buffer_invariants (fd);
if (!bufs.vfd[fd].dont_flush && (bufs.vfd[fd].flags != O_RDONLY) && bufs.vfd[fd].buffered && bufs.vfd[fd].read_write_pos)
{
int flush;
flush = vfdbuf_flush (errn, fd);
if (flush < 0)
return -1;
}
vu_set_fd_handler (fd, bufs.vfd[fd].sub_handler.vtable, bufs.vfd[fd].sub_handler.closure);
if (!bufs.vfd[fd].free_buffer)
must_free (bufs.vfd[fd].buf);
else
bufs.vfd[fd].free_buffer (bufs.vfd[fd].buf, bufs.vfd[fd].free_buffer_closure);
mem_set ((t_uchar *)&bufs.vfd[fd], 0, sizeof (bufs.vfd[fd]));
return 0;
}
/*(c vfdbuf_is_buffered)
* int vfdbuf_is_buffered (int fd);
*
* Return 1 if fd has a (vfdbuf) buffer, 0 otherwise.
*/
int
vfdbuf_is_buffered (int fd)
{
if ((ar_size (bufs.void_ptr) > fd) && bufs.vfd[fd].sub_handler.vtable)
return 1;
return 0;
}
/************************************************************************
*(h1 "Controlling Buffer Flushing")
*
*
*
*/
/*(c vfdbuf_set_dont_flush)
* int vfdbuf_set_dont_flush (int * errn, int fd, int setting);
*
* Activate or deactivate buffer flushing for descriptor `fd'.
*
* `setting' is 0 to deactivate and any other value to activate
* flushing.
*
* If flushing is deactivated, writes may cause the size of the buffer
* to be increased and calls to `vfdbuf_flush' have no effect.
*
* Return -1 and EINVAL if `fd' is not buffered, 0 otherwise.
*
* See also xref:"vfdbuf_is_dont_flush".
*/
int
vfdbuf_set_dont_flush (int * errn, int fd, int setting)
{
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
bufs.vfd[fd].dont_flush = !!setting;
check_buffer_invariants (fd);
return 0;
}
/*(c vfdbuf_is_dont_flush)
* int vfdbuf_is_dont_flush (int * errn, int fd);
*
* Return 0 if fd is buffered and flushing is enabled,
* a positive value if fd is buffered and flushing is disabled.
*
* Return -1 and EINVAL if fd is not buffered.
*/
int
vfdbuf_is_dont_flush (int * errn, int fd)
{
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
return bufs.vfd[fd].dont_flush;
}
/************************************************************************
*(h1 "Controlling Buffer Auto-shift")
*
*/
/*(c vfdbuf_set_auto_shift)
* int vfdbuf_set_auto_shift (int * errn, int fd, int setting);
*
* Activate or deactivate buffer auto-shift for descriptor `fd'.
*
* `setting' is 0 to deactivate and any other value to activate
* auto-shift.
*
* See xref:"vfdbuf_more".
*
* Return -1 and EINVAL if `fd' is not buffered, 0 otherwise.
*
* See also xref:"vfdbuf_is_auto_shift".
*/
int
vfdbuf_set_auto_shift (int * errn, int fd, int setting)
{
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
bufs.vfd[fd].auto_shift = !!setting;
check_buffer_invariants (fd);
return 0;
}
/*(c vfdbuf_is_auto_shift)
* int vfdbuf_is_auto_shift (int * errn, int fd);
*
* Return 0 if fd is buffered and auto-shift is disabled,
* a positive value if fd is buffered and auto-shift is enabled.
*
* Return -1 and EINVAL if fd is not buffered.
*/
int
vfdbuf_is_auto_shift (int * errn, int fd)
{
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
return bufs.vfd[fd].auto_shift;
}
/* static int sync_offset (int * errn, int fd);
*
* Move the descriptor's offset within the file to agree with the
* buffer's offset within the file.
*/
static int
sync_offset (int * errn, int fd)
{
check_buffer_invariants (fd);
invariant ( ( ((bufs.vfd[fd].flags & O_ACCMODE) != O_RDONLY)
&& ((bufs.vfd[fd].flags & O_ACCMODE) != O_RDWR))
|| ( (bufs.vfd[fd].buffered == 0)
&& (bufs.vfd[fd].read_write_pos == 0)));
if (bufs.vfd[fd].buffer_offset != bufs.vfd[fd].fd_offset)
{
off_t got;
got = bufs.vfd[fd].sub_handler.vtable->lseek (errn,
fd,
bufs.vfd[fd].buffer_offset,
SEEK_SET,
bufs.vfd[fd].sub_handler.closure);
if (got != bufs.vfd[fd].buffer_offset)
{
if (got >= 0)
*errn = EIO;
return -1;
}
bufs.vfd[fd].fd_offset = bufs.vfd[fd].buffer_offset;
check_buffer_invariants (fd);
}
return 0;
}
/************************************************************************
*(h1 "Access to Buffers")
*
*/
/*(c vfdbuf_getbuf)
* int vfdbuf_getbuf (int * errn,
* t_uchar ** buffer,
* long * bufsize,
* t_uchar ** read_write_position,
* long * buffered,
* int fd);
*
* Return the address and size of the buffer and the address (within
* that buffer) and number of buffered characters beyond the
* read/write position for descriptor `fd'.
*
* Upon return, `*buf' is the read/write position and `*buffered' is
* the number of buffered characters beyond that position. If
* `*buffered' is less than 0, then the characters between `*buffer'
* and `*buffer - *buffered' are not yet part of the file (the read/write
* position is beyond the end of the file).
*
* Any of `buffer', `bufsize', `read_write_pos' or `buffered' may be
* 0.
*
* Return -1 and EINVAL if `fd' is not buffered, 0 otherwise.
*/
int
vfdbuf_getbuf (int * errn,
t_uchar ** buffer,
long * bufsize,
t_uchar ** read_write_position,
long * buffered,
int * has_zero_byte,
int fd)
{
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
if (buffer)
*buffer = bufs.vfd[fd].buf;
if (bufsize)
*bufsize = bufs.vfd[fd].bufsize;
if (read_write_position)
*read_write_position = bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos;
if (buffered)
*buffered = bufs.vfd[fd].buffered - bufs.vfd[fd].read_write_pos;
if (has_zero_byte)
*has_zero_byte = bufs.vfd[fd].zero_byte;
invariant (!buffer || (*buffer == bufs.vfd[fd].buf));
invariant (!bufsize || (*bufsize == bufs.vfd[fd].bufsize));
invariant (!read_write_position || ((*read_write_position - bufs.vfd[fd].buf) == bufs.vfd[fd].read_write_pos));
invariant (!buffered || ((bufs.vfd[fd].read_write_pos + *buffered) == bufs.vfd[fd].buffered));
invariant (!has_zero_byte || (*has_zero_byte == bufs.vfd[fd].zero_byte));
return 0;
}
/*(c vfdbuf_takebuf)
* int vfdbuf_takebuf (int * errn,
* t_uchar ** bufbase,
* long * bufsize,
* t_uchar ** read_write_pos,
* long * buffered,
* int fd,
* void (*free_buffer)(t_uchar * buf, void * closure),
* void * free_buffer_closure);
*
* Return the address of and size of the buffer for `fd'. Also return
* the address of and number of buffered characters beyond the
* read/write position. (See `vfdbuf_getbuf'.)
*
* Responsibility for freeing the buffer returned (allocated by
* `must_malloc') rests with the caller. `vfdbuf' functions will continue
* to use the buffer. When the `vfdbuf' functions no longer require
* the buffer, they call `free_buffer':
*
* free_buffer (buffer, free_buffer_closure)
*
* Subsequent calls to `vfdbuf_takebuf', made before `free_buffer' is
* called will return the same buffer if the same values are passed
* for `free_buffer' and `free_buffer_closure' and a newly allocated
* buffer otherwise.
*/
int
vfdbuf_takebuf (int * errn,
t_uchar ** bufbase,
long * bufsize,
t_uchar ** read_write_pos,
long * buffered,
int * has_zero_byte,
int fd,
void (*free_buffer)(t_uchar * buf, void * closure),
void * free_buffer_closure)
{
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
if ( (bufs.vfd[fd].free_buffer != free_buffer)
|| (bufs.vfd[fd].free_buffer_closure != free_buffer_closure))
realloc_buf (fd, bufs.vfd[fd].bufsize);
bufs.vfd[fd].free_buffer = free_buffer;
bufs.vfd[fd].free_buffer_closure = free_buffer_closure;
return vfdbuf_getbuf (errn, bufbase, bufsize, read_write_pos, buffered, has_zero_byte, fd);
}
/************************************************************************
*(h1 "Controlling What is Buffered")
*
*
*/
/*(c vfdbuf_shift)
* int vfdbuf_shift (int * errn, int fd, long amt);
*
* \WARNING: there is a note in the code that says documentation
* for this function is out of date./
*
* Discard `amt' leading characters from a buffer, shifting any
* remaining characters to make room at the end of the buffer.
*
* If the "don't flush" flag is set for this buffer, this call will
* increase the size of the buffer by `amt' characters and leave existing
* characters untouched.
*
* If `amt' is larger than the number of buffered characters that
* preceed the read/write position, or if `amt' is -1, the effect is
* the same as if `amt' were exactly the number of buffered characters
* that preceed the read/write position.
*
* If the buffer is being used for output, this call will first flush
* shifted characters that are currently part of the file. If there
* are characters in the buffer beyond the end of the file, however, they
* are not flushed by this function. The effect is that this function
* will not make a file larger. (See `vfdbuf_buffer_to_position'.)
*
* Return 0 upon success, -1 upon error. This function can fail for a
* descriptor opened for writing if it was necessary to flush
* characters from the buffer, but the flush operation failed.
*
* One useful post-condition of this function is that there is room
* for `amt' more characters beyond the read/write position after a
* call to this function than were available before.
*
* Another useful post-condition is that the read/write position
* within the file is not changed by this function. (See
* `vfdbuf_advance'.)
*/
int
vfdbuf_shift (int * errn, int fd, long amt)
{
off_t after_short_write;
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
if ((amt > bufs.vfd[fd].read_write_pos) || (amt == -1))
amt = bufs.vfd[fd].read_write_pos;
if (bufs.vfd[fd].dont_flush)
{
realloc_buf (fd, bufs.vfd[fd].bufsize + amt);
return 0;
}
after_short_write = 0;
retry_after_short_write:
if (((bufs.vfd[fd].flags & O_ACCMODE) == O_WRONLY) || ((bufs.vfd[fd].flags & O_ACCMODE) == O_RDWR))
{
size_t to_write;
off_t wrote;
if (sync_offset (errn, fd) < 0)
return -1;
to_write = (bufs.vfd[fd].buffered < amt) ? bufs.vfd[fd].buffered : amt;
wrote = bufs.vfd[fd].sub_handler.vtable->write (errn,
fd,
bufs.vfd[fd].buf,
to_write,
bufs.vfd[fd].sub_handler.closure);
if (wrote < 0)
return -1;
bufs.vfd[fd].fd_offset += wrote;
if (to_write > wrote)
{
after_short_write = amt - wrote;
amt = wrote;
}
}
mem_move (bufs.vfd[fd].buf, bufs.vfd[fd].buf + amt, bufs.vfd[fd].buffered - amt);
bufs.vfd[fd].buffer_offset += amt;
bufs.vfd[fd].read_write_pos -= amt;
bufs.vfd[fd].buffered -= amt;
check_buffer_invariants (fd);
if (after_short_write)
{
amt = after_short_write;
goto retry_after_short_write;
}
return 0;
}
/*(c vfdbuf_advance)
* int vfdbuf_advance (int * errn, int fd, long amt);
*
* Advance the read/write position of a buffered descriptor.
*
* If `amt' is larger than the number of buffered characters beyond
* the read/write position, the size of the buffer is increased, and
* the new characters initialized to 0, but no new characters are read
* from the descriptor.
*
* One or more calls this function are commonly followed by a call
* to `vfdbuf_shift' to discard the advanced-over characters from the
* buffer. A good time to make such a call is before a call to
* `vfdbuf_more', if it is safe to discard characters from the buffer
* at that time.
*
* Return -1 and EINVAL if `fd' is not buffered, 0 otherwise.
*/
int
vfdbuf_advance (int * errn, int fd, long amt)
{
long bufsize_needed;
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
bufsize_needed = bufs.vfd[fd].read_write_pos + amt;
if (bufsize_needed > bufs.vfd[fd].bufsize)
realloc_buf (fd, bufsize_needed);
if (bufs.vfd[fd].read_write_pos + amt > bufs.vfd[fd].buffered)
{
long from;
long fill_to;
from = ((bufs.vfd[fd].read_write_pos > bufs.vfd[fd].buffered)
? bufs.vfd[fd].read_write_pos
: bufs.vfd[fd].buffered);
fill_to = bufs.vfd[fd].read_write_pos + amt;
mem_set (bufs.vfd[fd].buf + from, 0, fill_to - from);
}
bufs.vfd[fd].read_write_pos += amt;
check_buffer_invariants (fd);
return 0;
}
/*(c vfdbuf_flush)
* int vfdbuf_flush (int * errn, int fd);
*
* Write buffered characters prior to the read/write position to disk.
* This function has no effect if descriptor `fd' is not buffered, or
* if the "don't flush" flag is set for this descriptor. (See
* `vfdbuf_set_dont_flush'.)
*
* Flushed characters are removed from the buffer.
*
* The read/write position may be past the last character of the file.
* In that case, only the characters in the file are flushed to disk.
* (See `vfdbuf_buffer_to_position'.)
*
* Return 0 upon success, -1 upon error.
*/
int
vfdbuf_flush (int * errn, int fd)
{
off_t wrote;
size_t to_write;
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
if ( !bufs.vfd[fd].read_write_pos
|| bufs.vfd[fd].dont_flush
|| ((bufs.vfd[fd].flags != O_WRONLY) && (bufs.vfd[fd].flags != O_RDWR)))
return 0;
to_write = bufs.vfd[fd].read_write_pos;
if (to_write > bufs.vfd[fd].buffered)
to_write = bufs.vfd[fd].buffered;
retry:
if (sync_offset (errn, fd) < 0)
return -1;
wrote = bufs.vfd[fd].sub_handler.vtable->write (errn,
fd,
bufs.vfd[fd].buf,
to_write,
bufs.vfd[fd].sub_handler.closure);
if (wrote < 0)
return -1;
mem_move (bufs.vfd[fd].buf, bufs.vfd[fd].buf + wrote, bufs.vfd[fd].buffered - wrote);
to_write -= wrote;
bufs.vfd[fd].fd_offset += wrote;
bufs.vfd[fd].buffer_offset += wrote;
bufs.vfd[fd].read_write_pos -= wrote;
bufs.vfd[fd].buffered -= wrote;
if (to_write)
goto retry;
invariant ((bufs.vfd[fd].buffered == 0) || (bufs.vfd[fd].read_write_pos == 0));
/* This would discard any characters prior to the read_write_pos
* that are not already part of the file. That doesn't seem like
* the right thing to do, although it would give the invariant that
* (read_write_pos == 0) on exit.
*
* bufs.vfd[fd].buffer_offset += bufs.vfd[fd].read_write_pos;
* bufs.vfd[fd].read_write_pos = 0;
*/
check_buffer_invariants (fd);
return 0;
}
/*(c vfdbuf_return)
* int vfdbuf_return (int * errn, int fd, t_uchar * str, long len);
*
* Add characters to the buffer for descriptor `fd'.
*
* Characters are inserted immediately after the read/write position
* so that subsequent reads on this descriptor return those characters
* first and subsequent writes overwrite them. Subsequent flushes of
* a writable descriptor will ignore these characters unless the
* read/write position is first advanced by a write or by
* `vfdbuf_advance'.
*
* If `fd' is a read-only descriptor, buffered characters prior to
* the read/write position may be overwritten.
*
* The read/write position of the descriptor is unchanged relative to
* the beginning of the file by calls to this function.
*/
int
vfdbuf_return (int * errn, int fd, t_uchar * str, long len)
{
long size_needed;
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
if ((bufs.vfd[fd].flags & O_ACCMODE) == O_RDONLY)
{
if (bufs.vfd[fd].read_write_pos >= len)
{
bufs.vfd[fd].read_write_pos -= len;
mem_move (bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos, str, len);
return 0;
}
else
{
int ign;
vfdbuf_shift (&ign, fd, -1);
}
}
if (bufs.vfd[fd].buffered <= bufs.vfd[fd].read_write_pos)
size_needed = bufs.vfd[fd].read_write_pos + len;
else
size_needed = bufs.vfd[fd].buffered + len;
if (size_needed > bufs.vfd[fd].bufsize)
realloc_buf (fd, size_needed);
if (bufs.vfd[fd].buffered <= bufs.vfd[fd].read_write_pos)
{
mem_move (bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos, str, len);
bufs.vfd[fd].buffered = bufs.vfd[fd].read_write_pos + len;
}
else
{
mem_move (bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos + len,
bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos,
bufs.vfd[fd].buffered - bufs.vfd[fd].read_write_pos);
mem_move (bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos, str, len);
bufs.vfd[fd].buffered += len;
}
return 0;
}
/*(c vfdbuf_more)
* int vfdbuf_more (int * errn,
* t_uchar ** buffer,
* long * bufsize,
* t_uchar ** read_write_pos,
* long * buffered,
* int fd,
* long opt_amt);
*
* Buffer (by reading) additional characters for descriptor `fd'.
* Return the address of and number of buffered unread characters
* (including previously buffered but unread characters).
*
* `opt_amt' is the number of additional characters to read or 0 to
* read a default number of additional characters.
*
* Ordinarily, no characters are discarded from the buffer by this
* function. (See `vfdbuf_advance' and `vfdbuf_shift'.)
*
* If the buffer was created with the flag `vfdbuf_auto_shift',
* `opt_amt' is 0, and the last buffered character is more than 3/4 of
* the way through the buffer's allocated memory, then `vfdbuf_more'
* will call `vfdbuf_shift' before doing anything else. This is the
* only circumstance under which `vfdbuf_more' will discard characters
* from a buffer.
*
* The read/write position is not changed by this function.
*
* Return -1 upon error, the number of characters read upon success.
*/
ssize_t
vfdbuf_more (int * errn,
t_uchar ** buffer,
long * bufsize,
t_uchar ** read_write_pos,
long * buffered,
int * has_zero_byte,
int fd,
long opt_amt)
{
long size_needed;
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
if (!opt_amt)
{
if (bufs.vfd[fd].auto_shift)
{
long pos;
long size;
pos = bufs.vfd[fd].buffered;
size = bufs.vfd[fd].bufsize;
if (pos * 4 > size * 3)
if (vfdbuf_shift (errn, fd, -1))
return -1;
}
opt_amt = bufs.vfd[fd].bufsize - bufs.vfd[fd].buffered;
if (!opt_amt)
opt_amt = bufs.vfd[fd].bufsize;
if (!opt_amt)
opt_amt = 4096;
}
size_needed = bufs.vfd[fd].buffered + opt_amt;
if (size_needed > bufs.vfd[fd].bufsize)
realloc_buf (fd, size_needed);
{
off_t offset;
ssize_t got;
offset = bufs.vfd[fd].buffer_offset + bufs.vfd[fd].buffered;
if (offset != bufs.vfd[fd].fd_offset)
{
off_t went_to;
went_to = bufs.vfd[fd].sub_handler.vtable->lseek (errn,
fd,
offset,
SEEK_SET,
bufs.vfd[fd].sub_handler.closure);
if (0 > went_to)
return -1;
if (went_to != offset)
{
*errn = EIO;
return -1;
}
bufs.vfd[fd].fd_offset = offset;
}
got = bufs.vfd[fd].sub_handler.vtable->read (errn,
fd,
bufs.vfd[fd].buf + bufs.vfd[fd].buffered,
opt_amt,
bufs.vfd[fd].sub_handler.closure);
if (got < 0)
return -1;
if (got == 0)
bufs.vfd[fd].eof = 1;
else
{
bufs.vfd[fd].fd_offset += got;
bufs.vfd[fd].buffered += got;
}
if (0 > vfdbuf_getbuf (errn, buffer, bufsize, read_write_pos, buffered, has_zero_byte, fd))
return -1;
check_buffer_invariants (fd);
return got;
}
}
/************************************************************************
*(h1 "The End-of-File")
*
*
*
*/
/*(c vfdbuf_is_eof)
* int vfdbuf_is_eof (int * errn, int fd);
*
* Return 1 value if the end of file has been reached while
* reading from buffered descriptor `fd', 0 otherwise.
*
* Return -1 and EINVAL if `fd' is not a buffered descriptor.
*/
int
vfdbuf_is_eof (int * errn, int fd)
{
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
return bufs.vfd[fd].eof;
}
/*(c vfdbuf_clear_eof)
* void vfdbuf_clear_eof (int * errn, int fd);
*
* Clear the eof flag for buffered file descriptor `fd'.
*
* Return -1 and EINVAL if `fd' is not a buffered descriptor.
*/
int
vfdbuf_clear_eof (int * errn, int fd)
{
if (!vfdbuf_is_buffered (fd))
{
*errn = EINVAL;
return -1;
}
bufs.vfd[fd].eof = 0;
return 0;
}
#define vfdbuf_make_closure vu_sys_make_closure
#define vfdbuf_free_closure vu_sys_free_closure
int
vfdbuf_access (int * errn, char * path, int mode, void * closure)
{
return vu_sys_access (errn, path, mode, closure);
}
int
vfdbuf_chdir (int * errn, char * path, void * closure)
{
return vu_sys_chdir (errn, path, closure);
}
int
vfdbuf_chmod (int * errn, char * path, int mode, void * closure)
{
return vu_sys_chmod (errn, path, mode, closure);
}
int
vfdbuf_chown (int * errn, char * path, int owner, int group, void * closure)
{
return vu_sys_chown (errn, path, owner, group, closure);
}
int
vfdbuf_chroot (int * errn, char * path, void * closure)
{
return vu_sys_chroot (errn, path, closure);
}
int
vfdbuf_closedir (int * errn, DIR * dir, void * closure)
{
return bufs.vfd[vu_dir_fd (dir)].sub_handler.vtable->closedir (errn, dir, closure);
}
int
vfdbuf_fchdir (int * errn, int fd, void * closure)
{
return bufs.vfd[fd].sub_handler.vtable->fchdir (errn, fd, closure);
}
int
vfdbuf_fchmod (int * errn, int fd, int mode, void * closure)
{
return bufs.vfd[fd].sub_handler.vtable->fchmod (errn, fd, mode, closure);
}
int
vfdbuf_fchown (int * errn, int fd, int owner, int group, void * closure)
{
return bufs.vfd[fd].sub_handler.vtable->fchown (errn, fd, owner, group, closure);
}
int
vfdbuf_fstat (int * errn, int fd, struct stat * buf, void * closure)
{
return bufs.vfd[fd].sub_handler.vtable->fstat (errn, fd, buf, closure);
}
int
vfdbuf_link (int * errn, char * from, char * to, void * closure)
{
return vu_sys_link (errn, from, to, closure);
}
int
vfdbuf_lstat (int * errn, char * path, struct stat * buf, void * closure)
{
return vu_sys_lstat (errn, path, buf, closure);
}
int
vfdbuf_mkdir (int * errn, char * path, int mode, void * closure)
{
return vu_sys_mkdir (errn, path, mode, closure);
}
int
vfdbuf_open (int * errn, char * path, int flags, int mode, void * closure)
{
return vu_sys_open (errn, path, flags, mode, closure);
}
int
vfdbuf_opendir (int * errn, DIR ** retv, char * path, void * closure)
{
return vu_sys_opendir (errn, retv, path, closure);
}
int
vfdbuf_readdir (int * errn, struct alloc_limits * limits, char ** file_ret, DIR * dir, void * closure)
{
return bufs.vfd[ vu_dir_fd (dir)].sub_handler.vtable->readdir (errn, limits, file_ret, dir, closure);
}
int
vfdbuf_readlink (int * errn, char * path, char * buf, int bufsize, void * closure)
{
return vu_sys_readlink (errn, path, buf, bufsize, closure);
}
int
vfdbuf_rename (int * errn, char * from, char * to, void * closure)
{
return vu_sys_rename (errn, from, to, closure);
}
int
vfdbuf_rmdir (int * errn, char * path, void * closure)
{
return vu_sys_rmdir (errn, path, closure);
}
int
vfdbuf_stat (int * errn, char * path, struct stat * buf, void * closure)
{
return vu_sys_stat (errn, path, buf, closure);
}
int
vfdbuf_symlink (int * errn, char * from, char * to, void * closure)
{
return vu_sys_symlink (errn, from, to, closure);
}
int
vfdbuf_truncate (int * errn, char * path, off_t where, void * closure)
{
return vu_sys_truncate (errn, path, where, closure);
}
int
vfdbuf_unlink (int * errn, char * path, void * closure)
{
return vu_sys_unlink (errn, path, closure);
}
int
vfdbuf_utime (int * errn, char * path, struct utimbuf * times, void * closure)
{
return vu_sys_utime (errn, path, times, closure);
}
int
vfdbuf_fcntl (int * errn, int fd, int cmd, long arg, void * closure)
{
return bufs.vfd[fd].sub_handler.vtable->fcntl (errn, fd, cmd, arg, closure);
}
int
vfdbuf_dup (int * errn, int fd, void * closure)
{
return bufs.vfd[fd].sub_handler.vtable->dup (errn, fd, closure);
}
int
vfdbuf_dup2 (int * errn, int fd, int newfd, void * closure)
{
return bufs.vfd[fd].sub_handler.vtable->dup2 (errn, fd, newfd, closure);
}
int
vfdbuf_move_state (int * errn, int fd, int newfd, void * closure)
{
int sub_move;
struct vu_handler * sub_handler;
if (fd == newfd)
return fd;
sub_move = bufs.vfd[fd].sub_handler.vtable->move_state (errn,
fd,
newfd,
bufs.vfd[fd].sub_handler.closure);
if (0 > sub_move)
return -1;
/* sub_handler might have changed the underlying handler for fd
*/
sub_handler = vu_fd_dispatch (fd);
if (sub_handler->vtable != &vfdbuf_vtable)
bufs.vfd[fd].sub_handler = *sub_handler;
/* Move the buffer to the new descriptor.
*/
ar_ref (&bufs.void_ptr, newfd, sizeof (struct vfdbuf_record));
bufs.vfd[newfd] = bufs.vfd[fd];
/* Remove the buffer from the old descriptor.
*/
mem_set0 ((t_uchar *)&bufs.vfd[fd], sizeof (bufs.vfd[fd]));
/* Make sure the old fd is now using the sub_handler and not
* a vfdbuf handler.
*/
vu_set_fd_handler (fd,
bufs.vfd[newfd].sub_handler.vtable,
bufs.vfd[newfd].sub_handler.closure);
/* Make sure the new fd is now using a vfdbuf handler.
*/
vu_set_fd_handler (newfd, &vfdbuf_vtable, 0);
return 0;
}
int
vfdbuf_close (int * errn, int fd, void * closure)
{
int got;
check_buffer_invariants (fd);
if (!bufs.vfd[fd].dont_flush && (bufs.vfd[fd].flags != O_RDONLY) && bufs.vfd[fd].buffered && bufs.vfd[fd].read_write_pos)
{
int flush;
flush = vfdbuf_flush (errn, fd);
if (flush < 0)
return -1;
}
got = bufs.vfd[fd].sub_handler.vtable->close (errn,
fd,
bufs.vfd[fd].sub_handler.closure);
bufs.vfd[fd].sub_handler.vtable->free_closure (bufs.vfd[fd].sub_handler.closure);
if (!bufs.vfd[fd].free_buffer)
must_free (bufs.vfd[fd].buf);
else
bufs.vfd[fd].free_buffer (bufs.vfd[fd].buf, bufs.vfd[fd].free_buffer_closure);
mem_set ((t_uchar *)&bufs.vfd[fd], 0, sizeof (bufs.vfd[fd]));
return got;
}
int
vfdbuf_fsync (int * errn, int fd, void * closure)
{
if ((bufs.vfd[fd].flags != O_RDONLY) && bufs.vfd[fd].buffered && bufs.vfd[fd].read_write_pos)
{
if (bufs.vfd[fd].dont_flush)
{
*errn = EINVAL;
return -1;
}
if (vfdbuf_flush (errn, fd) < 0)
return -1;
}
return bufs.vfd[fd].sub_handler.vtable->fsync (errn, fd, bufs.vfd[fd].sub_handler.closure);
}
int
vfdbuf_ftruncate (int * errn, int fd, off_t where, void * closure)
{
int got;
got = bufs.vfd[fd].sub_handler.vtable->ftruncate (errn,
fd,
where,
bufs.vfd[fd].sub_handler.closure);
if (got < 0)
return -1;
if (bufs.vfd[fd].buffer_offset >= got)
{
bufs.vfd[fd].buffer_offset += bufs.vfd[fd].read_write_pos;
bufs.vfd[fd].read_write_pos = 0;
bufs.vfd[fd].buffered = 0;
}
else if (bufs.vfd[fd].buffer_offset + bufs.vfd[fd].buffered >= got)
{
long retained;
retained = got - bufs.vfd[fd].buffer_offset;
mem_set (bufs.vfd[fd].buf + retained, 0, bufs.vfd[fd].buffered - retained);
bufs.vfd[fd].buffered = retained;
}
check_buffer_invariants (fd);
return 0;
}
ssize_t
vfdbuf_read (int * errn, int fd, char * buf, size_t count, void * closure)
{
ssize_t orig_count;
ssize_t avail;
ssize_t filled_in;
orig_count = count;
if (bufs.vfd[fd].buffered > bufs.vfd[fd].read_write_pos)
avail = bufs.vfd[fd].buffered - bufs.vfd[fd].read_write_pos;
else
avail = 0;
filled_in = (avail < count ? avail : count);
mem_move (buf, bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos, filled_in);
bufs.vfd[fd].read_write_pos += filled_in;
buf += filled_in;
count -= filled_in;
check_buffer_invariants (fd);
if (!count)
return orig_count;
else
{
ssize_t got;
/* We have consumed all buffered characters without
* completing the read.
*/
invariant (bufs.vfd[fd].buffered == bufs.vfd[fd].read_write_pos);
if (!bufs.vfd[fd].dont_flush)
{
/* bufs.vfd[fd].dont_flush == 0
*
* Now read buffer-sized chunks directly into the
* caller's buffer, but do this only if we are
* permitted to flush the vfdbuf buffer.
*/
if (0 > vfdbuf_shift (errn, fd, -1))
{
if (orig_count == count)
return -1;
else
{
check_buffer_invariants (fd);
return orig_count - count;
}
}
/* The vfdbuf buffer is empty:
*/
invariant (bufs.vfd[fd].read_write_pos == 0);
invariant (bufs.vfd[fd].buffered == 0);
if (sync_offset (errn, fd) < 0)
{
if (orig_count == count)
return -1;
else
{
check_buffer_invariants (fd);
return orig_count - count;
}
}
while (count > bufs.vfd[fd].bufsize)
{
/* The following invariant is true because either we:
*
* 1. just called sync_offset
* or 2. added equal amounts to both `buffer_offset' and `fd_offset'.
*
*/
invariant (bufs.vfd[fd].buffer_offset == bufs.vfd[fd].fd_offset);
got = bufs.vfd[fd].sub_handler.vtable->read (errn,
fd,
buf,
bufs.vfd[fd].bufsize,
bufs.vfd[fd].sub_handler.closure);
if (got < 0)
{
if (count == orig_count)
return -1;
check_buffer_invariants (fd);
return orig_count - count;
}
if (got == 0)
{
bufs.vfd[fd].eof = 1;
check_buffer_invariants (fd);
return orig_count - count;
}
bufs.vfd[fd].fd_offset += got;
bufs.vfd[fd].buffer_offset += got;
buf += got;
count -= got;
}
}
/* There may be more to read because either
*
* 1. We weren't permitted to read buffersize chunks
* directly into the caller's buffer because
* `bufs.vfd[fd].dont_flush != 0'.
*
* or 2. We read buffersize chunks, but there remains
* more characters (smaller than the size of the
* buffer) yet to read.
*
*/
if (count)
{
/* Buffer some additional characters internally, then copy
* those to the caller's buffer.
*/
if (vfdbuf_more (errn, 0, 0, 0, 0, 0, fd, 0) < 0)
{
if (orig_count == count)
return -1;
else
{
check_buffer_invariants (fd);
return orig_count - count;
}
}
avail = bufs.vfd[fd].buffered - bufs.vfd[fd].read_write_pos;
filled_in = (avail < count ? avail : count);
mem_move (buf,
bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos,
filled_in);
bufs.vfd[fd].read_write_pos += filled_in;
buf += filled_in;
count -= filled_in;
}
check_buffer_invariants (fd);
return orig_count - count;
}
}
ssize_t
vfdbuf_write (int * errn, int fd, char const * buf, size_t count, void * closure)
{
ssize_t orig_count;
ssize_t avail;
ssize_t filled_in;
orig_count = count;
avail = bufs.vfd[fd].bufsize - bufs.vfd[fd].read_write_pos;
if (bufs.vfd[fd].dont_flush && (avail < count))
{
ssize_t added;
added = count - avail;
realloc_buf (fd, bufs.vfd[fd].bufsize + added);
avail = bufs.vfd[fd].bufsize - bufs.vfd[fd].read_write_pos;
invariant (avail >= count);
}
filled_in = (avail < count ? avail : count);
mem_move (bufs.vfd[fd].buf + bufs.vfd[fd].read_write_pos,
buf,
filled_in);
bufs.vfd[fd].read_write_pos += filled_in;
if (bufs.vfd[fd].read_write_pos > bufs.vfd[fd].buffered)
bufs.vfd[fd].buffered = bufs.vfd[fd].read_write_pos;
buf += filled_in;
count -= filled_in;
check_buffer_invariants (fd);
if (!count)
{
invariant (filled_in == orig_count);
return orig_count;
}
else
{
int errn2;
if (bufs.vfd[fd].buffered < bufs.vfd[fd].read_write_pos)
{
/* We are about to write buffered characters. The
* read_write_pos is past the last buffered character so there
* has been an lseek (or similar operation) that left characters
* in the buffer which are not yet part of the file. The
* current write ensures that the characters between the
* end-of-flie and the current read_write_pos are now part of
* the file. To make sure that `flush' writes those characters,
* we do this:
*/
bufs.vfd[fd].buffered = bufs.vfd[fd].read_write_pos;
}
if (vfdbuf_flush (&errn2, fd) < 0)
{
if (filled_in)
{
check_buffer_invariants (fd);
return filled_in;
}
else
{
*errn = errn2;
return -1;
}
}
/* This is not a general invariant of `vfdbuf_flush', but
* works here because we made sure that all characters before
* the read_write_pos were considered buffered before calling
* `vfdbuf_flush'.
*
* This invariant is important before the call to `sync_offset'.
*/
invariant (bufs.vfd[fd].read_write_pos == 0);
if (sync_offset (errn, fd) < 0)
{
if (count == orig_count)
return -1;
else
{
check_buffer_invariants (fd);
return orig_count - count;
}
}
while (count > bufs.vfd[fd].bufsize)
{
ssize_t wrote;
wrote = bufs.vfd[fd].sub_handler.vtable->write (errn,
fd,
buf,
bufs.vfd[fd].bufsize,
bufs.vfd[fd].sub_handler.closure);
if (wrote < 0)
return -1;
bufs.vfd[fd].fd_offset += wrote;
buf += wrote;
count -= wrote;
if (wrote < bufs.vfd[fd].bufsize)
{
check_buffer_invariants (fd);
return orig_count - count;
}
}
mem_move (bufs.vfd[fd].buf, buf, count);
bufs.vfd[fd].buffer_offset = bufs.vfd[fd].fd_offset;
bufs.vfd[fd].read_write_pos = count;
bufs.vfd[fd].buffered = count;
check_buffer_invariants (fd);
return orig_count;
}
}
off_t
vfdbuf_lseek (int * errn, int fd, off_t offset, int whence, void * closure)
{
check_buffer_invariants (fd);
if (bufs.vfd[fd].is_pipe)
{
if (whence == SEEK_SET)
{
if ((bufs.vfd[fd].flags & O_ACCMODE) == O_RDONLY)
{
/* is_pipe, SEEK_SET, O_RDONLY */
if (offset == bufs.vfd[fd].fd_offset)
return offset;
else
{
espipe_error:
*errn = ESPIPE;
return -1;
}
}
else
{
/* is_pipe, SEEK_SET, O_WRONLY */
if (offset == (bufs.vfd[fd].buffer_offset + bufs.vfd[fd].buffered))
return offset;
else
goto espipe_error;
}
/* not reached */
}
else if ((whence == SEEK_CUR) && (offset == 0))
{
if ((bufs.vfd[fd].flags & O_ACCMODE) == O_RDONLY)
{
/* is_pipe, SEEK_CUR, O_RDONLY */
return bufs.vfd[fd].fd_offset;
}
else
{
/* is_pipe, SEEK_CUR, O_WRONLY */
return bufs.vfd[fd].fd_offset + bufs.vfd[fd].buffered;
}
}
else
goto espipe_error;
/* is_pipe, not reached */
}
/* !is_pipe */
switch (whence)
{
default:
*errn = EINVAL;
return -1;
case SEEK_SET:
break;
case SEEK_CUR:
offset += (bufs.vfd[fd].buffer_offset + bufs.vfd[fd].read_write_pos);
break;
case SEEK_END:
{
struct stat statb;
if (0 > vu_fstat (errn, fd, &statb))
return -1;
offset += statb.st_size;
}
break;
}
if ((offset >= bufs.vfd[fd].buffer_offset)
&& (offset <= (bufs.vfd[fd].buffer_offset + bufs.vfd[fd].buffered)))
bufs.vfd[fd].read_write_pos = offset - bufs.vfd[fd].buffer_offset;
else
{
if (((bufs.vfd[fd].flags & O_ACCMODE) != O_RDONLY) && bufs.vfd[fd].buffered && bufs.vfd[fd].read_write_pos)
{
/* This is a writable descriptor and there are valid buffered characters
* preceeding the read/write position.
*/
if (bufs.vfd[fd].dont_flush)
{
*errn = EINVAL;
return -1;
}
if (0 > vfdbuf_flush (errn, fd))
return -1;
invariant (!bufs.vfd[fd].buffered);
}
/* Either this buffer is read-only, or there are no valid characters
* in the buffer.
*/
bufs.vfd[fd].buffer_offset = offset;
bufs.vfd[fd].read_write_pos = 0;
bufs.vfd[fd].buffered = 0;
}
check_buffer_invariants (fd);
return bufs.vfd[fd].buffer_offset + bufs.vfd[fd].read_write_pos;
}
struct vu_fs_discipline vfdbuf_vtable \
= { VU_FS_DISCIPLINE_INITIALIZERS (vfdbuf_) };
syntax highlighted by Code2HTML, v. 0.9.1