(root)/
coreutils-9.4/
gnulib-tests/
test-renameatu.c
       1  /* Test renameatu.
       2     Copyright (C) 2009-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Eric Blake <ebb9@byu.net>, 2009.  */
      18  
      19  #include <config.h>
      20  
      21  #include <renameatu.h>
      22  
      23  #include <stdio.h>
      24  
      25  #include "signature.h"
      26  SIGNATURE_CHECK (renameatu, int,
      27                   (int, char const *, int, char const *, unsigned int));
      28  
      29  #include <dirent.h>
      30  #include <fcntl.h>
      31  #include <errno.h>
      32  #include <stdlib.h>
      33  #include <string.h>
      34  #include <unistd.h>
      35  #include <sys/stat.h>
      36  
      37  #include "filenamecat.h"
      38  #include "ignore-value.h"
      39  #include "macros.h"
      40  
      41  #define BASE "test-renameatu.t"
      42  
      43  #include "test-rename.h"
      44  
      45  static int dfd1 = AT_FDCWD;
      46  static int dfd2 = AT_FDCWD;
      47  
      48  /* Wrapper to test renameatu like rename.  */
      49  static int
      50  do_rename (char const *name1, char const *name2)
      51  {
      52    return renameatu (dfd1, name1, dfd2, name2, 0);
      53  }
      54  
      55  int
      56  main (void)
      57  {
      58    int i;
      59    int dfd;
      60    char *cwd;
      61    int result;
      62  
      63    /* Clean up any trash from prior testsuite runs.  */
      64    ignore_value (system ("rm -rf " BASE "*"));
      65  
      66    /* Test behaviour for invalid file descriptors.  */
      67    {
      68      errno = 0;
      69      ASSERT (renameatu (-1, "foo", AT_FDCWD, "bar", 0) == -1);
      70      ASSERT (errno == EBADF);
      71    }
      72    {
      73      close (99);
      74      errno = 0;
      75      ASSERT (renameatu (99, "foo", AT_FDCWD, "bar", 0) == -1);
      76      ASSERT (errno == EBADF);
      77    }
      78    ASSERT (close (creat (BASE "oo", 0600)) == 0);
      79    {
      80      errno = 0;
      81      ASSERT (renameatu (AT_FDCWD, BASE "oo", -1, "bar", 0) == -1);
      82      ASSERT (errno == EBADF);
      83    }
      84    {
      85      errno = 0;
      86      ASSERT (renameatu (AT_FDCWD, BASE "oo", 99, "bar", 0) == -1);
      87      ASSERT (errno == EBADF);
      88    }
      89    ASSERT (unlink (BASE "oo") == 0);
      90  
      91    /* Test basic rename functionality, using current directory.  */
      92    result = test_rename (do_rename, false);
      93    dfd1 = open (".", O_RDONLY);
      94    ASSERT (0 <= dfd1);
      95    ASSERT (test_rename (do_rename, false) == result);
      96    dfd2 = dfd1;
      97    ASSERT (test_rename (do_rename, false) == result);
      98    dfd1 = AT_FDCWD;
      99    ASSERT (test_rename (do_rename, false) == result);
     100    ASSERT (close (dfd2) == 0);
     101  
     102    /* Create locations to manipulate.  */
     103    ASSERT (mkdir (BASE "sub1", 0700) == 0);
     104    ASSERT (mkdir (BASE "sub2", 0700) == 0);
     105    dfd = creat (BASE "00", 0600);
     106    ASSERT (0 <= dfd);
     107    ASSERT (close (dfd) == 0);
     108    cwd = getcwd (NULL, 0);
     109    ASSERT (cwd);
     110  
     111    dfd = open (BASE "sub1", O_RDONLY);
     112    ASSERT (0 <= dfd);
     113    ASSERT (chdir (BASE "sub2") == 0);
     114  
     115    /* There are 16 possible scenarios, based on whether an fd is
     116       AT_FDCWD or real, and whether a file is absolute or relative.
     117  
     118       To ensure that we test all of the code paths (rather than
     119       triggering early normalization optimizations), we use a loop to
     120       repeatedly rename a file in the parent directory, use an fd open
     121       on subdirectory 1, all while executing in subdirectory 2; all
     122       relative names are thus given with a leading "../".  Finally, the
     123       last scenario (two relative paths given, neither one AT_FDCWD)
     124       has two paths, based on whether the two fds are equivalent, so we
     125       do the other variant after the loop.  */
     126    for (i = 0; i < 16; i++)
     127      {
     128        int fd1 = (i & 8) ? dfd : AT_FDCWD;
     129        char *file1 = file_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL);
     130        int fd2 = (i & 2) ? dfd : AT_FDCWD;
     131        char *file2 = file_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL);
     132  
     133        ASSERT (sprintf (strchr (file1, '\0') - 2, "%02d", i) == 2);
     134        ASSERT (sprintf (strchr (file2, '\0') - 2, "%02d", i + 1) == 2);
     135        ASSERT (renameatu (fd1, file1, fd2, file2, 0) == 0);
     136        free (file1);
     137        free (file2);
     138      }
     139    dfd2 = open ("..", O_RDONLY);
     140    ASSERT (0 <= dfd2);
     141    ASSERT (renameatu (dfd, "../" BASE "16", dfd2, BASE "17", 0) == 0);
     142    ASSERT (close (dfd2) == 0);
     143  
     144    /* Now we change back to the parent directory, and set dfd to ".";
     145       using dfd in remaining tests will expose any bugs if emulation
     146       via /proc/self/fd doesn't check for empty names.  */
     147    ASSERT (chdir ("..") == 0);
     148    ASSERT (close (dfd) == 0);
     149    dfd = open (".", O_RDONLY);
     150    ASSERT (0 <= dfd);
     151  
     152    ASSERT (close (creat (BASE "sub2/file", 0600)) == 0);
     153    errno = 0;
     154    ASSERT (renameatu (dfd, BASE "sub1", dfd, BASE "sub2", 0) == -1);
     155    ASSERT (errno == EEXIST || errno == ENOTEMPTY);
     156    ASSERT (unlink (BASE "sub2/file") == 0);
     157    errno = 0;
     158    ASSERT (renameatu (dfd, BASE "sub2", dfd, BASE "sub1/.", 0) == -1);
     159    ASSERT (errno == EINVAL || errno == EISDIR || errno == EBUSY
     160            || errno == ENOTEMPTY || errno == EEXIST
     161            || errno == ENOENT /* WSL */);
     162    errno = 0;
     163    ASSERT (renameatu (dfd, BASE "sub2/.", dfd, BASE "sub1", 0) == -1);
     164    ASSERT (errno == EINVAL || errno == EBUSY || errno == EEXIST
     165            || errno == ENOENT /* WSL */);
     166    errno = 0;
     167    ASSERT (renameatu (dfd, BASE "17", dfd, BASE "sub1", 0) == -1);
     168    ASSERT (errno == EISDIR);
     169    errno = 0;
     170    ASSERT (renameatu (dfd, BASE "nosuch", dfd, BASE "18", 0) == -1);
     171    ASSERT (errno == ENOENT);
     172    errno = 0;
     173    ASSERT (renameatu (dfd, "", dfd, BASE "17", 0) == -1);
     174    ASSERT (errno == ENOENT);
     175    errno = 0;
     176    ASSERT (renameatu (dfd, BASE "17", dfd, "", 0) == -1);
     177    ASSERT (errno == ENOENT);
     178    errno = 0;
     179    ASSERT (renameatu (dfd, BASE "sub2", dfd, BASE "17", 0) == -1);
     180    ASSERT (errno == ENOTDIR);
     181    errno = 0;
     182    ASSERT (renameatu (dfd, BASE "17/", dfd, BASE "18", 0) == -1);
     183    ASSERT (errno == ENOTDIR);
     184    errno = 0;
     185    ASSERT (renameatu (dfd, BASE "17", dfd, BASE "18/", 0) == -1);
     186    ASSERT (errno == ENOTDIR || errno == ENOENT);
     187  
     188    /* Finally, make sure we cannot overwrite existing files.  */
     189    ASSERT (close (creat (BASE "sub2/file", 0600)) == 0);
     190    errno = 0;
     191    ASSERT ((renameatu (dfd, BASE "sub2/file", dfd, BASE "sub2/file",
     192                        RENAME_NOREPLACE)
     193             == -1)
     194            && errno == EEXIST);
     195    errno = 0;
     196    ASSERT ((renameatu (dfd, BASE "sub2", dfd, BASE "sub2", RENAME_NOREPLACE)
     197             == -1)
     198            && errno == EEXIST);
     199    errno = 0;
     200    ASSERT ((renameatu (dfd, BASE "sub2", dfd, BASE "sub1", RENAME_NOREPLACE)
     201             == -1)
     202            && errno == EEXIST);
     203    errno = 0;
     204    ASSERT ((renameatu (dfd, BASE "sub2/file", dfd, BASE "17", RENAME_NOREPLACE)
     205             == -1)
     206            && errno == EEXIST);
     207  
     208    /* Cleanup.  */
     209    ASSERT (close (dfd) == 0);
     210    ASSERT (unlink (BASE "sub2/file") == 0);
     211    ASSERT (unlink (BASE "17") == 0);
     212    ASSERT (rmdir (BASE "sub1") == 0);
     213    ASSERT (rmdir (BASE "sub2") == 0);
     214    free (cwd);
     215  
     216    if (result)
     217      fputs ("skipping test: symlinks not supported on this file system\n",
     218             stderr);
     219    return result;
     220  }