From patchwork Fri Aug 30 21:59:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joseph Myers X-Patchwork-Id: 1979230 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=M67Gh/rQ; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WwXBy5pLGz1yZs for ; Sat, 31 Aug 2024 08:00:26 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 94F84386484A for ; Fri, 30 Aug 2024 22:00:24 +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.129.124]) by sourceware.org (Postfix) with ESMTP id D769A385E011 for ; Fri, 30 Aug 2024 22:00:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D769A385E011 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 D769A385E011 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1725055207; cv=none; b=v5IxQeqws3orwPj7aNKMlX5itt0sox9VVqVJ1XP37ZT0PhbC7bVGFvtxMhg8yNp4hC3YZnTCGFWYUxGWtuSplLEvfJrdritQYa0KX86rK8pAOcHbB7xDMW8ctfemIl8cbVMtAQzbdYkrAOW4gDVz3yk6dNCUwbv19EHJcJaRAAg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1725055207; c=relaxed/simple; bh=D4yw+SoEdxiKrDlxXoBykcbM6j928PKxzLuCOzXexOc=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=M0aENGSHXhkszZHFDNUS8HVFH0GkIXiCbx8MbLom3CVgcBDpHKcxbcofW5KvPLO6lp2ofg8hf77wPWBOk8m2ruqgI3/6tTLFX470Oy6fJm0Am3SurPgzV8aeNIGze5npv/uGd77CPsG9Qyhj/zQDGiBofwAQbR4A0oGW/WznH4g= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1725055203; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type; bh=Y6XQQiNyPOpzax6gK0Iqhzc7iY8rEy8IjO+82OVlfnA=; b=M67Gh/rQv1wcq/bsLOeW1LqNqLtbucmw+zxWGkv0PcimfOpHtsc0n5pdqfATKcymNNJqYQ q4Z4aVdvhqejJfz9xMIRwyRLMsoo7JGX0daoexFEI8Zga5dhziF4JH79xi06GMIEGaZD9P XqXtlTP0frMJGiDJNsVJXw/qb3FXmfU= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-41-z-uxIVqTPCiiip0XvkXC5A-1; Fri, 30 Aug 2024 18:00:01 -0400 X-MC-Unique: z-uxIVqTPCiiip0XvkXC5A-1 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-42807a05413so20247365e9.2 for ; Fri, 30 Aug 2024 15:00:01 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1725055200; x=1725660000; h=mime-version:message-id:subject:to:from:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=Y6XQQiNyPOpzax6gK0Iqhzc7iY8rEy8IjO+82OVlfnA=; b=MoAh+dGi+cox9gaM1cyavPc46vLi1+XeQ6KysKsypYrU5i8vfOvVYEIJKNQfGovbyw boPq3pRmXeKTeclYJHIu/mNxwSewI/O9aYEF4JkfYxcqJV9QWaRwGvGtH767gNBUaTqO jm1UkFxHdGWcVQK70heveAd6R+7wTSC6KWVITi0KX8VcOSR8md98+Dx74CTY9D7j6M35 p5+9Oo6fY41uuFaXL0+o64vtez16x6EWMHh0T9Sep/Pm4GHcBNqVHzdgo21a9oBg6/i1 gSyHMfAL/cWGZDv9esmasmzax6/feti+Ihb+239esC5MLb7OqbehYvUC8eNDigszWQy2 RXXg== X-Gm-Message-State: AOJu0YzknF6GkPjZjql4oHncAhMeCbTM1Mu+OE0dUSRzIg49l+PeoxdI MNJRi5o63tCm3CAvDvUgSnvp2X5HxtEg+oT0t8nqKDMaG9mdsPmfSu430wjq5r5J7Vc6Yp4hOtF vkdqLg/h28VmSVVzfaIZwKuFx/hfrGThERhiCk0OGOha5W7qbIZS/W6tCkhXY/Iz8+4wGTsDe8d /OBnXZuUvpc7qWigCRWT/lBtj0Q6u1MzmH/rgeFxkacQ== X-Received: by 2002:a05:600c:4f42:b0:426:6220:cb57 with SMTP id 5b1f17b1804b1-42bb02d8cfemr56477255e9.25.1725055200192; Fri, 30 Aug 2024 15:00:00 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFxmpgpNMu/6Snd5RkAHRKMHA71aHHV3lP5F1dbZbQ5PYQoXbYdqupM13H15WhQ1d+uIuOZIw== X-Received: by 2002:a05:600c:4f42:b0:426:6220:cb57 with SMTP id 5b1f17b1804b1-42bb02d8cfemr56477055e9.25.1725055198712; Fri, 30 Aug 2024 14:59:58 -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 5b1f17b1804b1-42bb6e33e2asm57836845e9.45.2024.08.30.14.59.57 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 30 Aug 2024 14:59:58 -0700 (PDT) Received: from jsm28 (helo=localhost) by digraph.polyomino.org.uk with local-esmtp (Exim 4.95) (envelope-from ) id 1sk9eQ-001Xr4-34 for libc-alpha@sourceware.org; Fri, 30 Aug 2024 21:59:26 +0000 Date: Fri, 30 Aug 2024 21:59:26 +0000 (UTC) From: Joseph Myers To: libc-alpha@sourceware.org Subject: Add more thorough tests of freopen Message-ID: <92e8ff20-fb9a-a289-2e58-faaa8bb71be2@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-9.0 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_H3, 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~incoming=patchwork.ozlabs.org@sourceware.org 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. Note that there are two parts of the test disabled because of bugs discovered through running the test; one already has an open bug as indicated in the comment, the other (failure to clear FD_CLOEXEC when the mode indicates that should be done) doesn't appear to have an open bug at present. I expect to address those (including filing the required bug for the latter) separately. The test also doesn't cover changes to cancellation ("c" in mode); I think that will better be handled through a separate test. There are support/ changes to add helper interfaces used in checking file contents; those are worth careful review. Tested for x86_64. diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 948d960ccc..3c3d8a2bcd 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -216,6 +216,7 @@ tests := \ tst-fmemopen4 \ tst-fphex \ tst-fphex-wide \ + tst-freopen2 \ tst-fseek \ tst-fwrite \ tst-getline \ diff --git a/stdio-common/tst-freopen2.c b/stdio-common/tst-freopen2.c new file mode 100644 index 0000000000..4a5a3c016e --- /dev/null +++ b/stdio-common/tst-freopen2.c @@ -0,0 +1,446 @@ +/* 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 + +int +do_test (void) +{ + 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. */ + + 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); + + verbose_printf ("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 (file2, "File2new"); + + verbose_printf ("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"); + + verbose_printf ("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); + + verbose_printf ("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 (file2, "next"); + + verbose_printf ("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"); + + verbose_printf ("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); + + verbose_printf ("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"); + + verbose_printf ("Testing a -> a\n"); + fp = xfopen (file1, "w"); + 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"); + + /* Test changing to/from b (binary, no-op). */ + + verbose_printf ("Testing rb -> r\n"); + fp = xfopen (file1, "rb"); + fp = freopen (file2, "r", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "another5"); + xfclose (fp); + + verbose_printf ("Testing r -> rb\n"); + fp = xfopen (file1, "r"); + fp = freopen (file2, "rb", fp); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE_FILE_STRING (fp, "another5"); + xfclose (fp); + + /* Test changing to/from + (read-and-write). */ + + verbose_printf ("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"); + + verbose_printf ("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"); + + verbose_printf ("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"); + + verbose_printf ("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"); + + verbose_printf ("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"); + + verbose_printf ("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"); + + /* Test changing to/from e (FD_CLOEXEC). */ + + verbose_printf ("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. */ + TEST_COMPARE (ret & FD_CLOEXEC, 0); +#endif + TEST_COMPARE_FILE_STRING (fp, "plustomore"); + xfclose (fp); + + verbose_printf ("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); + + /* Test changing to/from m (mmap) (a no-op as far as testing + semantics is concerned). */ + + verbose_printf ("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); + + verbose_printf ("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); + + /* Test changing to/from x (O_EXCL). */ + + verbose_printf ("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"); + + verbose_printf ("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"); + + verbose_printf ("Testing w -> wx (file exists)\n"); + fp = xfopen (file1, "w"); + fp = freopen (file4, "wx", fp); + TEST_VERIFY (fp == NULL); + + /* Test with ,ccs=CHARSET. */ + + verbose_printf ("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); + + verbose_printf ("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); + + verbose_printf ("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); + + /* Test old file is closed even when opening the new file fails. */ + + verbose_printf ("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); + + /* Test that errors closing the old file are ignored. */ + + verbose_printf ("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); + + /* Test that error / EOF state from the old file are cleared. */ + + verbose_printf ("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); + + verbose_printf ("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); + + /* Test freopen with NULL, same mode (should flush content and reset + file offset). */ + + verbose_printf ("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); + + /* Test freopen with NULL, different mode. */ + + verbose_printf ("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); + + /* Test freopen with NULL, renamed file. */ + + verbose_printf ("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); + + /* Test freopen with NULL, deleted file. */ + + verbose_printf ("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"); + + free (temp_dir); + free (file1); + free (file2); + free (file3); + free (file4); + free (file_nodir); + free (file1a); + return 0; +} + +#include diff --git a/support/Makefile b/support/Makefile index 6e3c55394f..26bd3d38e4 100644 --- a/support/Makefile +++ b/support/Makefile @@ -49,6 +49,8 @@ libsupport-routines = \ support_check_stat_fd \ support_check_stat_path \ support_chroot \ + support_compare_file_bytes \ + support_compare_file_string \ support_copy_file \ support_copy_file_range \ support_create_timer \ @@ -65,6 +67,8 @@ libsupport-routines = \ support_isolate_in_subprocess \ support_mutex_pi_monotonic \ support_need_proc \ + support_open_and_compare_file_bytes \ + support_open_and_compare_file_string \ support_openpty \ support_path_support_time64 \ support_paths \ diff --git a/support/file_contents.h b/support/file_contents.h new file mode 100644 index 0000000000..eea05eebc0 --- /dev/null +++ b/support/file_contents.h @@ -0,0 +1,56 @@ +/* Functionality for checking file contents. + 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 + . */ + +#ifndef SUPPORT_FILE_CONTENTS_H +#define SUPPORT_FILE_CONTENTS_H + +#include +#include + +__BEGIN_DECLS + +/* Check that an already-open file has exactly the given bytes, + starting at the current offset. */ +int support_compare_file_bytes (FILE *fp, const char *contents, size_t length); + +/* Check that an already-open file has exactly the given string as + contents, starting at the current offset. */ +int support_compare_file_string (FILE *fp, const char *contents); + +/* Check that a not-currently-open file has exactly the given + bytes. */ +int support_open_and_compare_file_bytes (const char *file, + const char *contents, + size_t length); + +/* Check that a not-currently-open file has exactly the given string + as contents, starting at the current offset. */ +int support_open_and_compare_file_string (const char *file, + const char *contents); + +/* Compare bytes read from an open file with the given string. */ +#define TEST_COMPARE_FILE_STRING(FP, CONTENTS) \ + TEST_COMPARE (support_compare_file_string (FP, CONTENTS), 0) + +/* Open a file and compare bytes read from it with the given string. */ +#define TEST_OPEN_AND_COMPARE_FILE_STRING(FILE, CONTENTS) \ + TEST_COMPARE (support_open_and_compare_file_string (FILE, CONTENTS), 0) + +__END_DECLS + +#endif /* SUPPORT_FILE_CONTENTS_H */ diff --git a/support/support_compare_file_bytes.c b/support/support_compare_file_bytes.c new file mode 100644 index 0000000000..6e3b51b0a0 --- /dev/null +++ b/support/support_compare_file_bytes.c @@ -0,0 +1,42 @@ +/* Compare bytes from an open file. + 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 + +/* Check that an already-open file has exactly the given bytes, + starting at the current offset. */ + +int +support_compare_file_bytes (FILE *fp, const char *contents, size_t length) +{ + int c; + while (length > 0) + { + c = getc (fp); + if (c == EOF || (unsigned char) c != (unsigned char) contents[0]) + return 1; + contents++; + length--; + } + c = getc (fp); + if (c != EOF) + return 1; + return 0; +} diff --git a/support/support_compare_file_string.c b/support/support_compare_file_string.c new file mode 100644 index 0000000000..04513c3af1 --- /dev/null +++ b/support/support_compare_file_string.c @@ -0,0 +1,28 @@ +/* Compare string from an open file. + 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 + +int +support_compare_file_string (FILE *fp, const char *contents) +{ + return support_compare_file_bytes (fp, contents, strlen (contents)); +} diff --git a/support/support_open_and_compare_file_bytes.c b/support/support_open_and_compare_file_bytes.c new file mode 100644 index 0000000000..f804ed8e46 --- /dev/null +++ b/support/support_open_and_compare_file_bytes.c @@ -0,0 +1,33 @@ +/* Compare bytes from a file. + 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 + +/* Check that a not-currently-open file has exactly the given + bytes. */ + +int +support_open_and_compare_file_bytes (const char *file, const char *contents, + size_t length) +{ + FILE *fp = xfopen (file, "r"); + int ret = support_compare_file_bytes (fp, contents, length); + xfclose (fp); + return ret; +} diff --git a/support/support_open_and_compare_file_string.c b/support/support_open_and_compare_file_string.c new file mode 100644 index 0000000000..2b596d4c88 --- /dev/null +++ b/support/support_open_and_compare_file_string.c @@ -0,0 +1,32 @@ +/* Compare string from a file. + 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 + +/* Check that a not-currently-open file has exactly the given string + as contents, starting at the current offset. */ + +int +support_open_and_compare_file_string (const char *file, const char *contents) +{ + return support_open_and_compare_file_bytes (file, contents, + strlen (contents)); +}