diff options
Diffstat (limited to 'REORG.TODO/libio/fileops.c')
-rw-r--r-- | REORG.TODO/libio/fileops.c | 1600 |
1 files changed, 1600 insertions, 0 deletions
diff --git a/REORG.TODO/libio/fileops.c b/REORG.TODO/libio/fileops.c new file mode 100644 index 0000000000..c55e196dd8 --- /dev/null +++ b/REORG.TODO/libio/fileops.c @@ -0,0 +1,1600 @@ +/* Copyright (C) 1993-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Per Bothner <bothner@cygnus.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. + + As a special exception, if you link the code in this file with + files compiled with a GNU compiler to produce an executable, + that does not cause the resulting executable to be covered by + the GNU Lesser General Public License. This exception does not + however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + This exception applies to code released by its copyright holders + in files containing the exception. */ + + +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif +#include "libioP.h" +#include <assert.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#if _LIBC +# include "../wcsmbs/wcsmbsload.h" +# include "../iconv/gconv_charset.h" +# include "../iconv/gconv_int.h" +# include <shlib-compat.h> +# include <not-cancel.h> +# include <kernel-features.h> +#endif +#ifndef errno +extern int errno; +#endif +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + + +#ifdef _LIBC +# define open(Name, Flags, Prot) __open (Name, Flags, Prot) +# define lseek(FD, Offset, Whence) __lseek (FD, Offset, Whence) +# define read(FD, Buf, NBytes) __read (FD, Buf, NBytes) +# define write(FD, Buf, NBytes) __write (FD, Buf, NBytes) +#else +# define _IO_new_do_write _IO_do_write +# define _IO_new_file_attach _IO_file_attach +# define _IO_new_file_close_it _IO_file_close_it +# define _IO_new_file_finish _IO_file_finish +# define _IO_new_file_fopen _IO_file_fopen +# define _IO_new_file_init _IO_file_init +# define _IO_new_file_setbuf _IO_file_setbuf +# define _IO_new_file_sync _IO_file_sync +# define _IO_new_file_overflow _IO_file_overflow +# define _IO_new_file_seekoff _IO_file_seekoff +# define _IO_new_file_underflow _IO_file_underflow +# define _IO_new_file_write _IO_file_write +# define _IO_new_file_xsputn _IO_file_xsputn +#endif + + +#ifdef _LIBC +extern struct __gconv_trans_data __libio_translit attribute_hidden; +#endif + + +/* An fstream can be in at most one of put mode, get mode, or putback mode. + Putback mode is a variant of get mode. + + In a filebuf, there is only one current position, instead of two + separate get and put pointers. In get mode, the current position + is that of gptr(); in put mode that of pptr(). + + The position in the buffer that corresponds to the position + in external file system is normally _IO_read_end, except in putback + mode, when it is _IO_save_end and also when the file is in append mode, + since switching from read to write mode automatically sends the position in + the external file system to the end of file. + If the field _fb._offset is >= 0, it gives the offset in + the file as a whole corresponding to eGptr(). (?) + + PUT MODE: + If a filebuf is in put mode, then all of _IO_read_ptr, _IO_read_end, + and _IO_read_base are equal to each other. These are usually equal + to _IO_buf_base, though not necessarily if we have switched from + get mode to put mode. (The reason is to maintain the invariant + that _IO_read_end corresponds to the external file position.) + _IO_write_base is non-NULL and usually equal to _IO_buf_base. + We also have _IO_write_end == _IO_buf_end, but only in fully buffered mode. + The un-flushed character are those between _IO_write_base and _IO_write_ptr. + + GET MODE: + If a filebuf is in get or putback mode, eback() != egptr(). + In get mode, the unread characters are between gptr() and egptr(). + The OS file position corresponds to that of egptr(). + + PUTBACK MODE: + Putback mode is used to remember "excess" characters that have + been sputbackc'd in a separate putback buffer. + In putback mode, the get buffer points to the special putback buffer. + The unread characters are the characters between gptr() and egptr() + in the putback buffer, as well as the area between save_gptr() + and save_egptr(), which point into the original reserve buffer. + (The pointers save_gptr() and save_egptr() are the values + of gptr() and egptr() at the time putback mode was entered.) + The OS position corresponds to that of save_egptr(). + + LINE BUFFERED OUTPUT: + During line buffered output, _IO_write_base==base() && epptr()==base(). + However, ptr() may be anywhere between base() and ebuf(). + This forces a call to filebuf::overflow(int C) on every put. + If there is more space in the buffer, and C is not a '\n', + then C is inserted, and pptr() incremented. + + UNBUFFERED STREAMS: + If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer. +*/ + +#define CLOSED_FILEBUF_FLAGS \ + (_IO_IS_FILEBUF+_IO_NO_READS+_IO_NO_WRITES+_IO_TIED_PUT_GET) + + +void +_IO_new_file_init_internal (struct _IO_FILE_plus *fp) +{ + /* POSIX.1 allows another file handle to be used to change the position + of our file descriptor. Hence we actually don't know the actual + position before we do the first fseek (and until a following fflush). */ + fp->file._offset = _IO_pos_BAD; + fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS; + + _IO_link_in (fp); + fp->file._fileno = -1; +} + +/* External version of _IO_new_file_init_internal which switches off + vtable validation. */ +void +_IO_new_file_init (struct _IO_FILE_plus *fp) +{ + IO_set_accept_foreign_vtables (&_IO_vtable_check); + _IO_new_file_init_internal (fp); +} + +int +_IO_new_file_close_it (_IO_FILE *fp) +{ + int write_status; + if (!_IO_file_is_open (fp)) + return EOF; + + if ((fp->_flags & _IO_NO_WRITES) == 0 + && (fp->_flags & _IO_CURRENTLY_PUTTING) != 0) + write_status = _IO_do_flush (fp); + else + write_status = 0; + + _IO_unsave_markers (fp); + + int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0 + ? _IO_SYSCLOSE (fp) : 0); + + /* Free buffer. */ +#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T + if (fp->_mode > 0) + { + if (_IO_have_wbackup (fp)) + _IO_free_wbackup_area (fp); + _IO_wsetb (fp, NULL, NULL, 0); + _IO_wsetg (fp, NULL, NULL, NULL); + _IO_wsetp (fp, NULL, NULL); + } +#endif + _IO_setb (fp, NULL, NULL, 0); + _IO_setg (fp, NULL, NULL, NULL); + _IO_setp (fp, NULL, NULL); + + _IO_un_link ((struct _IO_FILE_plus *) fp); + fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS; + fp->_fileno = -1; + fp->_offset = _IO_pos_BAD; + + return close_status ? close_status : write_status; +} +libc_hidden_ver (_IO_new_file_close_it, _IO_file_close_it) + +void +_IO_new_file_finish (_IO_FILE *fp, int dummy) +{ + if (_IO_file_is_open (fp)) + { + _IO_do_flush (fp); + if (!(fp->_flags & _IO_DELETE_DONT_CLOSE)) + _IO_SYSCLOSE (fp); + } + _IO_default_finish (fp, 0); +} +libc_hidden_ver (_IO_new_file_finish, _IO_file_finish) + +_IO_FILE * +_IO_file_open (_IO_FILE *fp, const char *filename, int posix_mode, int prot, + int read_write, int is32not64) +{ + int fdesc; +#ifdef _LIBC + if (__glibc_unlikely (fp->_flags2 & _IO_FLAGS2_NOTCANCEL)) + fdesc = open_not_cancel (filename, + posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot); + else + fdesc = open (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot); +#else + fdesc = open (filename, posix_mode, prot); +#endif + if (fdesc < 0) + return NULL; + fp->_fileno = fdesc; + _IO_mask_flags (fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING); + /* For append mode, send the file offset to the end of the file. Don't + update the offset cache though, since the file handle is not active. */ + if ((read_write & (_IO_IS_APPENDING | _IO_NO_READS)) + == (_IO_IS_APPENDING | _IO_NO_READS)) + { + _IO_off64_t new_pos = _IO_SYSSEEK (fp, 0, _IO_seek_end); + if (new_pos == _IO_pos_BAD && errno != ESPIPE) + { + close_not_cancel (fdesc); + return NULL; + } + } + _IO_link_in ((struct _IO_FILE_plus *) fp); + return fp; +} +libc_hidden_def (_IO_file_open) + +_IO_FILE * +_IO_new_file_fopen (_IO_FILE *fp, const char *filename, const char *mode, + int is32not64) +{ + int oflags = 0, omode; + int read_write; + int oprot = 0666; + int i; + _IO_FILE *result; +#ifdef _LIBC + const char *cs; + const char *last_recognized; +#endif + + if (_IO_file_is_open (fp)) + return 0; + switch (*mode) + { + case 'r': + omode = O_RDONLY; + read_write = _IO_NO_WRITES; + break; + case 'w': + omode = O_WRONLY; + oflags = O_CREAT|O_TRUNC; + read_write = _IO_NO_READS; + break; + case 'a': + omode = O_WRONLY; + oflags = O_CREAT|O_APPEND; + read_write = _IO_NO_READS|_IO_IS_APPENDING; + break; + default: + __set_errno (EINVAL); + return NULL; + } +#ifdef _LIBC + last_recognized = mode; +#endif + for (i = 1; i < 7; ++i) + { + switch (*++mode) + { + case '\0': + break; + case '+': + omode = O_RDWR; + read_write &= _IO_IS_APPENDING; +#ifdef _LIBC + last_recognized = mode; +#endif + continue; + case 'x': + oflags |= O_EXCL; +#ifdef _LIBC + last_recognized = mode; +#endif + continue; + case 'b': +#ifdef _LIBC + last_recognized = mode; +#endif + continue; + case 'm': + fp->_flags2 |= _IO_FLAGS2_MMAP; + continue; + case 'c': + fp->_flags2 |= _IO_FLAGS2_NOTCANCEL; + continue; + case 'e': + oflags |= O_CLOEXEC; + fp->_flags2 |= _IO_FLAGS2_CLOEXEC; + continue; + default: + /* Ignore. */ + continue; + } + break; + } + + result = _IO_file_open (fp, filename, omode|oflags, oprot, read_write, + is32not64); + + if (result != NULL) + { + /* Test whether the mode string specifies the conversion. */ + cs = strstr (last_recognized + 1, ",ccs="); + if (cs != NULL) + { + /* Yep. Load the appropriate conversions and set the orientation + to wide. */ + struct gconv_fcts fcts; + struct _IO_codecvt *cc; + char *endp = __strchrnul (cs + 5, ','); + char *ccs = malloc (endp - (cs + 5) + 3); + + if (ccs == NULL) + { + int malloc_err = errno; /* Whatever malloc failed with. */ + (void) _IO_file_close_it (fp); + __set_errno (malloc_err); + return NULL; + } + + *((char *) __mempcpy (ccs, cs + 5, endp - (cs + 5))) = '\0'; + strip (ccs, ccs); + + if (__wcsmbs_named_conv (&fcts, ccs[2] == '\0' + ? upstr (ccs, cs + 5) : ccs) != 0) + { + /* Something went wrong, we cannot load the conversion modules. + This means we cannot proceed since the user explicitly asked + for these. */ + (void) _IO_file_close_it (fp); + free (ccs); + __set_errno (EINVAL); + return NULL; + } + + free (ccs); + + assert (fcts.towc_nsteps == 1); + assert (fcts.tomb_nsteps == 1); + + fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end; + fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base; + + /* Clear the state. We start all over again. */ + memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t)); + memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t)); + + cc = fp->_codecvt = &fp->_wide_data->_codecvt; + + /* The functions are always the same. */ + *cc = __libio_codecvt; + + cc->__cd_in.__cd.__nsteps = fcts.towc_nsteps; + cc->__cd_in.__cd.__steps = fcts.towc; + + cc->__cd_in.__cd.__data[0].__invocation_counter = 0; + cc->__cd_in.__cd.__data[0].__internal_use = 1; + cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST; + cc->__cd_in.__cd.__data[0].__statep = &result->_wide_data->_IO_state; + + cc->__cd_out.__cd.__nsteps = fcts.tomb_nsteps; + cc->__cd_out.__cd.__steps = fcts.tomb; + + cc->__cd_out.__cd.__data[0].__invocation_counter = 0; + cc->__cd_out.__cd.__data[0].__internal_use = 1; + cc->__cd_out.__cd.__data[0].__flags + = __GCONV_IS_LAST | __GCONV_TRANSLIT; + cc->__cd_out.__cd.__data[0].__statep = + &result->_wide_data->_IO_state; + + /* From now on use the wide character callback functions. */ + _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable; + + /* Set the mode now. */ + result->_mode = 1; + } + } + + return result; +} +libc_hidden_ver (_IO_new_file_fopen, _IO_file_fopen) + +_IO_FILE * +_IO_new_file_attach (_IO_FILE *fp, int fd) +{ + if (_IO_file_is_open (fp)) + return NULL; + fp->_fileno = fd; + fp->_flags &= ~(_IO_NO_READS+_IO_NO_WRITES); + fp->_flags |= _IO_DELETE_DONT_CLOSE; + /* Get the current position of the file. */ + /* We have to do that since that may be junk. */ + fp->_offset = _IO_pos_BAD; + int save_errno = errno; + if (_IO_SEEKOFF (fp, (_IO_off64_t)0, _IO_seek_cur, _IOS_INPUT|_IOS_OUTPUT) + == _IO_pos_BAD && errno != ESPIPE) + return NULL; + __set_errno (save_errno); + return fp; +} +libc_hidden_ver (_IO_new_file_attach, _IO_file_attach) + +_IO_FILE * +_IO_new_file_setbuf (_IO_FILE *fp, char *p, _IO_ssize_t len) +{ + if (_IO_default_setbuf (fp, p, len) == NULL) + return NULL; + + fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end + = fp->_IO_buf_base; + _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); + + return fp; +} +libc_hidden_ver (_IO_new_file_setbuf, _IO_file_setbuf) + + +_IO_FILE * +_IO_file_setbuf_mmap (_IO_FILE *fp, char *p, _IO_ssize_t len) +{ + _IO_FILE *result; + + /* Change the function table. */ + _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps; + fp->_wide_data->_wide_vtable = &_IO_wfile_jumps; + + /* And perform the normal operation. */ + result = _IO_new_file_setbuf (fp, p, len); + + /* If the call failed, restore to using mmap. */ + if (result == NULL) + { + _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_mmap; + fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap; + } + + return result; +} + +static _IO_size_t new_do_write (_IO_FILE *, const char *, _IO_size_t); + +/* Write TO_DO bytes from DATA to FP. + Then mark FP as having empty buffers. */ + +int +_IO_new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do) +{ + return (to_do == 0 + || (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF; +} +libc_hidden_ver (_IO_new_do_write, _IO_do_write) + +static +_IO_size_t +new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do) +{ + _IO_size_t count; + if (fp->_flags & _IO_IS_APPENDING) + /* On a system without a proper O_APPEND implementation, + you would need to sys_seek(0, SEEK_END) here, but is + not needed nor desirable for Unix- or Posix-like systems. + Instead, just indicate that offset (before and after) is + unpredictable. */ + fp->_offset = _IO_pos_BAD; + else if (fp->_IO_read_end != fp->_IO_write_base) + { + _IO_off64_t new_pos + = _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1); + if (new_pos == _IO_pos_BAD) + return 0; + fp->_offset = new_pos; + } + count = _IO_SYSWRITE (fp, data, to_do); + if (fp->_cur_column && count) + fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1; + _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); + fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base; + fp->_IO_write_end = (fp->_mode <= 0 + && (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED)) + ? fp->_IO_buf_base : fp->_IO_buf_end); + return count; +} + +int +_IO_new_file_underflow (_IO_FILE *fp) +{ + _IO_ssize_t count; +#if 0 + /* SysV does not make this test; take it out for compatibility */ + if (fp->_flags & _IO_EOF_SEEN) + return (EOF); +#endif + + if (fp->_flags & _IO_NO_READS) + { + fp->_flags |= _IO_ERR_SEEN; + __set_errno (EBADF); + return EOF; + } + if (fp->_IO_read_ptr < fp->_IO_read_end) + return *(unsigned char *) fp->_IO_read_ptr; + + if (fp->_IO_buf_base == NULL) + { + /* Maybe we already have a push back pointer. */ + if (fp->_IO_save_base != NULL) + { + free (fp->_IO_save_base); + fp->_flags &= ~_IO_IN_BACKUP; + } + _IO_doallocbuf (fp); + } + + /* Flush all line buffered files before reading. */ + /* FIXME This can/should be moved to genops ?? */ + if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED)) + { +#if 0 + _IO_flush_all_linebuffered (); +#else + /* We used to flush all line-buffered stream. This really isn't + required by any standard. My recollection is that + traditional Unix systems did this for stdout. stderr better + not be line buffered. So we do just that here + explicitly. --drepper */ + _IO_acquire_lock (_IO_stdout); + + if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) + == (_IO_LINKED | _IO_LINE_BUF)) + _IO_OVERFLOW (_IO_stdout, EOF); + + _IO_release_lock (_IO_stdout); +#endif + } + + _IO_switch_to_get_mode (fp); + + /* This is very tricky. We have to adjust those + pointers before we call _IO_SYSREAD () since + we may longjump () out while waiting for + input. Those pointers may be screwed up. H.J. */ + fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base; + fp->_IO_read_end = fp->_IO_buf_base; + fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end + = fp->_IO_buf_base; + + count = _IO_SYSREAD (fp, fp->_IO_buf_base, + fp->_IO_buf_end - fp->_IO_buf_base); + if (count <= 0) + { + if (count == 0) + fp->_flags |= _IO_EOF_SEEN; + else + fp->_flags |= _IO_ERR_SEEN, count = 0; + } + fp->_IO_read_end += count; + if (count == 0) + { + /* If a stream is read to EOF, the calling application may switch active + handles. As a result, our offset cache would no longer be valid, so + unset it. */ + fp->_offset = _IO_pos_BAD; + return EOF; + } + if (fp->_offset != _IO_pos_BAD) + _IO_pos_adjust (fp->_offset, count); + return *(unsigned char *) fp->_IO_read_ptr; +} +libc_hidden_ver (_IO_new_file_underflow, _IO_file_underflow) + +/* Guts of underflow callback if we mmap the file. This stats the file and + updates the stream state to match. In the normal case we return zero. + If the file is no longer eligible for mmap, its jump tables are reset to + the vanilla ones and we return nonzero. */ +static int +mmap_remap_check (_IO_FILE *fp) +{ + struct stat64 st; + + if (_IO_SYSSTAT (fp, &st) == 0 + && S_ISREG (st.st_mode) && st.st_size != 0 + /* Limit the file size to 1MB for 32-bit machines. */ + && (sizeof (ptrdiff_t) > 4 || st.st_size < 1*1024*1024)) + { + const size_t pagesize = __getpagesize (); +# define ROUNDED(x) (((x) + pagesize - 1) & ~(pagesize - 1)) + if (ROUNDED (st.st_size) < ROUNDED (fp->_IO_buf_end + - fp->_IO_buf_base)) + { + /* We can trim off some pages past the end of the file. */ + (void) __munmap (fp->_IO_buf_base + ROUNDED (st.st_size), + ROUNDED (fp->_IO_buf_end - fp->_IO_buf_base) + - ROUNDED (st.st_size)); + fp->_IO_buf_end = fp->_IO_buf_base + st.st_size; + } + else if (ROUNDED (st.st_size) > ROUNDED (fp->_IO_buf_end + - fp->_IO_buf_base)) + { + /* The file added some pages. We need to remap it. */ + void *p; +#ifdef _G_HAVE_MREMAP + p = __mremap (fp->_IO_buf_base, ROUNDED (fp->_IO_buf_end + - fp->_IO_buf_base), + ROUNDED (st.st_size), MREMAP_MAYMOVE); + if (p == MAP_FAILED) + { + (void) __munmap (fp->_IO_buf_base, + fp->_IO_buf_end - fp->_IO_buf_base); + goto punt; + } +#else + (void) __munmap (fp->_IO_buf_base, + fp->_IO_buf_end - fp->_IO_buf_base); + p = __mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED, + fp->_fileno, 0); + if (p == MAP_FAILED) + goto punt; +#endif + fp->_IO_buf_base = p; + fp->_IO_buf_end = fp->_IO_buf_base + st.st_size; + } + else + { + /* The number of pages didn't change. */ + fp->_IO_buf_end = fp->_IO_buf_base + st.st_size; + } +# undef ROUNDED + + fp->_offset -= fp->_IO_read_end - fp->_IO_read_ptr; + _IO_setg (fp, fp->_IO_buf_base, + fp->_offset < fp->_IO_buf_end - fp->_IO_buf_base + ? fp->_IO_buf_base + fp->_offset : fp->_IO_buf_end, + fp->_IO_buf_end); + + /* If we are already positioned at or past the end of the file, don't + change the current offset. If not, seek past what we have mapped, + mimicking the position left by a normal underflow reading into its + buffer until EOF. */ + + if (fp->_offset < fp->_IO_buf_end - fp->_IO_buf_base) + { + if (__lseek64 (fp->_fileno, fp->_IO_buf_end - fp->_IO_buf_base, + SEEK_SET) + != fp->_IO_buf_end - fp->_IO_buf_base) + fp->_flags |= _IO_ERR_SEEN; + else + fp->_offset = fp->_IO_buf_end - fp->_IO_buf_base; + } + + return 0; + } + else + { + /* Life is no longer good for mmap. Punt it. */ + (void) __munmap (fp->_IO_buf_base, + fp->_IO_buf_end - fp->_IO_buf_base); + punt: + fp->_IO_buf_base = fp->_IO_buf_end = NULL; + _IO_setg (fp, NULL, NULL, NULL); + if (fp->_mode <= 0) + _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps; + else + _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps; + fp->_wide_data->_wide_vtable = &_IO_wfile_jumps; + + return 1; + } +} + +/* Special callback replacing the underflow callbacks if we mmap the file. */ +int +_IO_file_underflow_mmap (_IO_FILE *fp) +{ + if (fp->_IO_read_ptr < fp->_IO_read_end) + return *(unsigned char *) fp->_IO_read_ptr; + + if (__glibc_unlikely (mmap_remap_check (fp))) + /* We punted to the regular file functions. */ + return _IO_UNDERFLOW (fp); + + if (fp->_IO_read_ptr < fp->_IO_read_end) + return *(unsigned char *) fp->_IO_read_ptr; + + fp->_flags |= _IO_EOF_SEEN; + return EOF; +} + +static void +decide_maybe_mmap (_IO_FILE *fp) +{ + /* We use the file in read-only mode. This could mean we can + mmap the file and use it without any copying. But not all + file descriptors are for mmap-able objects and on 32-bit + machines we don't want to map files which are too large since + this would require too much virtual memory. */ + struct stat64 st; + + if (_IO_SYSSTAT (fp, &st) == 0 + && S_ISREG (st.st_mode) && st.st_size != 0 + /* Limit the file size to 1MB for 32-bit machines. */ + && (sizeof (ptrdiff_t) > 4 || st.st_size < 1*1024*1024) + /* Sanity check. */ + && (fp->_offset == _IO_pos_BAD || fp->_offset <= st.st_size)) + { + /* Try to map the file. */ + void *p; + + p = __mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED, fp->_fileno, 0); + if (p != MAP_FAILED) + { + /* OK, we managed to map the file. Set the buffer up and use a + special jump table with simplified underflow functions which + never tries to read anything from the file. */ + + if (__lseek64 (fp->_fileno, st.st_size, SEEK_SET) != st.st_size) + { + (void) __munmap (p, st.st_size); + fp->_offset = _IO_pos_BAD; + } + else + { + _IO_setb (fp, p, (char *) p + st.st_size, 0); + + if (fp->_offset == _IO_pos_BAD) + fp->_offset = 0; + + _IO_setg (fp, p, p + fp->_offset, p + st.st_size); + fp->_offset = st.st_size; + + if (fp->_mode <= 0) + _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_mmap; + else + _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_mmap; + fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap; + + return; + } + } + } + + /* We couldn't use mmap, so revert to the vanilla file operations. */ + + if (fp->_mode <= 0) + _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps; + else + _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps; + fp->_wide_data->_wide_vtable = &_IO_wfile_jumps; +} + +int +_IO_file_underflow_maybe_mmap (_IO_FILE *fp) +{ + /* This is the first read attempt. Choose mmap or vanilla operations + and then punt to the chosen underflow routine. */ + decide_maybe_mmap (fp); + return _IO_UNDERFLOW (fp); +} + + +int +_IO_new_file_overflow (_IO_FILE *f, int ch) +{ + if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ + { + f->_flags |= _IO_ERR_SEEN; + __set_errno (EBADF); + return EOF; + } + /* If currently reading or no buffer allocated. */ + if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL) + { + /* Allocate a buffer if needed. */ + if (f->_IO_write_base == NULL) + { + _IO_doallocbuf (f); + _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base); + } + /* Otherwise must be currently reading. + If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end, + logically slide the buffer forwards one block (by setting the + read pointers to all point at the beginning of the block). This + makes room for subsequent output. + Otherwise, set the read pointers to _IO_read_end (leaving that + alone, so it can continue to correspond to the external position). */ + if (__glibc_unlikely (_IO_in_backup (f))) + { + size_t nbackup = f->_IO_read_end - f->_IO_read_ptr; + _IO_free_backup_area (f); + f->_IO_read_base -= MIN (nbackup, + f->_IO_read_base - f->_IO_buf_base); + f->_IO_read_ptr = f->_IO_read_base; + } + + if (f->_IO_read_ptr == f->_IO_buf_end) + f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base; + f->_IO_write_ptr = f->_IO_read_ptr; + f->_IO_write_base = f->_IO_write_ptr; + f->_IO_write_end = f->_IO_buf_end; + f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end; + + f->_flags |= _IO_CURRENTLY_PUTTING; + if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED)) + f->_IO_write_end = f->_IO_write_ptr; + } + if (ch == EOF) + return _IO_do_write (f, f->_IO_write_base, + f->_IO_write_ptr - f->_IO_write_base); + if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */ + if (_IO_do_flush (f) == EOF) + return EOF; + *f->_IO_write_ptr++ = ch; + if ((f->_flags & _IO_UNBUFFERED) + || ((f->_flags & _IO_LINE_BUF) && ch == '\n')) + if (_IO_do_write (f, f->_IO_write_base, + f->_IO_write_ptr - f->_IO_write_base) == EOF) + return EOF; + return (unsigned char) ch; +} +libc_hidden_ver (_IO_new_file_overflow, _IO_file_overflow) + +int +_IO_new_file_sync (_IO_FILE *fp) +{ + _IO_ssize_t delta; + int retval = 0; + + /* char* ptr = cur_ptr(); */ + if (fp->_IO_write_ptr > fp->_IO_write_base) + if (_IO_do_flush(fp)) return EOF; + delta = fp->_IO_read_ptr - fp->_IO_read_end; + if (delta != 0) + { +#ifdef TODO + if (_IO_in_backup (fp)) + delta -= eGptr () - Gbase (); +#endif + _IO_off64_t new_pos = _IO_SYSSEEK (fp, delta, 1); + if (new_pos != (_IO_off64_t) EOF) + fp->_IO_read_end = fp->_IO_read_ptr; +#ifdef ESPIPE + else if (errno == ESPIPE) + ; /* Ignore error from unseekable devices. */ +#endif + else + retval = EOF; + } + if (retval != EOF) + fp->_offset = _IO_pos_BAD; + /* FIXME: Cleanup - can this be shared? */ + /* setg(base(), ptr, ptr); */ + return retval; +} +libc_hidden_ver (_IO_new_file_sync, _IO_file_sync) + +static int +_IO_file_sync_mmap (_IO_FILE *fp) +{ + if (fp->_IO_read_ptr != fp->_IO_read_end) + { +#ifdef TODO + if (_IO_in_backup (fp)) + delta -= eGptr () - Gbase (); +#endif + if (__lseek64 (fp->_fileno, fp->_IO_read_ptr - fp->_IO_buf_base, + SEEK_SET) + != fp->_IO_read_ptr - fp->_IO_buf_base) + { + fp->_flags |= _IO_ERR_SEEN; + return EOF; + } + } + fp->_offset = fp->_IO_read_ptr - fp->_IO_buf_base; + fp->_IO_read_end = fp->_IO_read_ptr = fp->_IO_read_base; + return 0; +} + +/* ftell{,o} implementation. The only time we modify the state of the stream + is when we have unflushed writes. In that case we seek to the end and + record that offset in the stream object. */ +static _IO_off64_t +do_ftell (_IO_FILE *fp) +{ + _IO_off64_t result, offset = 0; + + /* No point looking at unflushed data if we haven't allocated buffers + yet. */ + if (fp->_IO_buf_base != NULL) + { + bool unflushed_writes = fp->_IO_write_ptr > fp->_IO_write_base; + + bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING; + + /* When we have unflushed writes in append mode, seek to the end of the + file and record that offset. This is the only time we change the file + stream state and it is safe since the file handle is active. */ + if (unflushed_writes && append_mode) + { + result = _IO_SYSSEEK (fp, 0, _IO_seek_end); + if (result == _IO_pos_BAD) + return EOF; + else + fp->_offset = result; + } + + /* Adjust for unflushed data. */ + if (!unflushed_writes) + offset -= fp->_IO_read_end - fp->_IO_read_ptr; + /* We don't trust _IO_read_end to represent the current file offset when + writing in append mode because the value would have to be shifted to + the end of the file during a flush. Use the write base instead, along + with the new offset we got above when we did a seek to the end of the + file. */ + else if (append_mode) + offset += fp->_IO_write_ptr - fp->_IO_write_base; + /* For all other modes, _IO_read_end represents the file offset. */ + else + offset += fp->_IO_write_ptr - fp->_IO_read_end; + } + + if (fp->_offset != _IO_pos_BAD) + result = fp->_offset; + else + result = _IO_SYSSEEK (fp, 0, _IO_seek_cur); + + if (result == EOF) + return result; + + result += offset; + + if (result < 0) + { + __set_errno (EINVAL); + return EOF; + } + + return result; +} + +_IO_off64_t +_IO_new_file_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode) +{ + _IO_off64_t result; + _IO_off64_t delta, new_offset; + long count; + + /* Short-circuit into a separate function. We don't want to mix any + functionality and we don't want to touch anything inside the FILE + object. */ + if (mode == 0) + return do_ftell (fp); + + /* POSIX.1 8.2.3.7 says that after a call the fflush() the file + offset of the underlying file must be exact. */ + int must_be_exact = (fp->_IO_read_base == fp->_IO_read_end + && fp->_IO_write_base == fp->_IO_write_ptr); + + bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base + || _IO_in_put_mode (fp)); + + /* Flush unwritten characters. + (This may do an unneeded write if we seek within the buffer. + But to be able to switch to reading, we would need to set + egptr to pptr. That can't be done in the current design, + which assumes file_ptr() is eGptr. Anyway, since we probably + end up flushing when we close(), it doesn't make much difference.) + FIXME: simulate mem-mapped files. */ + if (was_writing && _IO_switch_to_get_mode (fp)) + return EOF; + + if (fp->_IO_buf_base == NULL) + { + /* It could be that we already have a pushback buffer. */ + if (fp->_IO_read_base != NULL) + { + free (fp->_IO_read_base); + fp->_flags &= ~_IO_IN_BACKUP; + } + _IO_doallocbuf (fp); + _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base); + _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); + } + + switch (dir) + { + case _IO_seek_cur: + /* Adjust for read-ahead (bytes is buffer). */ + offset -= fp->_IO_read_end - fp->_IO_read_ptr; + + if (fp->_offset == _IO_pos_BAD) + goto dumb; + /* Make offset absolute, assuming current pointer is file_ptr(). */ + offset += fp->_offset; + if (offset < 0) + { + __set_errno (EINVAL); + return EOF; + } + + dir = _IO_seek_set; + break; + case _IO_seek_set: + break; + case _IO_seek_end: + { + struct stat64 st; + if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode)) + { + offset += st.st_size; + dir = _IO_seek_set; + } + else + goto dumb; + } + } + /* At this point, dir==_IO_seek_set. */ + + /* If destination is within current buffer, optimize: */ + if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL + && !_IO_in_backup (fp)) + { + _IO_off64_t start_offset = (fp->_offset + - (fp->_IO_read_end - fp->_IO_buf_base)); + if (offset >= start_offset && offset < fp->_offset) + { + _IO_setg (fp, fp->_IO_buf_base, + fp->_IO_buf_base + (offset - start_offset), + fp->_IO_read_end); + _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base); + + _IO_mask_flags (fp, 0, _IO_EOF_SEEN); + goto resync; + } + } + + if (fp->_flags & _IO_NO_READS) + goto dumb; + + /* Try to seek to a block boundary, to improve kernel page management. */ + new_offset = offset & ~(fp->_IO_buf_end - fp->_IO_buf_base - 1); + delta = offset - new_offset; + if (delta > fp->_IO_buf_end - fp->_IO_buf_base) + { + new_offset = offset; + delta = 0; + } + result = _IO_SYSSEEK (fp, new_offset, 0); + if (result < 0) + return EOF; + if (delta == 0) + count = 0; + else + { + count = _IO_SYSREAD (fp, fp->_IO_buf_base, + (must_be_exact + ? delta : fp->_IO_buf_end - fp->_IO_buf_base)); + if (count < delta) + { + /* We weren't allowed to read, but try to seek the remainder. */ + offset = count == EOF ? delta : delta-count; + dir = _IO_seek_cur; + goto dumb; + } + } + _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + delta, + fp->_IO_buf_base + count); + _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base); + fp->_offset = result + count; + _IO_mask_flags (fp, 0, _IO_EOF_SEEN); + return offset; + dumb: + + _IO_unsave_markers (fp); + result = _IO_SYSSEEK (fp, offset, dir); + if (result != EOF) + { + _IO_mask_flags (fp, 0, _IO_EOF_SEEN); + fp->_offset = result; + _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); + _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base); + } + return result; + +resync: + /* We need to do it since it is possible that the file offset in + the kernel may be changed behind our back. It may happen when + we fopen a file and then do a fork. One process may access the + file and the kernel file offset will be changed. */ + if (fp->_offset >= 0) + _IO_SYSSEEK (fp, fp->_offset, 0); + + return offset; +} +libc_hidden_ver (_IO_new_file_seekoff, _IO_file_seekoff) + +_IO_off64_t +_IO_file_seekoff_mmap (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode) +{ + _IO_off64_t result; + + /* If we are only interested in the current position, calculate it and + return right now. This calculation does the right thing when we are + using a pushback buffer, but in the usual case has the same value as + (fp->_IO_read_ptr - fp->_IO_buf_base). */ + if (mode == 0) + return fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr); + + switch (dir) + { + case _IO_seek_cur: + /* Adjust for read-ahead (bytes is buffer). */ + offset += fp->_IO_read_ptr - fp->_IO_read_base; + break; + case _IO_seek_set: + break; + case _IO_seek_end: + offset += fp->_IO_buf_end - fp->_IO_buf_base; + break; + } + /* At this point, dir==_IO_seek_set. */ + + if (offset < 0) + { + /* No negative offsets are valid. */ + __set_errno (EINVAL); + return EOF; + } + + result = _IO_SYSSEEK (fp, offset, 0); + if (result < 0) + return EOF; + + if (offset > fp->_IO_buf_end - fp->_IO_buf_base) + /* One can fseek arbitrarily past the end of the file + and it is meaningless until one attempts to read. + Leave the buffer pointers in EOF state until underflow. */ + _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_end, fp->_IO_buf_end); + else + /* Adjust the read pointers to match the file position, + but so the next read attempt will call underflow. */ + _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + offset, + fp->_IO_buf_base + offset); + + fp->_offset = result; + + _IO_mask_flags (fp, 0, _IO_EOF_SEEN); + + return offset; +} + +static _IO_off64_t +_IO_file_seekoff_maybe_mmap (_IO_FILE *fp, _IO_off64_t offset, int dir, + int mode) +{ + /* We only get here when we haven't tried to read anything yet. + So there is nothing more useful for us to do here than just + the underlying lseek call. */ + + _IO_off64_t result = _IO_SYSSEEK (fp, offset, dir); + if (result < 0) + return EOF; + + fp->_offset = result; + return result; +} + +_IO_ssize_t +_IO_file_read (_IO_FILE *fp, void *buf, _IO_ssize_t size) +{ + return (__builtin_expect (fp->_flags2 & _IO_FLAGS2_NOTCANCEL, 0) + ? read_not_cancel (fp->_fileno, buf, size) + : read (fp->_fileno, buf, size)); +} +libc_hidden_def (_IO_file_read) + +_IO_off64_t +_IO_file_seek (_IO_FILE *fp, _IO_off64_t offset, int dir) +{ + return __lseek64 (fp->_fileno, offset, dir); +} +libc_hidden_def (_IO_file_seek) + +int +_IO_file_stat (_IO_FILE *fp, void *st) +{ + return __fxstat64 (_STAT_VER, fp->_fileno, (struct stat64 *) st); +} +libc_hidden_def (_IO_file_stat) + +int +_IO_file_close_mmap (_IO_FILE *fp) +{ + /* In addition to closing the file descriptor we have to unmap the file. */ + (void) __munmap (fp->_IO_buf_base, fp->_IO_buf_end - fp->_IO_buf_base); + fp->_IO_buf_base = fp->_IO_buf_end = NULL; + /* Cancelling close should be avoided if possible since it leaves an + unrecoverable state behind. */ + return close_not_cancel (fp->_fileno); +} + +int +_IO_file_close (_IO_FILE *fp) +{ + /* Cancelling close should be avoided if possible since it leaves an + unrecoverable state behind. */ + return close_not_cancel (fp->_fileno); +} +libc_hidden_def (_IO_file_close) + +_IO_ssize_t +_IO_new_file_write (_IO_FILE *f, const void *data, _IO_ssize_t n) +{ + _IO_ssize_t to_do = n; + while (to_do > 0) + { + _IO_ssize_t count = (__builtin_expect (f->_flags2 + & _IO_FLAGS2_NOTCANCEL, 0) + ? write_not_cancel (f->_fileno, data, to_do) + : write (f->_fileno, data, to_do)); + if (count < 0) + { + f->_flags |= _IO_ERR_SEEN; + break; + } + to_do -= count; + data = (void *) ((char *) data + count); + } + n -= to_do; + if (f->_offset >= 0) + f->_offset += n; + return n; +} + +_IO_size_t +_IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n) +{ + const char *s = (const char *) data; + _IO_size_t to_do = n; + int must_flush = 0; + _IO_size_t count = 0; + + if (n <= 0) + return 0; + /* This is an optimized implementation. + If the amount to be written straddles a block boundary + (or the filebuf is unbuffered), use sys_write directly. */ + + /* First figure out how much space is available in the buffer. */ + if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) + { + count = f->_IO_buf_end - f->_IO_write_ptr; + if (count >= n) + { + const char *p; + for (p = s + n; p > s; ) + { + if (*--p == '\n') + { + count = p - s + 1; + must_flush = 1; + break; + } + } + } + } + else if (f->_IO_write_end > f->_IO_write_ptr) + count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */ + + /* Then fill the buffer. */ + if (count > 0) + { + if (count > to_do) + count = to_do; +#ifdef _LIBC + f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count); +#else + memcpy (f->_IO_write_ptr, s, count); + f->_IO_write_ptr += count; +#endif + s += count; + to_do -= count; + } + if (to_do + must_flush > 0) + { + _IO_size_t block_size, do_write; + /* Next flush the (full) buffer. */ + if (_IO_OVERFLOW (f, EOF) == EOF) + /* If nothing else has to be written we must not signal the + caller that everything has been written. */ + return to_do == 0 ? EOF : n - to_do; + + /* Try to maintain alignment: write a whole number of blocks. */ + block_size = f->_IO_buf_end - f->_IO_buf_base; + do_write = to_do - (block_size >= 128 ? to_do % block_size : 0); + + if (do_write) + { + count = new_do_write (f, s, do_write); + to_do -= count; + if (count < do_write) + return n - to_do; + } + + /* Now write out the remainder. Normally, this will fit in the + buffer, but it's somewhat messier for line-buffered files, + so we let _IO_default_xsputn handle the general case. */ + if (to_do) + to_do -= _IO_default_xsputn (f, s+do_write, to_do); + } + return n - to_do; +} +libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn) + +_IO_size_t +_IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n) +{ + _IO_size_t want, have; + _IO_ssize_t count; + char *s = data; + + want = n; + + if (fp->_IO_buf_base == NULL) + { + /* Maybe we already have a push back pointer. */ + if (fp->_IO_save_base != NULL) + { + free (fp->_IO_save_base); + fp->_flags &= ~_IO_IN_BACKUP; + } + _IO_doallocbuf (fp); + } + + while (want > 0) + { + have = fp->_IO_read_end - fp->_IO_read_ptr; + if (want <= have) + { + memcpy (s, fp->_IO_read_ptr, want); + fp->_IO_read_ptr += want; + want = 0; + } + else + { + if (have > 0) + { +#ifdef _LIBC + s = __mempcpy (s, fp->_IO_read_ptr, have); +#else + memcpy (s, fp->_IO_read_ptr, have); + s += have; +#endif + want -= have; + fp->_IO_read_ptr += have; + } + + /* Check for backup and repeat */ + if (_IO_in_backup (fp)) + { + _IO_switch_to_main_get_area (fp); + continue; + } + + /* If we now want less than a buffer, underflow and repeat + the copy. Otherwise, _IO_SYSREAD directly to + the user buffer. */ + if (fp->_IO_buf_base + && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)) + { + if (__underflow (fp) == EOF) + break; + + continue; + } + + /* These must be set before the sysread as we might longjmp out + waiting for input. */ + _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); + _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base); + + /* Try to maintain alignment: read a whole number of blocks. */ + count = want; + if (fp->_IO_buf_base) + { + _IO_size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base; + if (block_size >= 128) + count -= want % block_size; + } + + count = _IO_SYSREAD (fp, s, count); + if (count <= 0) + { + if (count == 0) + fp->_flags |= _IO_EOF_SEEN; + else + fp->_flags |= _IO_ERR_SEEN; + + break; + } + + s += count; + want -= count; + if (fp->_offset != _IO_pos_BAD) + _IO_pos_adjust (fp->_offset, count); + } + } + + return n - want; +} +libc_hidden_def (_IO_file_xsgetn) + +static _IO_size_t +_IO_file_xsgetn_mmap (_IO_FILE *fp, void *data, _IO_size_t n) +{ + _IO_size_t have; + char *read_ptr = fp->_IO_read_ptr; + char *s = (char *) data; + + have = fp->_IO_read_end - fp->_IO_read_ptr; + + if (have < n) + { + if (__glibc_unlikely (_IO_in_backup (fp))) + { +#ifdef _LIBC + s = __mempcpy (s, read_ptr, have); +#else + memcpy (s, read_ptr, have); + s += have; +#endif + n -= have; + _IO_switch_to_main_get_area (fp); + read_ptr = fp->_IO_read_ptr; + have = fp->_IO_read_end - fp->_IO_read_ptr; + } + + if (have < n) + { + /* Check that we are mapping all of the file, in case it grew. */ + if (__glibc_unlikely (mmap_remap_check (fp))) + /* We punted mmap, so complete with the vanilla code. */ + return s - (char *) data + _IO_XSGETN (fp, data, n); + + read_ptr = fp->_IO_read_ptr; + have = fp->_IO_read_end - read_ptr; + } + } + + if (have < n) + fp->_flags |= _IO_EOF_SEEN; + + if (have != 0) + { + have = MIN (have, n); +#ifdef _LIBC + s = __mempcpy (s, read_ptr, have); +#else + memcpy (s, read_ptr, have); + s += have; +#endif + fp->_IO_read_ptr = read_ptr + have; + } + + return s - (char *) data; +} + +static _IO_size_t +_IO_file_xsgetn_maybe_mmap (_IO_FILE *fp, void *data, _IO_size_t n) +{ + /* We only get here if this is the first attempt to read something. + Decide which operations to use and then punt to the chosen one. */ + + decide_maybe_mmap (fp); + return _IO_XSGETN (fp, data, n); +} + +#ifdef _LIBC +versioned_symbol (libc, _IO_new_do_write, _IO_do_write, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_attach, _IO_file_attach, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_close_it, _IO_file_close_it, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_finish, _IO_file_finish, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_fopen, _IO_file_fopen, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_init, _IO_file_init, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_setbuf, _IO_file_setbuf, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_sync, _IO_file_sync, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_overflow, _IO_file_overflow, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_seekoff, _IO_file_seekoff, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_underflow, _IO_file_underflow, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_write, _IO_file_write, GLIBC_2_1); +versioned_symbol (libc, _IO_new_file_xsputn, _IO_file_xsputn, GLIBC_2_1); +#endif + +const struct _IO_jump_t _IO_file_jumps libio_vtable = +{ + JUMP_INIT_DUMMY, + JUMP_INIT(finish, _IO_file_finish), + JUMP_INIT(overflow, _IO_file_overflow), + JUMP_INIT(underflow, _IO_file_underflow), + JUMP_INIT(uflow, _IO_default_uflow), + JUMP_INIT(pbackfail, _IO_default_pbackfail), + JUMP_INIT(xsputn, _IO_file_xsputn), + JUMP_INIT(xsgetn, _IO_file_xsgetn), + JUMP_INIT(seekoff, _IO_new_file_seekoff), + JUMP_INIT(seekpos, _IO_default_seekpos), + JUMP_INIT(setbuf, _IO_new_file_setbuf), + JUMP_INIT(sync, _IO_new_file_sync), + JUMP_INIT(doallocate, _IO_file_doallocate), + JUMP_INIT(read, _IO_file_read), + JUMP_INIT(write, _IO_new_file_write), + JUMP_INIT(seek, _IO_file_seek), + JUMP_INIT(close, _IO_file_close), + JUMP_INIT(stat, _IO_file_stat), + JUMP_INIT(showmanyc, _IO_default_showmanyc), + JUMP_INIT(imbue, _IO_default_imbue) +}; +libc_hidden_data_def (_IO_file_jumps) + +const struct _IO_jump_t _IO_file_jumps_mmap libio_vtable = +{ + JUMP_INIT_DUMMY, + JUMP_INIT(finish, _IO_file_finish), + JUMP_INIT(overflow, _IO_file_overflow), + JUMP_INIT(underflow, _IO_file_underflow_mmap), + JUMP_INIT(uflow, _IO_default_uflow), + JUMP_INIT(pbackfail, _IO_default_pbackfail), + JUMP_INIT(xsputn, _IO_new_file_xsputn), + JUMP_INIT(xsgetn, _IO_file_xsgetn_mmap), + JUMP_INIT(seekoff, _IO_file_seekoff_mmap), + JUMP_INIT(seekpos, _IO_default_seekpos), + JUMP_INIT(setbuf, (_IO_setbuf_t) _IO_file_setbuf_mmap), + JUMP_INIT(sync, _IO_file_sync_mmap), + JUMP_INIT(doallocate, _IO_file_doallocate), + JUMP_INIT(read, _IO_file_read), + JUMP_INIT(write, _IO_new_file_write), + JUMP_INIT(seek, _IO_file_seek), + JUMP_INIT(close, _IO_file_close_mmap), + JUMP_INIT(stat, _IO_file_stat), + JUMP_INIT(showmanyc, _IO_default_showmanyc), + JUMP_INIT(imbue, _IO_default_imbue) +}; + +const struct _IO_jump_t _IO_file_jumps_maybe_mmap libio_vtable = +{ + JUMP_INIT_DUMMY, + JUMP_INIT(finish, _IO_file_finish), + JUMP_INIT(overflow, _IO_file_overflow), + JUMP_INIT(underflow, _IO_file_underflow_maybe_mmap), + JUMP_INIT(uflow, _IO_default_uflow), + JUMP_INIT(pbackfail, _IO_default_pbackfail), + JUMP_INIT(xsputn, _IO_new_file_xsputn), + JUMP_INIT(xsgetn, _IO_file_xsgetn_maybe_mmap), + JUMP_INIT(seekoff, _IO_file_seekoff_maybe_mmap), + JUMP_INIT(seekpos, _IO_default_seekpos), + JUMP_INIT(setbuf, (_IO_setbuf_t) _IO_file_setbuf_mmap), + JUMP_INIT(sync, _IO_new_file_sync), + JUMP_INIT(doallocate, _IO_file_doallocate), + JUMP_INIT(read, _IO_file_read), + JUMP_INIT(write, _IO_new_file_write), + JUMP_INIT(seek, _IO_file_seek), + JUMP_INIT(close, _IO_file_close), + JUMP_INIT(stat, _IO_file_stat), + JUMP_INIT(showmanyc, _IO_default_showmanyc), + JUMP_INIT(imbue, _IO_default_imbue) +}; |