From patchwork Thu Nov 27 12:28:39 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 415478 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id CE90C14019D for ; Thu, 27 Nov 2014 23:28:53 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:date:from:to:cc:subject:message-id :mime-version:content-type; q=dns; s=default; b=n/v56YbS4wWrAQbg TIRbTw96rHImk0cQ11zwlS9ooXi5nEX9YBbW7DTi0PQns8jjP3RSJZokhMXWXUAh rYF3ytigo5g6n9OaeglkVzTslh4e2VVWvJbwSN4MZhdRy77Ct/xs8tEQYzEvG+tx koHVV0KBOBee75FFOuxvZiJLwTw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:date:from:to:cc:subject:message-id :mime-version:content-type; s=default; bh=H9MCykcYt5TloFbVnStHnj d/g5s=; b=Rw9qc3fPw/M4IbFVnZeuIiy3ebP1gjkGpby689VdJ4sUGVTPdBti5s uXz7yC8uw39sHXsqbbshJIt/Ma/gk+85T+v48+lTslzJMOALtD3vq/amuctsVSsw mBG5EdKFw3c+18xGVY69LfNSuhVUWE77AAnYpNAHem8BP2HbzAASc= Received: (qmail 14242 invoked by alias); 27 Nov 2014 12:28:48 -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 14232 invoked by uid 89); 27 Nov 2014 12:28:47 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.3 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Date: Thu, 27 Nov 2014 17:58:39 +0530 From: Siddhesh Poyarekar To: libc-alpha@sourceware.org Cc: carlos@redhat.com Subject: [PATCH] Reset cached offset when reading to end of stream (BZ #17653) Message-ID: <20141127122839.GA31809@spoyarek.pnq.redhat.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.22.1-rc1 (2013-10-16) POSIX allows applications to switch file handles when a read results in an end of file. Unset the cached offset at this point so that it is queried again. Tested on x86_64 to verify that there are no regressions. The patch also has a test case addition that fails without the patch and passes with it. Siddhesh [BZ #17653] * libio/fileops.c (_IO_new_file_underflow): Unset cached offset on EOF. * libio/wfileops.c (_IO_wfile_underflow): Likewise. * libio/tst-ftell-active-handler.c (fgets_func_t): New type. (fgets_func): Function pointer to fgets and fgetws. (do_ftell_test): Add test to verify ftell value after read EOF. (do_test): Set fgets_func. --- libio/fileops.c | 8 +++++- libio/tst-ftell-active-handler.c | 54 ++++++++++++++++++++++++++++++++++------ libio/wfileops.c | 1 + 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/libio/fileops.c b/libio/fileops.c index 1fc5719..3ae9682 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -615,7 +615,13 @@ _IO_new_file_underflow (fp) } fp->_IO_read_end += count; if (count == 0) - return EOF; + { + /* 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; diff --git a/libio/tst-ftell-active-handler.c b/libio/tst-ftell-active-handler.c index 72066b4..d7edbdf 100644 --- a/libio/tst-ftell-active-handler.c +++ b/libio/tst-ftell-active-handler.c @@ -86,7 +86,9 @@ static size_t data_len; static size_t file_len; typedef int (*fputs_func_t) (const void *data, FILE *fp); +typedef void *(*fgets_func_t) (void *ws, int n, FILE *fp); fputs_func_t fputs_func; +fgets_func_t fgets_func; /* This test verifies that the offset reported by ftell is correct after the file is truncated using ftruncate. ftruncate does not change the file @@ -290,20 +292,22 @@ do_ftell_test (const char *filename) int fd_mode; size_t old_off; size_t new_off; + size_t eof_off; } test_modes[] = { /* In w, w+ and r+ modes, the file position should be at the beginning of the file. After the write, the offset should be - updated to data_len. */ - {"w", O_WRONLY | O_TRUNC, 0, data_len}, - {"w+", O_RDWR | O_TRUNC, 0, data_len}, - {"r+", O_RDWR, 0, data_len}, + updated to data_len. We don't use eof_off in w and a modes since + they don't allow reading. */ + {"w", O_WRONLY | O_TRUNC, 0, data_len, 0}, + {"w+", O_RDWR | O_TRUNC, 0, data_len, 2 * data_len}, + {"r+", O_RDWR, 0, data_len, 3 * data_len}, /* For the 'a' mode, the initial file position should be the current end of file. After the write, the offset has data_len added to the old value. For a+ mode however, the initial file position is the file position of the underlying file descriptor, since it is initially assumed to be in read mode. */ - {"a", O_WRONLY, data_len, 2 * data_len}, - {"a+", O_RDWR, 0, 3 * data_len}, + {"a", O_WRONLY, 3 * data_len, 4 * data_len, 5 * data_len}, + {"a+", O_RDWR, 0, 5 * data_len, 6 * data_len}, }; for (int j = 0; j < 2; j++) { @@ -348,12 +352,44 @@ do_ftell_test (const char *filename) if (off != test_modes[i].new_off) { - printf ("Incorrect new offset. Expected %zu but got %ld\n", + printf ("Incorrect new offset. Expected %zu but got %ld, ", test_modes[i].new_off, off); ret |= 1; } else - printf ("new offset = %ld\n", off); + printf ("new offset = %ld, ", off); + + /* Read to the end, write some data to the fd and check if ftell can + see the new ofset. Do this test only for files that allow + reading. */ + if (test_modes[i].fd_mode != O_WRONLY) + { + char tmpbuf[data_len]; + + rewind (fp); + + while (fgets_func (tmpbuf, sizeof (tmpbuf), fp) && !feof (fp)); + + write_ret = write (fd, data, data_len); + if (write_ret != data_len) + { + printf ("write failed (%m)\n"); + ret |= 1; + } + off = ftell (fp); + + if (off != test_modes[i].eof_off) + { + printf ("Incorrect offset after read EOF. " + "Expected %zu but got %ld\n", + test_modes[i].eof_off, off); + ret |= 1; + } + else + printf ("offset after EOF = %ld\n", off); + } + else + putc ('\n', stdout); fclose (fp); } @@ -617,6 +653,7 @@ do_test (void) /* Tests for regular files. */ puts ("Regular mode:"); fputs_func = (fputs_func_t) fputs; + fgets_func = (fgets_func_t) fgets; data = char_data; data_len = strlen (char_data); ret |= do_one_test (filename); @@ -638,6 +675,7 @@ do_test (void) return 1; } fputs_func = (fputs_func_t) fputws; + fgets_func = (fgets_func_t) fgetws; data = wide_data; data_len = wcslen (wide_data); ret |= do_one_test (filename); diff --git a/libio/wfileops.c b/libio/wfileops.c index 71281c1..267b093 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -268,6 +268,7 @@ _IO_wfile_underflow (fp) /* There are some bytes in the external buffer but they don't convert to anything. */ __set_errno (EILSEQ); + fp->_offset = _IO_pos_BAD; return WEOF; } if (fp->_offset != _IO_pos_BAD)