From patchwork Sat Jul 11 07:56:20 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 7644 Received: (qmail 124233 invoked by alias); 11 Jul 2015 07:56:34 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 124222 invoked by uid 89); 11 Jul 2015 07:56:34 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.3 required=5.0 tests=AWL, BAYES_00, KAM_LAZY_DOMAIN_SECURITY, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=no version=3.3.2 X-HELO: mx1.redhat.com From: "Siddhesh Poyarekar" To: libc-alpha@sourceware.org Cc: carlos@redhat.com Subject: [PATCH] Also flush write when reading large amount of data (BZ #18659) Date: Sat, 11 Jul 2015 13:26:20 +0530 Message-Id: <1436601380-12456-1-git-send-email-siddhesh@redhat.com> When a program calls fread on a file immediately after writing a small amount of data, it may fail to flush the written data if the read size is greater than or equal to the FILE buffer size. Trouble is that when we do fread directly after fwrite (where the former has not flushed), we don't bother to switch to get mode right away and we depend on __underflow to do that for us. Fix is to explicitly switch to get mode if we're currently putting. Verified on x86_64 that there are no regressions due to this fix. Siddhesh [BZ #18659] * libio/fileops.c (_IO_file_xsgetn): Switch to get mode if we're in put mode. * libio/tst-big-read-after-write.c: New test case. * libio/Makefile (tests): Use it. --- libio/Makefile | 2 +- libio/fileops.c | 7 ++++ libio/tst-big-read-after-write.c | 91 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 libio/tst-big-read-after-write.c diff --git a/libio/Makefile b/libio/Makefile index 7b3bcf9..60a2341 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -61,7 +61,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \ bug-memstream1 bug-wmemstream1 \ tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \ tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \ - tst-ftell-append tst-fputws + tst-ftell-append tst-fputws tst-big-read-after-write ifeq (yes,$(build-shared)) # Add test-fopenloc only if shared library is enabled since it depends on # shared localedata objects. diff --git a/libio/fileops.c b/libio/fileops.c index cbcd6f5..3064f44 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -1375,6 +1375,13 @@ _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n) _IO_doallocbuf (fp); } + /* If we were in put mode, switch to get mode. __underflow does that for us, + but only if the request sizes fit into the buffer. For larger requests, + unbuffered requests go unnoticed. */ + if (_IO_in_put_mode (fp)) + if (_IO_switch_to_get_mode (fp) == EOF) + return EOF; + while (want > 0) { have = fp->_IO_read_end - fp->_IO_read_ptr; diff --git a/libio/tst-big-read-after-write.c b/libio/tst-big-read-after-write.c new file mode 100644 index 0000000..506b955 --- /dev/null +++ b/libio/tst-big-read-after-write.c @@ -0,0 +1,91 @@ +/* Verify that reads on large block sizes immediately after writes don't fail + to flush buffer and switch correctly to read mode. + Copyright (C) 2015 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 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 + . */ + +#include +#include +#include +#include +#include +#include + +/* It is very unlikely that the block size (and hence buffer size) would be + equal to or greater than 64MB. */ +#define BUF_LEN (64 * 1024 * 1024) +char foo[BUF_LEN]; +static const char *data = "abcdefghijklmnopqrstuvwxyz"; + +static int do_test (void); + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" + +static int +do_one_test (const char *filename, const char *mode) +{ + FILE *f; + size_t n; + + f = fopen (filename, mode); + if (f == NULL) + { + printf ("Failed to open %s in %s mode\n", filename, mode); + return 1; + } + + fwrite(data, 1, strlen (data), f); + + n = fread(foo, 1, BUF_LEN, f); + if (n != 0) + { + printf ("fread before seek: expected %d but got %zu\n", 0, n); + return 1; + } + + fseek(f, 0, SEEK_SET); + n = fread(foo, 1, BUF_LEN, f); + if (n != strlen (data)) + { + printf ("fread after seek: expected %zu but got %zu\n", strlen (data), n); + return 1; + } + + return 0; +} + +static int +do_test (void) +{ + int ret = 0; + char *filename; + int fd = create_temp_file ("tst-big-read-after-write-tmp.", &filename); + + if (fd == -1) + { + printf ("create_temp_file: %m\n"); + return 1; + } + + close (fd); + + ret |= do_one_test (filename, "a+"); + ret |= do_one_test (filename, "r+"); + ret |= do_one_test (filename, "w+"); + + return ret; +}