diff options
Diffstat (limited to 'libio/fileops.c')
-rw-r--r-- | libio/fileops.c | 91 |
1 files changed, 76 insertions, 15 deletions
diff --git a/libio/fileops.c b/libio/fileops.c index 4db4a76..0cce828 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -1,4 +1,5 @@ -/* Copyright (C) 1993-2024 Free Software Foundation, Inc. +/* Copyright (C) 1993-2025 Free Software Foundation, Inc. + Copyright The GNU Toolchain Authors. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -126,15 +127,48 @@ _IO_new_file_init (struct _IO_FILE_plus *fp) int _IO_new_file_close_it (FILE *fp) { - int write_status; + int flush_status = 0; 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; + flush_status = _IO_do_flush (fp); + else if (fp->_fileno >= 0 + /* If this is the active handle, we must seek the + underlying open file description (possibly shared with + other file descriptors that remain open) to the correct + offset. But if this stream is in a state such that some + other handle might have become the active handle, then + (a) at the time it entered that state, the underlying + open file description had the correct offset, and (b) + seeking the underlying open file description, even to + its newly determined current offset, is not safe because + it can race with operations on a different active + handle. So check here for cases where it is necessary + to seek, while avoiding seeking in cases where it is + unsafe to do so. */ + && (_IO_in_backup (fp) + || (fp->_mode <= 0 && fp->_IO_read_ptr < fp->_IO_read_end) + || (_IO_vtable_offset (fp) == 0 + && fp->_mode > 0 && (fp->_wide_data->_IO_read_ptr + < fp->_wide_data->_IO_read_end)))) + { + off64_t o = _IO_SEEKOFF (fp, 0, _IO_seek_cur, 0); + if (o == EOF) + { + if (errno != ESPIPE) + flush_status = EOF; + } + else + { + if (_IO_in_backup (fp)) + o -= fp->_IO_save_end - fp->_IO_save_base; + flush_status = (_IO_SYSSEEK (fp, o, SEEK_SET) < 0 && errno != ESPIPE + ? EOF + : 0); + } + } _IO_unsave_markers (fp); @@ -159,7 +193,7 @@ _IO_new_file_close_it (FILE *fp) fp->_fileno = -1; fp->_offset = _IO_pos_BAD; - return close_status ? close_status : write_status; + return close_status ? close_status : flush_status; } libc_hidden_ver (_IO_new_file_close_it, _IO_file_close_it) @@ -220,7 +254,7 @@ _IO_new_file_fopen (FILE *fp, const char *filename, const char *mode, const char *last_recognized; if (_IO_file_is_open (fp)) - return 0; + return NULL; switch (*mode) { case 'r': @@ -480,7 +514,7 @@ _IO_new_file_underflow (FILE *fp) /* Maybe we already have a push back pointer. */ if (fp->_IO_save_base != NULL) { - free (fp->_IO_save_base); + _IO_free_backup_buf (fp, fp->_IO_save_base); fp->_flags &= ~_IO_IN_BACKUP; } _IO_doallocbuf (fp); @@ -798,6 +832,11 @@ _IO_new_file_sync (FILE *fp) 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 (_IO_in_backup (fp)) + { + _IO_switch_to_main_get_area (fp); + delta += fp->_IO_read_ptr - fp->_IO_read_end; + } if (delta != 0) { off64_t new_pos = _IO_SYSSEEK (fp, delta, 1); @@ -819,17 +858,21 @@ libc_hidden_ver (_IO_new_file_sync, _IO_file_sync) int _IO_file_sync_mmap (FILE *fp) { + off64_t o = fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr); if (fp->_IO_read_ptr != fp->_IO_read_end) { - if (__lseek64 (fp->_fileno, fp->_IO_read_ptr - fp->_IO_buf_base, - SEEK_SET) - != fp->_IO_read_ptr - fp->_IO_buf_base) + if (_IO_in_backup (fp)) + { + _IO_switch_to_main_get_area (fp); + o -= fp->_IO_read_end - fp->_IO_read_base; + } + if (__lseek64 (fp->_fileno, o, SEEK_SET) != o) { fp->_flags |= _IO_ERR_SEEN; return EOF; } } - fp->_offset = fp->_IO_read_ptr - fp->_IO_buf_base; + fp->_offset = o; fp->_IO_read_end = fp->_IO_read_ptr = fp->_IO_read_base; return 0; } @@ -885,6 +928,16 @@ do_ftell (FILE *fp) if (result == EOF) return result; + if (result == 0 && offset < 0) + { + /* This happens for some character devices that always report + file offset 0 even after some data has been read (instead of + failing with ESPIPE). The fclose path ignores this + error. */ + __set_errno (ESPIPE); + return EOF; + } + result += offset; if (result < 0) @@ -932,7 +985,7 @@ _IO_new_file_seekoff (FILE *fp, off64_t offset, int dir, int mode) /* It could be that we already have a pushback buffer. */ if (fp->_IO_read_base != NULL) { - free (fp->_IO_read_base); + _IO_free_backup_buf (fp, fp->_IO_read_base); fp->_flags &= ~_IO_IN_BACKUP; } _IO_doallocbuf (fp); @@ -1067,11 +1120,18 @@ _IO_file_seekoff_mmap (FILE *fp, off64_t offset, int dir, int mode) if (mode == 0) return fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr); + if (_IO_in_backup (fp)) + { + if (dir == _IO_seek_cur) + offset += fp->_IO_read_ptr - fp->_IO_read_end; + _IO_switch_to_main_get_area (fp); + } + switch (dir) { case _IO_seek_cur: /* Adjust for read-ahead (bytes is buffer). */ - offset += fp->_IO_read_ptr - fp->_IO_read_base; + offset += fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr); break; case _IO_seek_set: break; @@ -1184,6 +1244,7 @@ _IO_new_file_write (FILE *f, const void *data, ssize_t n) f->_flags |= _IO_ERR_SEEN; break; } + f->_total_written += count; to_do -= count; data = (void *) ((char *) data + count); } @@ -1282,7 +1343,7 @@ _IO_file_xsgetn (FILE *fp, void *data, size_t n) /* Maybe we already have a push back pointer. */ if (fp->_IO_save_base != NULL) { - free (fp->_IO_save_base); + _IO_free_backup_buf (fp, fp->_IO_save_base); fp->_flags &= ~_IO_IN_BACKUP; } _IO_doallocbuf (fp); |