(root)/
coreutils-9.4/
gnulib-tests/
test-link.h
       1  /* Test of link() function.
       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  /* This file is designed to test both link(a,b) and
      18     linkat(AT_FDCWD,a,AT_FDCWD,b,0).  FUNC is the function to test.
      19     Assumes that BASE and ASSERT are already defined, and that
      20     appropriate headers are already included.  If PRINT, warn before
      21     skipping tests with status 77.  This test does not try to create
      22     hard links to symlinks, but does test other aspects of symlink.  */
      23  
      24  static int
      25  test_link (int (*func) (char const *, char const *), bool print)
      26  {
      27    int fd;
      28    int ret;
      29  
      30    /* Create first file.  */
      31    fd = open (BASE "a", O_CREAT | O_EXCL | O_WRONLY, 0600);
      32    ASSERT (0 <= fd);
      33    ASSERT (write (fd, "hello", 5) == 5);
      34    ASSERT (close (fd) == 0);
      35  
      36    /* Not all file systems support link.  Mingw doesn't have reliable
      37       st_nlink on hard links, but our implementation does fail with
      38       EPERM on poor file systems, and we can detect the inferior stat()
      39       via st_ino.  Cygwin 1.5.x copies rather than links files on those
      40       file systems, but there, st_nlink and st_ino are reliable.  */
      41    ret = func (BASE "a", BASE "b");
      42    if (!ret)
      43      {
      44        struct stat st;
      45        ASSERT (stat (BASE "b", &st) == 0);
      46        if (st.st_ino && st.st_nlink != 2)
      47          {
      48            ASSERT (unlink (BASE "b") == 0);
      49            errno = EPERM;
      50            ret = -1;
      51          }
      52      }
      53    if (ret == -1)
      54      {
      55        /* If the device does not support hard links, errno is
      56           EPERM on Linux,
      57           EOPNOTSUPP on FreeBSD,
      58           EACCES on Android within Termux.  */
      59        switch (errno)
      60          {
      61          case EPERM:
      62          case EOPNOTSUPP:
      63          #if defined __ANDROID__
      64          case EACCES:
      65          #endif
      66            if (print)
      67              fputs ("skipping test: "
      68                     "hard links not supported on this file system\n",
      69                     stderr);
      70            ASSERT (unlink (BASE "a") == 0);
      71            return 77;
      72          default:
      73            perror ("link");
      74            return 1;
      75          }
      76      }
      77    ASSERT (ret == 0);
      78  
      79    /* Now, for some behavior tests.  Modify the contents of 'b', and
      80       ensure that 'a' can see it, both while 'b' exists and after.  */
      81    fd = open (BASE "b", O_APPEND | O_WRONLY);
      82    ASSERT (0 <= fd);
      83    ASSERT (write (fd, "world", 5) == 5);
      84    ASSERT (close (fd) == 0);
      85    {
      86      char buf[11] = { 0 };
      87      fd = open (BASE "a", O_RDONLY);
      88      ASSERT (0 <= fd);
      89      ASSERT (read (fd, buf, 10) == 10);
      90      ASSERT (strcmp (buf, "helloworld") == 0);
      91      ASSERT (close (fd) == 0);
      92      ASSERT (unlink (BASE "b") == 0);
      93      fd = open (BASE "a", O_RDONLY);
      94      ASSERT (0 <= fd);
      95      ASSERT (read (fd, buf, 10) == 10);
      96      ASSERT (strcmp (buf, "helloworld") == 0);
      97      ASSERT (close (fd) == 0);
      98    }
      99  
     100    /* Test for various error conditions.  */
     101    ASSERT (mkdir (BASE "d", 0700) == 0);
     102    errno = 0;
     103    ASSERT (func (BASE "a", ".") == -1);
     104    ASSERT (errno == EEXIST || errno == EINVAL);
     105    errno = 0;
     106    ASSERT (func (BASE "a", BASE "a") == -1);
     107    ASSERT (errno == EEXIST);
     108    ASSERT (func (BASE "a", BASE "b") == 0);
     109    errno = 0;
     110    ASSERT (func (BASE "a", BASE "b") == -1);
     111    ASSERT (errno == EEXIST);
     112    errno = 0;
     113    ASSERT (func (BASE "a", BASE "d") == -1);
     114    ASSERT (errno == EEXIST);
     115    errno = 0;
     116    ASSERT (func (BASE "c", BASE "e") == -1);
     117    ASSERT (errno == ENOENT);
     118    errno = 0;
     119    ASSERT (func (BASE "a", BASE "c/.") == -1);
     120    ASSERT (errno == ENOENT);
     121    errno = 0;
     122    ASSERT (func (BASE "a/", BASE "c") == -1);
     123    ASSERT (errno == ENOTDIR || errno == EINVAL);
     124    errno = 0;
     125    ASSERT (func (BASE "a", BASE "c/") == -1);
     126    ASSERT (errno == ENOTDIR || errno == ENOENT || errno == EINVAL);
     127  
     128    /* Most platforms reject hard links to directories, and even on
     129       those that do permit it, most users can't create them.  We assume
     130       that if this test is run as root and we managed to create a hard
     131       link, then unlink better be able to clean it up.  */
     132    {
     133      int result;
     134      errno = 0;
     135      result = func (BASE "d", BASE "c");
     136      if (result == 0)
     137        {
     138          /* Probably root on Solaris.  */
     139          ASSERT (unlink (BASE "c") == 0);
     140        }
     141      else
     142        {
     143          /* Most everyone else.  */
     144          ASSERT (errno == EPERM || errno == EACCES || errno == EISDIR);
     145          errno = 0;
     146          ASSERT (func (BASE "d/.", BASE "c") == -1);
     147          ASSERT (errno == EPERM || errno == EACCES || errno == EISDIR
     148                  || errno == EINVAL);
     149          errno = 0;
     150          ASSERT (func (BASE "d/.//", BASE "c") == -1);
     151          ASSERT (errno == EPERM || errno == EACCES || errno == EISDIR
     152                  || errno == EINVAL);
     153        }
     154    }
     155    ASSERT (unlink (BASE "a") == 0);
     156    errno = 0;
     157    ASSERT (unlink (BASE "c") == -1);
     158    ASSERT (errno == ENOENT);
     159    ASSERT (rmdir (BASE "d") == 0);
     160  
     161    /* Test invalid use of symlink.  */
     162    if (symlink (BASE "a", BASE "link") != 0)
     163      {
     164        ASSERT (unlink (BASE "b") == 0);
     165        if (print)
     166          fputs ("skipping test: symlinks not supported on this file system\n",
     167                 stderr);
     168        return 77;
     169      }
     170    errno = 0;
     171    ASSERT (func (BASE "b", BASE "link/") == -1);
     172    ASSERT (errno == ENOTDIR || errno == ENOENT || errno == EEXIST
     173            || errno == EINVAL);
     174    errno = 0;
     175    ASSERT (func (BASE "b", BASE "link") == -1);
     176    ASSERT (errno == EEXIST);
     177    ASSERT (rename (BASE "b", BASE "a") == 0);
     178    errno = 0;
     179    ASSERT (func (BASE "link/", BASE "b") == -1);
     180    ASSERT (errno == ENOTDIR || errno == EEXIST || errno == EINVAL);
     181  
     182    /* Clean up.  */
     183    ASSERT (unlink (BASE "a") == 0);
     184    ASSERT (unlink (BASE "link") == 0);
     185  
     186    return 0;
     187  }