(root)/
glibc-2.38/
stdio-common/
tst-renameat2.c
       1  /* Linux implementation for renameat2 function.
       2     Copyright (C) 2018-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library.  If not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <array_length.h>
      20  #include <errno.h>
      21  #include <fcntl.h>
      22  #include <stdbool.h>
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <support/check.h>
      26  #include <support/support.h>
      27  #include <support/temp_file.h>
      28  #include <support/xunistd.h>
      29  #include <unistd.h>
      30  
      31  /* Directory with the temporary files.  */
      32  static char *directory;
      33  static int directory_fd;
      34  
      35  /* Paths within that directory.  */
      36  static char *old_path;          /* File is called "old".  */
      37  static char *new_path;          /* File is called "new".  */
      38  
      39  /* Subdirectory within the directory above.  */
      40  static char *subdirectory;
      41  int subdirectory_fd;
      42  
      43  /* And a pathname in that directory (called "file").  */
      44  static char *subdir_path;
      45  
      46  static void
      47  prepare (int argc, char **argv)
      48  {
      49    directory = support_create_temp_directory ("tst-renameat2-");
      50    directory_fd = xopen (directory, O_RDONLY | O_DIRECTORY, 0);
      51    old_path = xasprintf ("%s/old", directory);
      52    add_temp_file (old_path);
      53    new_path = xasprintf ("%s/new", directory);
      54    add_temp_file (new_path);
      55    subdirectory = xasprintf ("%s/subdir", directory);
      56    xmkdir (subdirectory, 0777);
      57    add_temp_file (subdirectory);
      58    subdirectory_fd = xopen (subdirectory, O_RDONLY | O_DIRECTORY, 0);
      59    subdir_path = xasprintf ("%s/file", subdirectory);
      60    add_temp_file (subdir_path);
      61  }
      62  
      63  /* Delete all files, preparing a clean slate for the next test.  */
      64  static void
      65  delete_all_files (void)
      66  {
      67    char *files[] = { old_path, new_path, subdir_path };
      68    for (size_t i = 0; i < array_length (files); ++i)
      69      if (unlink (files[i]) != 0 && errno != ENOENT)
      70        FAIL_EXIT1 ("unlink (\"%s\"): %m", files[i]);
      71  }
      72  
      73  /* Return true if PATH exists in the file system.  */
      74  static bool
      75  file_exists (const char *path)
      76  {
      77    return access (path, F_OK) == 0;
      78  }
      79  
      80  /* Check that PATH exists and has size EXPECTED_SIZE.  */
      81  static void
      82  check_size (const char *path, off64_t expected_size)
      83  {
      84    struct stat64 st;
      85    xstat (path, &st);
      86    if (st.st_size != expected_size)
      87      FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld",
      88                  path, (unsigned long long int) expected_size,
      89                  (unsigned long long int) st.st_size);
      90  }
      91  
      92  /* Rename tests where the target does not exist.  */
      93  static void
      94  rename_without_existing_target (unsigned int flags)
      95  {
      96    delete_all_files ();
      97    support_write_file_string (old_path, "");
      98    TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, flags), 0);
      99    TEST_VERIFY (!file_exists (old_path));
     100    TEST_VERIFY (file_exists (new_path));
     101  
     102    delete_all_files ();
     103    support_write_file_string (old_path, "");
     104    TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, flags), 0);
     105    TEST_VERIFY (!file_exists (old_path));
     106    TEST_VERIFY (file_exists (new_path));
     107  
     108    delete_all_files ();
     109    support_write_file_string (old_path, "");
     110    TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", 0),
     111                  0);
     112    TEST_VERIFY (!file_exists (old_path));
     113    TEST_VERIFY (file_exists (subdir_path));
     114  }
     115  
     116  static int
     117  do_test (void)
     118  {
     119    /* Tests with zero flags argument.  These are expected to succeed
     120       because this renameat2 variant can be implemented with
     121       renameat.  */
     122    rename_without_existing_target (0);
     123  
     124    /* renameat2 without flags replaces an existing destination.  */
     125    delete_all_files ();
     126    support_write_file_string (old_path, "123");
     127    support_write_file_string (new_path, "1234");
     128    TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, 0), 0);
     129    TEST_VERIFY (!file_exists (old_path));
     130    check_size (new_path, 3);
     131  
     132    /* Now we need to check for kernel support of renameat2 with
     133       flags.  */
     134    delete_all_files ();
     135    support_write_file_string (old_path, "");
     136    if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE)
     137        != 0)
     138      {
     139        if (errno == EINVAL)
     140          puts ("warning: no support for renameat2 with flags");
     141        else
     142          FAIL_EXIT1 ("renameat2 probe failed: %m");
     143      }
     144    else
     145      {
     146        /* We have full renameat2 support.  */
     147        rename_without_existing_target (RENAME_NOREPLACE);
     148  
     149        /* Now test RENAME_NOREPLACE with an existing target.  */
     150        delete_all_files ();
     151        support_write_file_string (old_path, "123");
     152        support_write_file_string (new_path, "1234");
     153        TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path,
     154                                 RENAME_NOREPLACE), -1);
     155        TEST_COMPARE (errno, EEXIST);
     156        check_size (old_path, 3);
     157        check_size (new_path, 4);
     158  
     159        delete_all_files ();
     160        support_write_file_string (old_path, "123");
     161        support_write_file_string (new_path, "1234");
     162        TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path,
     163                                 RENAME_NOREPLACE), -1);
     164        TEST_COMPARE (errno, EEXIST);
     165        check_size (old_path, 3);
     166        check_size (new_path, 4);
     167  
     168        delete_all_files ();
     169        support_write_file_string (old_path, "123");
     170        support_write_file_string (subdir_path, "1234");
     171        TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file",
     172                                 RENAME_NOREPLACE), -1);
     173        TEST_COMPARE (errno, EEXIST);
     174        check_size (old_path, 3);
     175        check_size (subdir_path, 4);
     176  
     177        /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE
     178           is invalid.  */
     179        TEST_COMPARE (renameat2 (directory_fd, "ignored",
     180                                 subdirectory_fd, "ignored",
     181                                 RENAME_NOREPLACE | RENAME_EXCHANGE), -1);
     182        TEST_COMPARE (errno, EINVAL);
     183      }
     184  
     185    /* Create all the pathnames to avoid warnings from the test
     186       harness.  */
     187    support_write_file_string (old_path, "");
     188    support_write_file_string (new_path, "");
     189    support_write_file_string (subdir_path, "");
     190  
     191    free (directory);
     192    free (subdirectory);
     193    free (old_path);
     194    free (new_path);
     195    free (subdir_path);
     196  
     197    xclose (directory_fd);
     198    xclose (subdirectory_fd);
     199  
     200    return 0;
     201  }
     202  
     203  #define PREPARE prepare
     204  #include <support/test-driver.c>