/* 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 #include #include #include 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. */ }