Files
oldlinux-files/gnu/glibc/glibc-1.05.tar/glibc-1.05/sysdeps/mach/hurd/stdio_init.c
2024-02-19 00:24:47 -05:00

486 lines
12 KiB
C

/* Copyright (C) 1991 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA. */
#include <ansidecl.h>
#include <stdio.h>
#include <hurd.h>
#include <hurd/shared.h>
struct mapped
{
struct shared_io shared;
/* The rest of the shared page is owned by the user (stdio). */
io_t file; /* Port to the file. */
size_t blksize; /* Optimal size for i/o. */
memory_object_t cntl; /* Memory object for this page. */
/* Memory objects for reading and writing data.
These two might be the same. */
memory_object_t rdmemobj, wrmemobj;
};
/* Get IT. */
static error_t
#ifdef __GNUC__
__inline
#endif
DEFUN(get_it, (m), struct mapped *m)
{
try:
__spin_lock(m->shared.lock);
switch (m->shared.it_status)
{
case USER_POTENTIALLY_IT:
m->shared.it_status = USER_IT;
case USER_RELEASE_IT:
case USER_IT:
__spin_unlock(m->shared.lock);
return 0;
case USER_NOT_IT:
__spin_unlock(m->shared.lock);
{
error_t error = __io_get_it(m->file, m->cntl);
if (error == 0)
return 0;
else
/* Don't just tail-recurse because that might
make the function not get inlined. Sigh. */
goto try;
}
default:
__libc_fatal("get_it: Unrecognized IT status!\n");
}
}
/* Release IT. */
static error_t
#ifdef __GNUC__
__inline
#endif
DEFUN(release_it, (m), struct mapped *m)
{
__spin_lock(m->shared.lock);
switch (m->shared.it_status)
{
case USER_IT:
m->shared.it_status = USER_POTENTIALLY_IT;
case USER_NOT_IT:
__spin_unlock(m->shared.lock);
return 0;
case USER_RELEASE_IT:
__spin_unlock(m->shared.lock);
return __io_release_it(m->file, m->cntl);
default:
__libc_fatal("release_it: Unrecognized IT status!\n");
}
}
static int
DEFUN(mapped_close, (cookie), PTR cookie)
{
struct mapped *CONST m = (struct mapped *) cookie;
int am_it;
error_t error = 0;
__spin_lock(m->shared.lock);
am_it = m->shared.it_status != USER_NOT_IT;
__spin_unlock(m->shared.lock);
if (am_it)
error = __io_release_it(m->file, m->cntl);
#define DO(foo) if (error == 0) error = foo; else (void) foo
DO(__vm_deallocate(__mach_task_self(), m, sizeof(*m)));
DO(__port_deallocate(__mach_task_self(), m->file));
DO(__port_deallocate(__mach_task_self(), m->cntl));
DO(__port_deallocate(__mach_task_self(), m->rdmemobj));
DO(__port_deallocate(__mach_task_self(), m->wrmemobj));
#undef DO
if (error != 0)
{
errno = error;
return -1;
}
return 0;
}
static int
DEFUN(mapped_seek, (cookie, pos, whence),
PTR cookie AND fpos_t *pos AND int whence)
{
struct shared_io *CONST m = (struct shared_io *) cookie;
int error;
error = get_it(m);
if (error == 0)
{
switch (whence)
{
case SEEK_SET:
if (!m->seekable && *pos > m->file_pointer)
error = ESPIPE;
else
{
m->file_pointer = *pos;
error = 0;
}
break;
case SEEK_CUR:
if (!m->seekable && *pos < 0)
error = ESPIPE;
else
{
m->file_pointer += *pos;
*pos = m->file_pointer;
error = 0;
}
break;
case SEEK_END:
if (!m->use_file_size)
error = ESPIPE;
else
{
off_t desired = m->file_size + *pos;
if (!m->seekable && desired < m->file_pointer)
error = ESPIPE;
else
{
*pos = m->file_pointer = desired;
error = 0;
}
}
break;
default:
error = EINVAL;
break;
}
if (error == 0)
error = release_it(m);
else
(void) release_it(m);
}
if (error != 0)
{
errno = error;
return -1;
}
return 0;
}
static int
DEFUN(mapped_input, (stream), FILE *stream)
{
struct mapped *CONST m = (struct mapped *) cookie;
struct shared_io *CONST sio = &m->shared;
off_t pos, bufp;
error_t error;
off_t size;
if (error = get_it(sio))
{
__error(stream) = 1;
errno = error;
return EOF;
}
if (__buffer(stream) != NULL && m->rdmemobj == m->wrmemobj &&
__offset(stream) == __target(stream))
{
/* The right spot is already mapped. */
sio->accessed = 1; /* Tell the FS we are reading it. */
__get_limit(stream) = __buffer(stream) + __bufsize(stream);
if (!sio->written)
/* Make the next writing operation call mapped_output,
so it can set the `written' bit at the right time. */
__put_limit(stream) = __buffer(stream);
if (error = release_it(m))
goto release_lost;
__bufp(stream) = __buffer(stream);
return *__bufp(stream)++;
}
if (__buffer(stream) != NULL)
{
/* Remove the old mapping. */
size_t mapping_size = ((__get_limit(stream) > __buffer(stream) ?
__get_limit(stream) : __put_limit(stream)) -
__buffer(stream));
(void) __vm_deallocate(__mach_task_self(),
__buffer(stream), mapping_size);
__buffer(stream) = NULL;
}
/* We're reading, so we're not at the end-of-file. */
__eof(stream) = 0;
pos = __target(stream);
while (sio->use_read_size && sio->read_size < pos)
{
if (sio->use_file_size && pos >= sio->file_size)
{
/* Tried to read past the end of the file. */
if (sio->eof_notify)
__io_eofnotify(m->file);
if (error = release_it(sio))
goto release_lost;
__eof(stream) = 1;
return EOF;
}
/* Block until there is more to read. */
error = __io_readsleep(m->file);
if (error)
{
(void) release_it(m);
errno = error;
return EOF;
}
}
if (sio->use_read_size)
size = sio->read_size;
else if (sio->use_file_size)
size = sio->file_size;
else
__libc_fatal("!use_read_size && !use_file_size\n");
/* Round POS to a block boundary, leaving the excess in BUFP. */
bufp = pos % m->blksize;
pos -= bufp;
/* Decide how big a window on the file to use. */
size -= pos;
if (size > m->blksize)
size = m->blksize;
/* Map the data. */
{
vm_prot_t prot = VM_PROT_READ;
if (stream->__mode.__write && m->rdmemobj == m->wrmemobj)
prot |= VM_PROT_WRITE;
error = __vm_map(__mach_task_self(),
&__buffer(stream), size, 0, 1,
m->rdmemobj, pos, 0, prot, prot, VM_INHERIT_NONE);
}
if (error == 0)
/* Tell the FS that we have read some data. */
sio->accessed = 1;
if (error == 0)
(void) release_it(m);
else
error = release_it(m);
if (error)
{
release_lost:
__put_limit(stream) = __get_limit(stream) = __buffer(stream);
errno = error;
return EOF;
}
/* Set the offset to the position where our mapping begins. */
__offset(stream) = pos;
/* Set the target position to just past the end of our mapping. */
__target(stream) = pos + size;
/* Make the next output operation call __flshfp. */
__put_limit(stream) = __buffer(stream);
__bufsize(stream) = size;
__get_limit(stream) = __buffer(stream) + size;
__bufp(stream) = __buffer(stream) + bufp;
return (unsigned char) *__bufp(stream)++;
}
static void
DEFUN(mapped_output, (stream, c),
FILE *stream AND int c)
{
struct mapped *CONST m = (struct mapped *) cookie;
struct shared_io *CONST sio = &m->shared;
error_t error;
off_t pos, bufp;
size_t size;
if (error = get_it(sio))
{
__error(stream) = 1;
errno = error;
return EOF;
}
if (__put_limit(stream) > __buffer(stream))
{
if (__bufp(stream) > __buffer(stream))
/* Tell the FS that we have written some data. */
sio->written = 1;
if (sio->use_postnotify_size &&
(__offset(stream) +
(__bufp(stream) - __buffer(stream))) > sio->postnotify_size)
{
/* Notify the FS about what has been written. */
if (error = __io_postnotify(m->file, m->cntl,
__offset(stream),
__bufp(stream) - __buffer(stream)))
goto end;
}
}
if (__buffer(stream) != NULL)
{
/* Remove the old mapping. */
(void) __vm_deallocate(__mach_task_self(), __buffer(stream),
__get_limit(stream) - __buffer(stream));
__buffer(stream) = NULL;
}
size = m->blksize;
pos = __target(stream);
/* Round POS to a block boundary, leaving the excess in BUFP. */
bufp = pos % size;
pos -= bufp;
if (sio->use_postnotify_size && size > sio->postnotify_size)
/* %%% What if SIZE < BUFP after this? */
size = sio->postnotify_size;
while (sio->use_prenotify_size && __target(stream) >= sio->prenotify_use)
if (error = __io_prenotify(m->file, m->cntl, pos, size))
goto end;
/* Map the data. */
{
vm_prot_t prot = VM_PROT_WRITE;
if (stream->__mode.__read && m->wrmemobj == m->rdmemobj)
prot |= VM_PROT_READ;
error = __vm_map(__mach_task_self(),
&__buffer(stream), size, 0, 1,
m->memobj, pos, 0, prot, prot, VM_INHERIT_NONE);
}
end:
release_it(m);
if (error)
{
__put_limit(stream) = __get_limit(stream) = __buffer(stream);
__error(stream) = 1;
errno = error;
}
else
{
/* Set the offset to the position where our mapping begins. */
__target(stream) = __offset(stream) = pos;
/* Make the next input operation call __fillbf. */
__get_limit(stream) = __buffer(stream);
__bufsize(stream) = size;
__put_limit(stream) = __buffer(stream) + size;
__bufp(stream) = __buffer(stream) + bufp;
*__bufp(stream)++ = (unsigned char) c;
}
}
/* Initialize STREAM as necessary.
This may change I/O functions, give a buffer, etc.
If no buffer is allocated, but the bufsize is set,
the bufsize will be used to allocate the buffer. */
void
DEFUN(__stdio_init_stream, (stream), FILE *stream)
{
CONST io_t file = (io_t) __cookie(stream);
io_statbuf_t buf;
memory_object_t cntl, xxmemobj, rdmemobj, wrmemobj;
struct mapped *m;
if (__io_stat(file, &buf))
return;
if (S_ISFIFO(buf.stb_mode))
{
/* It's named pipe (FIFO). Make it unbuffered. */
__userbuf(stream) = 1;
return;
}
if (sizeof(*m) > __vm_page_size)
__libc_fatal("stdio: sizeof(struct mapped) > vm_page_size");
/* Try to use mapped i/o. */
if (__io_map_cntl(m->file, &xxcntl, &rdcntl, &wtcntl))
return;
cntl = xxcntl; /* %%% ??? */
if (__io_map(m->file, &xxmemobj, &rdmemobj, &wrmemobj))
{
(void) __port_deallocate(__mach_task_self(), cntl);
return;
}
if (rdmemobj == MACH_PORT_NULL)
rdmemobj = xxmemobj;
if (wrmemobj == MACH_PORT_NULL)
wrmemobj = xxmemobj;
/* Lose if we can't do mapped i/o in the necessary direction(s). */
if ((stream->__mode.__read && rdmemobj == MACH_PORT_NULL) ||
(stream->__mode.__write && wrmemobj == MACH_PORT_NULL) ||
/* Map the shared page. */
__vm_map(__mach_task_self(), &m, sizeof(*m), NULL, 1, cntl, 0, 0,
VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE,
VM_INHERIT_NONE))
{
(void) __port_deallocate(__mach_task_self(), cntl);
(void) __port_deallocate(__mach_task_self(), xxmemobj);
(void) __port_deallocate(__mach_task_self(), rdmemobj);
(void) __port_deallocate(__mach_task_self(), wrmemobj);
return;
}
m->file = file;
m->blksize = buf.stb_blksize;
m->cntl = cntl;
m->rdmemobj = rdmemobj;
m->wrmemobj = wrmemobj;
__io_funcs(stream).__close = mapped_close;
__io_funcs(stream).__seek = mapped_seek;
__room_funcs(stream).__input = mapped_input;
__room_funcs(stream).__output = mapped_output;
__cookie(stream) = (PTR) m;
__userbuf(stream) = 1; /* Tell stdio not to allocate a buffer. */
}