From patchwork Mon Sep 2 22:03:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joseph Myers X-Patchwork-Id: 96938 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4D8E7385E83F for ; Mon, 2 Sep 2024 22:03:37 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 054EA3858C66 for ; Mon, 2 Sep 2024 22:03:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 054EA3858C66 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 054EA3858C66 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1725314594; cv=none; b=Cq+0+uHGuSC5zC2ROYPkgMPLGfG6VI1I9c4NK45u8nYTnExuNgbznLYlg7vL/+glifEz2zxouYw8MNYKAoRRwCw25w2rH+a3YKxqtbnHIhqXXbmWjP0avJN8EtUzEmx77a74PoeG4rsQVv6yBq92hLjso7j8U5PrISLfiaYC78Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1725314594; c=relaxed/simple; bh=vHEbHsq0YdABIwnbI9R061JaJoLEts1/27GRnmkihK4=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=m52bPq5F7UPGTNZ9EC9bVRJo9ex+CowP6aewqvnO4tWSRBAlUHMg1BfYaItRXgTaOuBouRxWRo1363iKAGNSUnjYkj8AA5LIVJ77wQFJAJbi9kbIj137vD0hl1rW836zoxvLNWaNkBGX8E4/Nm/KAB8F2wFyx0i5oy5BMTupvQk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1725314590; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=Msa16W3lcY0S7VHLUoheVdD3JeJ8XwJO022C04gCv/8=; b=YvypGyp0jFsMRmvvVHf5vPnxkGKrCHaXvP4UkjFgEsK2DOGVT0OsFvLlI/SwpSwO0Bwk5c 49DXi2nHbLxw861JoMwT6G5REkfqFlFnazM2RWV6rLIqYlX/oWfrMN0mD1aMj6UgT8ZMIj 7bfRORsgd+TRakH3Pm4y08IgfYQgHk4= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-150-Q4YZfxguNieO7p6xQdiXqg-1; Mon, 02 Sep 2024 18:03:09 -0400 X-MC-Unique: Q4YZfxguNieO7p6xQdiXqg-1 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-42bb6d3cd05so42810105e9.0 for ; Mon, 02 Sep 2024 15:03:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1725314585; x=1725919385; h=mime-version:references:message-id:in-reply-to:subject:cc:to:from :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Msa16W3lcY0S7VHLUoheVdD3JeJ8XwJO022C04gCv/8=; b=mJ3EFXok9i0h0QXMcyxidUnLiwKMIBANfKKFqvSTEGBMT22HX85lisqBNXD9+HuaCp 0VMnl1s68tr7YX6mz3d6uM87m2BJTsfAXMfElQRN+DhEmL89Icn/TqK48l3eDM6oilyC KYRQTbP62SLyZGzWnem2N5pzSmNZa2Z5sLycP1Nbus7rN1YWpRqaTJvgsLh7hkWqIk9/ pzifeKYwTLhCuYaIEA1VT0bUeOLC/Noi2MC8i5VuxJyW/jIeOKVcpuOB/mynRrwdTweD 970c04IBrmU6BJrM9bGITGLUrBFaISUh5pcyKhxS24Umb5pWwaU/yaCiUp2NzMU9leL8 +aDA== X-Gm-Message-State: AOJu0YyaixJo4XP2lLitVfp2kyXXx2CECM4tcMdQpEaTmj6rO3mJK7Yp GhFt8Mdnc38vf6SGDmKZFwpkwPs9ip99kouQ82n60cZXcfweVFzi0x5b4Ttobty1m7Zq2oysAn6 BTBTgLCt0aezv2AVPXyKPN43TJnmtx5NQsV/Tn0QEX/SZTt3QzbmNL4/K+y8p0pwr7Q== X-Received: by 2002:adf:a38a:0:b0:374:c7f8:3d52 with SMTP id ffacd0b85a97d-374fa04b2f6mr842656f8f.54.1725314585002; Mon, 02 Sep 2024 15:03:05 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHxrWUgrkL7g1CHMxHwNJMOLudiMDSMzu2HDRFxaQsp68zc4iLT3kemXiukbA+XteDGYayw3w== X-Received: by 2002:adf:a38a:0:b0:374:c7f8:3d52 with SMTP id ffacd0b85a97d-374fa04b2f6mr842630f8f.54.1725314583950; Mon, 02 Sep 2024 15:03:03 -0700 (PDT) Received: from digraph.polyomino.org.uk (digraph.polyomino.org.uk. [2001:8b0:bf73:93f7::51bb:e332]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-374c44aabd8sm6190950f8f.77.2024.09.02.15.03.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Sep 2024 15:03:03 -0700 (PDT) Received: from jsm28 (helo=localhost) by digraph.polyomino.org.uk with local-esmtp (Exim 4.95) (envelope-from ) id 1slF8Y-007M7w-2E; Mon, 02 Sep 2024 22:03:02 +0000 Date: Mon, 2 Sep 2024 22:03:02 +0000 (UTC) From: Joseph Myers To: Florian Weimer cc: libc-alpha@sourceware.org Subject: [PATCH v2] Add more thorough tests of freopen In-Reply-To: <87h6ayt92d.fsf@oldenburg3.str.redhat.com> Message-ID: References: <92e8ff20-fb9a-a289-2e58-faaa8bb71be2@redhat.com> <87h6ayt92d.fsf@oldenburg3.str.redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-9.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org On Mon, 2 Sep 2024, Florian Weimer wrote: > I think it's clear that it's possible to use freopen on streams created > by fopen (where it is somewhat redundant for us) and on the (in glibc's > case initial) values of stdin, stdout, stderr, where it is essential for > non-POSIX platforms. But there are other ways to create streams. It > seems to be the interaction with popen, fmemopen, open_memstream, and > glibc's fopencookie has been ignored so far. > > Some of these combinations do not work at all in the glibc > implementation, something that cannot be fixed easily, so I think we > should document these limitations in the glibc manual. This seems to be > unaddressed in the POSIX text. I propose to add documentation, and tests for stdin / stdout / stderr, separately. This revised patch does test the case of a stream opened with fopen64, and that of reopening a stream more than once (including mixing freopen and freopen64). > You could add mtrace and checks around all the > tests to verify that there are now leaks, as in this test: tests added. mtrace tests run into existing leaks, I think bug 24583 - should I split out the ,ccs= tests so the rest can have mtrace tests? > A separate test (using support_become_root, support_can_chroot) could > check what happens if /proc is not mounted and the /proc/self/fd code > path is used. I propose to add that separately as well. > > + verbose_printf ("Testing r -> r\n"); > > + fp = xfopen (file1, "r"); > > + fp = freopen (file2, "r", fp); > > + TEST_VERIFY_EXIT (fp != NULL); > > + TEST_COMPARE_FILE_STRING (fp, "file2"); > > + xfclose (fp); > > As a general comment, I would have expected a check that the > non-modified file is in fact not modified. Seems very likely given our > implementation, but still. (I wonder if it would be conforming to > create a hard link to the new name on freopen.) Added such a check (not for this specific r -> r test). > > + /* Test freopen with NULL, renamed file. */ > > This is supposed to exercise the /proc/self/fd path and show that no new > pathname lookup is peformed, right? (Along with the following test.) A > comment could mention that. I've expanded the comment (not specifically referencing /proc/self/fd since I'm thinking of this as testing the feature rather than the implementation approach). Add more thorough tests of freopen freopen is rather minimally tested in libio/tst-freopen and libio/test-freopen. Add some more thorough tests, covering different cases for change of mode in particular. The tests are run for both freopen and freopen64 (given that those functions have two separate copies of much of the code, so any bug fix directly in the freopen code would probably need applying in both places). Note that there are two parts of the test disabled because of bugs discovered through running the test, with bug numbers given in comments. I expect to address those separately. The test also doesn't cover changes to cancellation ("c" in mode); I think that will better be handled through a separate test. Also to handle separately: testing on stdin / stdout / stderr; documenting lack of support for streams opened with popen / fmemopen / open_memstream / fopencookie; maybe also a chroot test without /proc; maybe also more thorough tests for large file handling on 32-bit systems (freopen64). Tested for x86_64. --- Changed in v2: the support/ changes (on which this depends) have been sent as a separate patch; test both freopen and freopen64; use support/descriptors.h to check for descriptor leaks; make a -> a test really test a -> a not w -> a; also test files opened with fopen64; also test calling freopen more than once on the same FILE * (or mixing freopen and freopen64); check unmodified file contents in some cases; expand comments on tests with renamed or deleted files. diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 948d960ccc..49258a26f3 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -216,6 +216,8 @@ tests := \ tst-fmemopen4 \ tst-fphex \ tst-fphex-wide \ + tst-freopen2 \ + tst-freopen64-2 \ tst-fseek \ tst-fwrite \ tst-getline \ diff --git a/stdio-common/tst-freopen2-main.c b/stdio-common/tst-freopen2-main.c new file mode 100644 index 0000000000..76a56e344d --- /dev/null +++ b/stdio-common/tst-freopen2-main.c @@ -0,0 +1,547 @@ +/* Test freopen. + Copyright (C) 2024 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 + +#include +#include +#include +#include +#include +#include +#include + +#define START_TEST(DESC) \ + do \ + { \ + fds = support_descriptors_list (); \ + verbose_printf (DESC); \ + } \ + while (0) + +#define END_TEST \ + do \ + { \ + support_descriptors_check (fds); \ + support_descriptors_free (fds); \ + } \ + while (0) + +int +do_test (void) +{ + struct support_descriptors *fds; + char *temp_dir = support_create_temp_directory ("tst-freopen2"); + char *file1 = xasprintf ("%s/file1", temp_dir); + support_write_file_string (file1, "file1"); + add_temp_file (file1); + char *file2 = xasprintf ("%s/file2", temp_dir); + support_write_file_string (file2, "file2"); + add_temp_file (file2); + char *file3 = xasprintf ("%s/file3", temp_dir); + char *file4 = xasprintf ("%s/file4", temp_dir); + char *file_nodir = xasprintf ("%s/nodir/file", temp_dir); + char *file1a = xasprintf ("%s/file1a", temp_dir); + FILE *fp; + int ret; + wint_t wc; + int fd; + + /* Test each pair of old and new modes from r w a. */ + + START_TEST ("Testing r -> r\n"); + fp = xfopen (file1, "r"); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "file2"); + xfclose (fp); + END_TEST; + + START_TEST ("Testing r -> w\n"); + fp = xfopen (file1, "r"); + fp = FREOPEN (file2, "w", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("File2new", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file1, "file1"); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "File2new"); + END_TEST; + + START_TEST ("Testing r -> a\n"); + fp = xfopen (file1, "r"); + fp = FREOPEN (file2, "a", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("3", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "File2new3"); + END_TEST; + + START_TEST ("Testing w -> r\n"); + fp = xfopen (file1, "w"); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "File2new3"); + xfclose (fp); + END_TEST; + + START_TEST ("Testing w -> w\n"); + fp = xfopen (file1, "w"); + fp = FREOPEN (file2, "w", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("next", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file1, ""); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "next"); + END_TEST; + + START_TEST ("Testing w -> a\n"); + fp = xfopen (file1, "w"); + fp = FREOPEN (file2, "a", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("4", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "next4"); + END_TEST; + + START_TEST ("Testing a -> r\n"); + fp = xfopen (file1, "a"); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "next4"); + xfclose (fp); + END_TEST; + + START_TEST ("Testing a -> w\n"); + fp = xfopen (file1, "a"); + fp = FREOPEN (file2, "w", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("another", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another"); + END_TEST; + + START_TEST ("Testing a -> a\n"); + fp = xfopen (file1, "a"); + fp = FREOPEN (file2, "a", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("5", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another5"); + END_TEST; + + /* Test for file originally opened with fopen64. */ + START_TEST ("Testing fopen64 a -> a\n"); + fp = fopen64 (file1, "a"); + TEST_VERIFY_EXIT (fp != NULL); + fp = FREOPEN (file2, "a", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("64", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another564"); + END_TEST; + + /* Test calling freopen more than once on the same FILE *. */ + + START_TEST ("Testing r -> w -> r\n"); + fp = xfopen (file1, "r"); + fp = FREOPEN (file2, "w", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("freopen-twice", fp); + TEST_VERIFY (ret >= 0); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "freopen-twice"); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "freopen-twice"); + END_TEST; + + START_TEST ("Testing r -> w -> r (exactly one freopen64)\n"); + fp = xfopen (file1, "r"); + fp = OTHER_FREOPEN (file2, "w", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("freopen-twice64", fp); + TEST_VERIFY (ret >= 0); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "freopen-twice64"); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "freopen-twice64"); + END_TEST; + + /* Test changing to/from b (binary, no-op). */ + + START_TEST ("Testing rb -> r\n"); + fp = xfopen (file1, "rb"); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "freopen-twice64"); + xfclose (fp); + END_TEST; + + START_TEST ("Testing r -> rb\n"); + fp = xfopen (file1, "r"); + fp = FREOPEN (file2, "rb", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "freopen-twice64"); + xfclose (fp); + END_TEST; + + /* Test changing to/from + (read-and-write). */ + + START_TEST ("Testing r -> w+\n"); + fp = xfopen (file1, "r"); + fp = FREOPEN (file2, "w+", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("latest", fp); + TEST_VERIFY (ret >= 0); + ret = fseek (fp, 0, SEEK_SET); + TEST_COMPARE (ret, 0); + TEST_COMPARE_FILE_STRING (fp, "latest"); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "latest"); + END_TEST; + + START_TEST ("Testing w -> a+\n"); + fp = xfopen (file1, "w"); + fp = FREOPEN (file2, "a+", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("suffix", fp); + TEST_VERIFY (ret >= 0); + ret = fseek (fp, 0, SEEK_SET); + TEST_COMPARE (ret, 0); + TEST_COMPARE_FILE_STRING (fp, "latestsuffix"); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "latestsuffix"); + END_TEST; + + START_TEST ("Testing a -> r+\n"); + fp = xfopen (file1, "a"); + fp = FREOPEN (file2, "r+", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "latestsuffix"); + ret = fseek (fp, 0, SEEK_SET); + TEST_COMPARE (ret, 0); + ret = fputs ("new", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "newestsuffix"); + END_TEST; + + START_TEST ("Testing r+ -> w\n"); + fp = xfopen (file1, "r+"); + fp = FREOPEN (file2, "w", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("plusto", fp); + TEST_VERIFY (ret >= 0); + ret = fseek (fp, 0, SEEK_SET); + TEST_COMPARE (ret, 0); + errno = 0; + TEST_COMPARE (fgetc (fp), EOF); + TEST_COMPARE (errno, EBADF); + clearerr (fp); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plusto"); + END_TEST; + + START_TEST ("Testing w+ -> a\n"); + fp = xfopen (file1, "w+"); + fp = FREOPEN (file2, "a", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("more", fp); + TEST_VERIFY (ret >= 0); + ret = fseek (fp, 0, SEEK_SET); + TEST_COMPARE (ret, 0); + errno = 0; + TEST_COMPARE (fgetc (fp), EOF); + TEST_COMPARE (errno, EBADF); + clearerr (fp); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plustomore"); + END_TEST; + + START_TEST ("Testing a+ -> r\n"); + fp = xfopen (file1, "a+"); + fp = FREOPEN (file2, "rr", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "plustomore"); + ret = fputs ("2", fp); + TEST_COMPARE (ret, EOF); + clearerr (fp); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plustomore"); + END_TEST; + + /* Test changing to/from e (FD_CLOEXEC). */ + + START_TEST ("Testing re -> r\n"); + fp = xfopen (file1, "re"); + ret = fcntl (fileno (fp), F_GETFD); + TEST_VERIFY (ret != -1); + TEST_COMPARE (ret & FD_CLOEXEC, FD_CLOEXEC); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fcntl (fileno (fp), F_GETFD); + TEST_VERIFY (ret != -1); +#if 0 /* Fails to clear FD_CLOEXEC (bug 32134). */ + TEST_COMPARE (ret & FD_CLOEXEC, 0); +#endif + TEST_COMPARE_FILE_STRING (fp, "plustomore"); + xfclose (fp); + END_TEST; + + START_TEST ("Testing r -> re\n"); + fp = xfopen (file1, "r"); + ret = fcntl (fileno (fp), F_GETFD); + TEST_VERIFY (ret != -1); + TEST_COMPARE (ret & FD_CLOEXEC, 0); + fp = FREOPEN (file2, "re", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fcntl (fileno (fp), F_GETFD); + TEST_VERIFY (ret != -1); + TEST_COMPARE (ret & FD_CLOEXEC, FD_CLOEXEC); + TEST_COMPARE_FILE_STRING (fp, "plustomore"); + xfclose (fp); + END_TEST; + + /* Test changing to/from m (mmap) (a no-op as far as testing + semantics is concerned). */ + + START_TEST ("Testing rm -> r\n"); + fp = xfopen (file1, "rm"); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "plustomore"); + xfclose (fp); + END_TEST; + + START_TEST ("Testing r -> rm\n"); + fp = xfopen (file1, "r"); + fp = FREOPEN (file2, "rm", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "plustomore"); + xfclose (fp); + END_TEST; + + /* Test changing to/from x (O_EXCL). */ + + START_TEST ("Testing wx -> w\n"); + fp = xfopen (file3, "wx"); + add_temp_file (file3); + fp = FREOPEN (file2, "w", fp); + TEST_VERIFY_EXIT (fp != NULL); + ret = fputs ("wxtow", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "wxtow"); + END_TEST; + + START_TEST ("Testing w -> wx (file does not exist)\n"); + fp = xfopen (file1, "w"); + fp = FREOPEN (file4, "wx", fp); + TEST_VERIFY_EXIT (fp != NULL); + add_temp_file (file4); + ret = fputs ("wtowx", fp); + TEST_VERIFY (ret >= 0); + xfclose (fp); + TEST_OPEN_AND_COMPARE_FILE_STRING (file4, "wtowx"); + END_TEST; + + START_TEST ("Testing w -> wx (file exists)\n"); + fp = xfopen (file1, "w"); + fp = FREOPEN (file4, "wx", fp); + TEST_VERIFY (fp == NULL); + END_TEST; + + /* Test with ,ccs=CHARSET. */ + + START_TEST ("testing w,ccs=utf-8 -> r\n"); + fp = xfopen (file1, "w,ccs=utf-8"); + ret = fputws (L"\xc0\xc1", fp); + TEST_VERIFY (ret >= 0); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "wxtow"); + xfclose (fp); + END_TEST; + + START_TEST ("testing w,ccs=iso-8859-1 -> r,ccs=utf-8\n"); + fp = xfopen (file2, "w,ccs=iso-8859-1"); + ret = fputws (L"\xc0\xc1", fp); + TEST_VERIFY (ret >= 0); +#if 0 /* Doesn't work (bug 23675). */ + fp = FREOPEN (file1, "r,ccs=utf-8", fp); + TEST_VERIFY_EXIT (fp != NULL); +#else /* Works instead. */ + xfclose (fp); + fp = xfopen (file1, "r,ccs=utf-8"); +#endif + wc = fgetwc (fp); + TEST_COMPARE (wc, (wint_t) 0xc0); + wc = fgetwc (fp); + TEST_COMPARE (wc, (wint_t) 0xc1); + wc = fgetwc (fp); + TEST_COMPARE (wc, WEOF); + xfclose (fp); + END_TEST; + + START_TEST ("testing r,ccs=utf-8 -> r\n"); + fp = xfopen (file1, "r,ccs=utf-8"); + fp = FREOPEN (file1, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "\u00c0\u00c1"); + xfclose (fp); + END_TEST; + + /* Test old file is closed even when opening the new file fails. */ + + START_TEST ("testing r -> r (opening new file fails)\n"); + fp = xfopen (file1, "r"); + fd = fileno (fp); + fp = FREOPEN (file_nodir, "r", fp); + TEST_VERIFY (fp == NULL); + errno = 0; + ret = fcntl (fd, F_GETFL); + TEST_COMPARE (ret, -1); + TEST_COMPARE (errno, EBADF); + END_TEST; + + /* Test that errors closing the old file are ignored. */ + + START_TEST ("testing errors closing old file ignored\n"); + fp = xfopen ("/dev/full", "w"); + fputc ('x', fp); + fp = FREOPEN (file1, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "\u00c0\u00c1"); + xfclose (fp); + END_TEST; + + /* Test that error / EOF state from the old file are cleared. */ + + START_TEST ("testing error state from old file cleared\n"); + fp = xfopen ("/dev/full", "w"); + fputc ('x', fp); + fflush (fp); + TEST_VERIFY (ferror (fp)); + TEST_VERIFY (!feof (fp)); + fp = FREOPEN (file2, "w", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_VERIFY (!ferror (fp)); + TEST_VERIFY (!feof (fp)); + xfclose (fp); + END_TEST; + + START_TEST ("testing EOF state from old file cleared\n"); + fp = xfopen ("/dev/null", "r"); + fgetc (fp); + TEST_VERIFY (!ferror (fp)); + TEST_VERIFY (feof (fp)); + fp = FREOPEN (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_VERIFY (!ferror (fp)); + TEST_VERIFY (!feof (fp)); + xfclose (fp); + END_TEST; + + /* Test freopen with NULL, same mode (should flush content and reset + file offset). */ + + START_TEST ("testing freopen with NULL, same mode\n"); + fp = xfopen (file1, "r+"); + ret = fputs ("same mode", fp); + TEST_VERIFY (ret >= 0); + fp = FREOPEN (NULL, "r+", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "same mode"); + xfclose (fp); + END_TEST; + + /* Test freopen with NULL, different mode. */ + + START_TEST ("testing freopen with NULL, different mode\n"); + fp = xfopen (file1, "w"); + ret = fputs ("different mode", fp); + TEST_VERIFY (ret >= 0); + fp = FREOPEN (NULL, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "different mode"); + xfclose (fp); + END_TEST; + + /* Test freopen with NULL, renamed file. This verifies that + reopening succeeds (and resets the file position indicator to + start of file) even when the original path could no longer be + opened. */ + + START_TEST ("testing freopen with NULL, renamed file\n"); + fp = xfopen (file1, "r+"); + ret = fputs ("file has been renamed", fp); + TEST_VERIFY (ret >= 0); + ret = rename (file1, file1a); + TEST_COMPARE (ret, 0); + fp = FREOPEN (NULL, "r+", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "file has been renamed"); + xfclose (fp); + ret = rename (file1a, file1); + TEST_COMPARE (ret, 0); + END_TEST; + + /* Test freopen with NULL, deleted file. This verifies that + reopening succeeds (and resets the file position indicator to + start of file) even when the original path could no longer be + opened. */ + + START_TEST ("testing freopen with NULL, deleted file\n"); + fp = xfopen (file1, "r+"); + ret = fputs ("file has now been deleted", fp); + TEST_VERIFY (ret >= 0); + ret = remove (file1); + TEST_COMPARE (ret, 0); + fp = FREOPEN (NULL, "r+", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "file has now been deleted"); + xfclose (fp); + /* Recreate the file so it is present when expected for temporary + file deletion. */ + support_write_file_string (file1, "file1"); + END_TEST; + + free (temp_dir); + free (file1); + free (file2); + free (file3); + free (file4); + free (file_nodir); + free (file1a); + return 0; +} + +#include diff --git a/stdio-common/tst-freopen2.c b/stdio-common/tst-freopen2.c new file mode 100644 index 0000000000..11ec7a9783 --- /dev/null +++ b/stdio-common/tst-freopen2.c @@ -0,0 +1,3 @@ +#define FREOPEN freopen +#define OTHER_FREOPEN freopen64 +#include diff --git a/stdio-common/tst-freopen64-2.c b/stdio-common/tst-freopen64-2.c new file mode 100644 index 0000000000..88fdc64d8c --- /dev/null +++ b/stdio-common/tst-freopen64-2.c @@ -0,0 +1,3 @@ +#define FREOPEN freopen64 +#define OTHER_FREOPEN freopen +#include