(root)/
coreutils-9.4/
gnulib-tests/
test-linkat.c
       1  /* Tests of linkat.
       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 <unistd.h>
      22  
      23  #include "signature.h"
      24  SIGNATURE_CHECK (linkat, int, (int, char const *, int, char const *, int));
      25  
      26  #include <fcntl.h>
      27  #include <errno.h>
      28  #include <stdio.h>
      29  #include <stdlib.h>
      30  #include <string.h>
      31  #include <sys/stat.h>
      32  
      33  #include "areadlink.h"
      34  #include "filenamecat.h"
      35  #include "same-inode.h"
      36  #include "ignore-value.h"
      37  #include "macros.h"
      38  
      39  #define BASE "test-linkat.t"
      40  
      41  #include "test-link.h"
      42  
      43  static int dfd1 = AT_FDCWD;
      44  static int dfd2 = AT_FDCWD;
      45  static int flag = AT_SYMLINK_FOLLOW;
      46  
      47  /* Wrapper to test linkat like link.  */
      48  static int
      49  do_link (char const *name1, char const *name2)
      50  {
      51    return linkat (dfd1, name1, dfd2, name2, flag);
      52  }
      53  
      54  /* Can we expect that link() and linkat(), when called on a symlink,
      55     increment the link count of that symlink?  */
      56  #if LINK_FOLLOWS_SYMLINKS == 0
      57  # define EXPECT_LINK_HARDLINKS_SYMLINKS 1
      58  #elif LINK_FOLLOWS_SYMLINKS == -1
      59  extern int __xpg4;
      60  # define EXPECT_LINK_HARDLINKS_SYMLINKS (__xpg4 == 0)
      61  #else
      62  # define EXPECT_LINK_HARDLINKS_SYMLINKS 0
      63  #endif
      64  
      65  /* Wrapper to see if two symlinks act the same.  */
      66  static void
      67  check_same_link (char const *name1, char const *name2)
      68  {
      69    struct stat st1;
      70    struct stat st2;
      71    char *contents1;
      72    char *contents2;
      73    ASSERT (lstat (name1, &st1) == 0);
      74    ASSERT (lstat (name2, &st2) == 0);
      75    contents1 = areadlink_with_size (name1, st1.st_size);
      76    contents2 = areadlink_with_size (name2, st2.st_size);
      77    ASSERT (contents1);
      78    ASSERT (contents2);
      79    ASSERT (strcmp (contents1, contents2) == 0);
      80    if (EXPECT_LINK_HARDLINKS_SYMLINKS)
      81      ASSERT (SAME_INODE (st1, st2));
      82    free (contents1);
      83    free (contents2);
      84  }
      85  
      86  int
      87  main (void)
      88  {
      89    int i;
      90    int dfd;
      91    char *cwd;
      92    int result;
      93  
      94    /* Clean up any trash from prior testsuite runs.  */
      95    ignore_value (system ("rm -rf " BASE "*"));
      96  
      97    /* Test behaviour for invalid file descriptors.  */
      98    {
      99      errno = 0;
     100      ASSERT (linkat (-1, "foo", AT_FDCWD, "bar", 0) == -1);
     101      ASSERT (errno == EBADF);
     102    }
     103    {
     104      close (99);
     105      errno = 0;
     106      ASSERT (linkat (99, "foo", AT_FDCWD, "bar", 0) == -1);
     107      ASSERT (errno == EBADF);
     108    }
     109    ASSERT (close (creat (BASE "oo", 0600)) == 0);
     110    {
     111      errno = 0;
     112      ASSERT (linkat (AT_FDCWD, BASE "oo", -1, "bar", 0) == -1);
     113      ASSERT (errno == EBADF);
     114    }
     115    {
     116      errno = 0;
     117      ASSERT (linkat (AT_FDCWD, BASE "oo", 99, "bar", 0) == -1);
     118      ASSERT (errno == EBADF);
     119    }
     120    ASSERT (unlink (BASE "oo") == 0);
     121  
     122    /* Test basic link functionality, without mentioning symlinks.  */
     123    result = test_link (do_link, true);
     124    dfd1 = open (".", O_RDONLY);
     125    ASSERT (0 <= dfd1);
     126    ASSERT (test_link (do_link, false) == result);
     127    dfd2 = dfd1;
     128    ASSERT (test_link (do_link, false) == result);
     129    dfd1 = AT_FDCWD;
     130    ASSERT (test_link (do_link, false) == result);
     131    flag = 0;
     132    ASSERT (test_link (do_link, false) == result);
     133    dfd1 = dfd2;
     134    ASSERT (test_link (do_link, false) == result);
     135    dfd2 = AT_FDCWD;
     136    ASSERT (test_link (do_link, false) == result);
     137    ASSERT (close (dfd1) == 0);
     138    dfd1 = AT_FDCWD;
     139    ASSERT (test_link (do_link, false) == result);
     140  
     141    /* Skip the rest of the test if the file system does not support hard links
     142       and symlinks.  */
     143    if (result)
     144      return result;
     145  
     146    /* Create locations to manipulate.  */
     147    ASSERT (mkdir (BASE "sub1", 0700) == 0);
     148    ASSERT (mkdir (BASE "sub2", 0700) == 0);
     149    ASSERT (close (creat (BASE "00", 0600)) == 0);
     150    cwd = getcwd (NULL, 0);
     151    ASSERT (cwd);
     152  
     153    dfd = open (BASE "sub1", O_RDONLY);
     154    ASSERT (0 <= dfd);
     155    ASSERT (chdir (BASE "sub2") == 0);
     156  
     157    /* There are 16 possible scenarios, based on whether an fd is
     158       AT_FDCWD or real, whether a file is absolute or relative, coupled
     159       with whether flag is set for 32 iterations.
     160  
     161       To ensure that we test all of the code paths (rather than
     162       triggering early normalization optimizations), we use a loop to
     163       repeatedly rename a file in the parent directory, use an fd open
     164       on subdirectory 1, all while executing in subdirectory 2; all
     165       relative names are thus given with a leading "../".  Finally, the
     166       last scenario (two relative paths given, neither one AT_FDCWD)
     167       has two paths, based on whether the two fds are equivalent, so we
     168       do the other variant after the loop.  */
     169    for (i = 0; i < 32; i++)
     170      {
     171        int fd1 = (i & 8) ? dfd : AT_FDCWD;
     172        char *file1 = mfile_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL);
     173        int fd2 = (i & 2) ? dfd : AT_FDCWD;
     174        char *file2 = mfile_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL);
     175        ASSERT (file1);
     176        ASSERT (file2);
     177        flag = (i & 0x10 ? AT_SYMLINK_FOLLOW : 0);
     178  
     179        ASSERT (sprintf (strchr (file1, '\0') - 2, "%02d", i) == 2);
     180        ASSERT (sprintf (strchr (file2, '\0') - 2, "%02d", i + 1) == 2);
     181        ASSERT (linkat (fd1, file1, fd2, file2, flag) == 0);
     182        ASSERT (unlinkat (fd1, file1, 0) == 0);
     183        free (file1);
     184        free (file2);
     185      }
     186    dfd2 = open ("..", O_RDONLY);
     187    ASSERT (0 <= dfd2);
     188    ASSERT (linkat (dfd, "../" BASE "32", dfd2, BASE "33", 0) == 0);
     189    ASSERT (linkat (dfd, "../" BASE "33", dfd2, BASE "34",
     190                    AT_SYMLINK_FOLLOW) == 0);
     191    ASSERT (close (dfd2) == 0);
     192  
     193    /* Now we change back to the parent directory, and set dfd to ".",
     194       in order to test behavior on symlinks.  */
     195    ASSERT (chdir ("..") == 0);
     196    ASSERT (close (dfd) == 0);
     197    if (symlink (BASE "sub1", BASE "link1"))
     198      {
     199        ASSERT (unlink (BASE "32") == 0);
     200        ASSERT (unlink (BASE "33") == 0);
     201        ASSERT (unlink (BASE "34") == 0);
     202        ASSERT (rmdir (BASE "sub1") == 0);
     203        ASSERT (rmdir (BASE "sub2") == 0);
     204        free (cwd);
     205        if (!result)
     206          fputs ("skipping test: symlinks not supported on this file system\n",
     207                 stderr);
     208        return result;
     209      }
     210    dfd = open (".", O_RDONLY);
     211    ASSERT (0 <= dfd);
     212    ASSERT (symlink (BASE "34", BASE "link2") == 0);
     213    ASSERT (symlink (BASE "link3", BASE "link3") == 0);
     214    ASSERT (symlink (BASE "nowhere", BASE "link4") == 0);
     215  
     216    /* Link cannot overwrite existing files.  */
     217    errno = 0;
     218    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1", 0) == -1);
     219    ASSERT (errno == EEXIST);
     220    errno = 0;
     221    ASSERT (linkat (dfd, BASE "link1/", dfd, BASE "sub1", 0) == -1);
     222    ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
     223    errno = 0;
     224    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1/", 0) == -1);
     225    ASSERT (errno == EEXIST || errno == ENOTDIR);
     226    errno = 0;
     227    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1",
     228                    AT_SYMLINK_FOLLOW) == -1);
     229    ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
     230    errno = 0;
     231    ASSERT (linkat (dfd, BASE "link1/", dfd, BASE "sub1",
     232                    AT_SYMLINK_FOLLOW) == -1);
     233    ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES
     234            || errno == EINVAL);
     235    errno = 0;
     236    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "sub1/",
     237                    AT_SYMLINK_FOLLOW) == -1);
     238    ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES
     239            || errno == EINVAL);
     240    errno = 0;
     241    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link2", 0) == -1);
     242    ASSERT (errno == EEXIST);
     243    errno = 0;
     244    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link2",
     245                    AT_SYMLINK_FOLLOW) == -1);
     246    ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
     247    errno = 0;
     248    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link3", 0) == -1);
     249    ASSERT (errno == EEXIST || errno == ELOOP);
     250    errno = 0;
     251    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link3",
     252                    AT_SYMLINK_FOLLOW) == -1);
     253    ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES
     254            || errno == ELOOP);
     255    errno = 0;
     256    ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link3", 0) == -1);
     257    ASSERT (errno == EEXIST || errno == ELOOP);
     258    errno = 0;
     259    ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link3",
     260                    AT_SYMLINK_FOLLOW) == -1);
     261    ASSERT (errno == EEXIST || errno == ELOOP);
     262  
     263    /* AT_SYMLINK_FOLLOW only follows first argument, not second.  */
     264    errno = 0;
     265    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link4", 0) == -1);
     266    ASSERT (errno == EEXIST);
     267    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link4",
     268                    AT_SYMLINK_FOLLOW) == -1);
     269    ASSERT (errno == EEXIST || errno == EPERM || errno == EACCES);
     270    errno = 0;
     271    ASSERT (linkat (dfd, BASE "34", dfd, BASE "link4", 0) == -1);
     272    ASSERT (errno == EEXIST);
     273    errno = 0;
     274    ASSERT (linkat (dfd, BASE "34", dfd, BASE "link4", AT_SYMLINK_FOLLOW) == -1);
     275    ASSERT (errno == EEXIST);
     276  
     277    /* Trailing slash handling.  */
     278    errno = 0;
     279    ASSERT (linkat (dfd, BASE "link2/", dfd, BASE "link5", 0) == -1);
     280    ASSERT (errno == ENOTDIR);
     281    errno = 0;
     282    ASSERT (linkat (dfd, BASE "link2/", dfd, BASE "link5",
     283                    AT_SYMLINK_FOLLOW) == -1);
     284    ASSERT (errno == ENOTDIR || errno == EINVAL);
     285    errno = 0;
     286    ASSERT (linkat (dfd, BASE "link3/", dfd, BASE "link5", 0) == -1);
     287    ASSERT (errno == ELOOP);
     288    errno = 0;
     289    ASSERT (linkat (dfd, BASE "link3/", dfd, BASE "link5",
     290                    AT_SYMLINK_FOLLOW) == -1);
     291    ASSERT (errno == ELOOP || errno == EINVAL);
     292    errno = 0;
     293    ASSERT (linkat (dfd, BASE "link4/", dfd, BASE "link5", 0) == -1);
     294    ASSERT (errno == ENOENT);
     295    errno = 0;
     296    ASSERT (linkat (dfd, BASE "link4/", dfd, BASE "link5",
     297                    AT_SYMLINK_FOLLOW) == -1);
     298    ASSERT (errno == ENOENT || errno == EINVAL);
     299  
     300    /* Check for hard links to symlinks.  */
     301    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link5", 0) == 0);
     302    check_same_link (BASE "link1", BASE "link5");
     303    ASSERT (unlink (BASE "link5") == 0);
     304    errno = 0;
     305    ASSERT (linkat (dfd, BASE "link1", dfd, BASE "link5",
     306                    AT_SYMLINK_FOLLOW) == -1);
     307    ASSERT (errno == EPERM || errno == EACCES);
     308    ASSERT (linkat (dfd, BASE "link2", dfd, BASE "link5", 0) == 0);
     309    check_same_link (BASE "link2", BASE "link5");
     310    ASSERT (unlink (BASE "link5") == 0);
     311    ASSERT (linkat (dfd, BASE "link2", dfd, BASE "file", AT_SYMLINK_FOLLOW) == 0);
     312    errno = 0;
     313    ASSERT (areadlink (BASE "file") == NULL);
     314    ASSERT (errno == EINVAL);
     315    ASSERT (unlink (BASE "file") == 0);
     316    ASSERT (linkat (dfd, BASE "link3", dfd, BASE "link5", 0) == 0);
     317    check_same_link (BASE "link3", BASE "link5");
     318    ASSERT (unlink (BASE "link5") == 0);
     319    errno = 0;
     320    ASSERT (linkat (dfd, BASE "link3", dfd, BASE "link5",
     321                    AT_SYMLINK_FOLLOW) == -1);
     322    ASSERT (errno == ELOOP);
     323    ASSERT (linkat (dfd, BASE "link4", dfd, BASE "link5", 0) == 0);
     324    check_same_link (BASE "link4", BASE "link5");
     325    ASSERT (unlink (BASE "link5") == 0);
     326    errno = 0;
     327    ASSERT (linkat (dfd, BASE "link4", dfd, BASE "link5",
     328                    AT_SYMLINK_FOLLOW) == -1);
     329    ASSERT (errno == ENOENT);
     330  
     331    /* Check that symlink to symlink to file is followed all the way.  */
     332    ASSERT (symlink (BASE "link2", BASE "link5") == 0);
     333    ASSERT (linkat (dfd, BASE "link5", dfd, BASE "link6", 0) == 0);
     334    check_same_link (BASE "link5", BASE "link6");
     335    ASSERT (unlink (BASE "link6") == 0);
     336    ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file", AT_SYMLINK_FOLLOW) == 0);
     337    errno = 0;
     338    ASSERT (areadlink (BASE "file") == NULL);
     339    ASSERT (errno == EINVAL);
     340    ASSERT (unlink (BASE "file") == 0);
     341    ASSERT (unlink (BASE "link5") == 0);
     342    ASSERT (symlink (BASE "link3", BASE "link5") == 0);
     343    errno = 0;
     344    ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file",
     345                    AT_SYMLINK_FOLLOW) == -1);
     346    ASSERT (errno == ELOOP);
     347    ASSERT (unlink (BASE "link5") == 0);
     348    ASSERT (symlink (BASE "link4", BASE "link5") == 0);
     349    errno = 0;
     350    ASSERT (linkat (dfd, BASE "link5", dfd, BASE "file",
     351                    AT_SYMLINK_FOLLOW) == -1);
     352    ASSERT (errno == ENOENT);
     353  
     354    /* Now for some real fun with directory crossing.  */
     355    ASSERT (symlink (cwd, BASE "sub1/link") == 0);
     356    ASSERT (symlink (".././/" BASE "sub1/link/" BASE "link2",
     357                     BASE "sub2/link") == 0);
     358    ASSERT (close (dfd) == 0);
     359    dfd = open (BASE "sub1", O_RDONLY);
     360    ASSERT (0 <= dfd);
     361    dfd2 = open (BASE "sub2", O_RDONLY);
     362    ASSERT (0 < dfd2);
     363    ASSERT (linkat (dfd, "../" BASE "sub2/link", dfd2, "./..//" BASE "sub1/file",
     364                AT_SYMLINK_FOLLOW) == 0);
     365    errno = 0;
     366    ASSERT (areadlink (BASE "sub1/file") == NULL);
     367    ASSERT (errno == EINVAL);
     368  
     369    /* Cleanup.  */
     370    ASSERT (close (dfd) == 0);
     371    ASSERT (close (dfd2) == 0);
     372    ASSERT (unlink (BASE "sub1/file") == 0);
     373    ASSERT (unlink (BASE "sub1/link") == 0);
     374    ASSERT (unlink (BASE "sub2/link") == 0);
     375    ASSERT (unlink (BASE "32") == 0);
     376    ASSERT (unlink (BASE "33") == 0);
     377    ASSERT (unlink (BASE "34") == 0);
     378    ASSERT (rmdir (BASE "sub1") == 0);
     379    ASSERT (rmdir (BASE "sub2") == 0);
     380    ASSERT (unlink (BASE "link1") == 0);
     381    ASSERT (unlink (BASE "link2") == 0);
     382    ASSERT (unlink (BASE "link3") == 0);
     383    ASSERT (unlink (BASE "link4") == 0);
     384    ASSERT (unlink (BASE "link5") == 0);
     385    free (cwd);
     386    return result;
     387  }