diff options
Diffstat (limited to 'gnulib-tests/test-link.h')
-rw-r--r-- | gnulib-tests/test-link.h | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/gnulib-tests/test-link.h b/gnulib-tests/test-link.h new file mode 100644 index 0000000..f6aaea8 --- /dev/null +++ b/gnulib-tests/test-link.h @@ -0,0 +1,187 @@ +/* Test of link() function. + Copyright (C) 2009-2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* This file is designed to test both link(a,b) and + linkat(AT_FDCWD,a,AT_FDCWD,b,0). FUNC is the function to test. + Assumes that BASE and ASSERT are already defined, and that + appropriate headers are already included. If PRINT, warn before + skipping tests with status 77. This test does not try to create + hard links to symlinks, but does test other aspects of symlink. */ + +static int +test_link (int (*func) (char const *, char const *), bool print) +{ + int fd; + int ret; + + /* Create first file. */ + fd = open (BASE "a", O_CREAT | O_EXCL | O_WRONLY, 0600); + ASSERT (0 <= fd); + ASSERT (write (fd, "hello", 5) == 5); + ASSERT (close (fd) == 0); + + /* Not all file systems support link. Mingw doesn't have reliable + st_nlink on hard links, but our implementation does fail with + EPERM on poor file systems, and we can detect the inferior stat() + via st_ino. Cygwin 1.5.x copies rather than links files on those + file systems, but there, st_nlink and st_ino are reliable. */ + ret = func (BASE "a", BASE "b"); + if (!ret) + { + struct stat st; + ASSERT (stat (BASE "b", &st) == 0); + if (st.st_ino && st.st_nlink != 2) + { + ASSERT (unlink (BASE "b") == 0); + errno = EPERM; + ret = -1; + } + } + if (ret == -1) + { + /* If the device does not support hard links, errno is + EPERM on Linux, + EOPNOTSUPP on FreeBSD, + EACCES on Android within Termux. */ + switch (errno) + { + case EPERM: + case EOPNOTSUPP: + #if defined __ANDROID__ + case EACCES: + #endif + if (print) + fputs ("skipping test: " + "hard links not supported on this file system\n", + stderr); + ASSERT (unlink (BASE "a") == 0); + return 77; + default: + perror ("link"); + return 1; + } + } + ASSERT (ret == 0); + + /* Now, for some behavior tests. Modify the contents of 'b', and + ensure that 'a' can see it, both while 'b' exists and after. */ + fd = open (BASE "b", O_APPEND | O_WRONLY); + ASSERT (0 <= fd); + ASSERT (write (fd, "world", 5) == 5); + ASSERT (close (fd) == 0); + { + char buf[11] = { 0 }; + fd = open (BASE "a", O_RDONLY); + ASSERT (0 <= fd); + ASSERT (read (fd, buf, 10) == 10); + ASSERT (strcmp (buf, "helloworld") == 0); + ASSERT (close (fd) == 0); + ASSERT (unlink (BASE "b") == 0); + fd = open (BASE "a", O_RDONLY); + ASSERT (0 <= fd); + ASSERT (read (fd, buf, 10) == 10); + ASSERT (strcmp (buf, "helloworld") == 0); + ASSERT (close (fd) == 0); + } + + /* Test for various error conditions. */ + ASSERT (mkdir (BASE "d", 0700) == 0); + errno = 0; + ASSERT (func (BASE "a", ".") == -1); + ASSERT (errno == EEXIST || errno == EINVAL); + errno = 0; + ASSERT (func (BASE "a", BASE "a") == -1); + ASSERT (errno == EEXIST); + ASSERT (func (BASE "a", BASE "b") == 0); + errno = 0; + ASSERT (func (BASE "a", BASE "b") == -1); + ASSERT (errno == EEXIST); + errno = 0; + ASSERT (func (BASE "a", BASE "d") == -1); + ASSERT (errno == EEXIST); + errno = 0; + ASSERT (func (BASE "c", BASE "e") == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (func (BASE "a", BASE "c/.") == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (func (BASE "a/", BASE "c") == -1); + ASSERT (errno == ENOTDIR || errno == EINVAL); + errno = 0; + ASSERT (func (BASE "a", BASE "c/") == -1); + ASSERT (errno == ENOTDIR || errno == ENOENT || errno == EINVAL); + + /* Most platforms reject hard links to directories, and even on + those that do permit it, most users can't create them. We assume + that if this test is run as root and we managed to create a hard + link, then unlink better be able to clean it up. */ + { + int result; + errno = 0; + result = func (BASE "d", BASE "c"); + if (result == 0) + { + /* Probably root on Solaris. */ + ASSERT (unlink (BASE "c") == 0); + } + else + { + /* Most everyone else. */ + ASSERT (errno == EPERM || errno == EACCES || errno == EISDIR); + errno = 0; + ASSERT (func (BASE "d/.", BASE "c") == -1); + ASSERT (errno == EPERM || errno == EACCES || errno == EISDIR + || errno == EINVAL); + errno = 0; + ASSERT (func (BASE "d/.//", BASE "c") == -1); + ASSERT (errno == EPERM || errno == EACCES || errno == EISDIR + || errno == EINVAL); + } + } + ASSERT (unlink (BASE "a") == 0); + errno = 0; + ASSERT (unlink (BASE "c") == -1); + ASSERT (errno == ENOENT); + ASSERT (rmdir (BASE "d") == 0); + + /* Test invalid use of symlink. */ + if (symlink (BASE "a", BASE "link") != 0) + { + ASSERT (unlink (BASE "b") == 0); + if (print) + fputs ("skipping test: symlinks not supported on this file system\n", + stderr); + return 77; + } + errno = 0; + ASSERT (func (BASE "b", BASE "link/") == -1); + ASSERT (errno == ENOTDIR || errno == ENOENT || errno == EEXIST + || errno == EINVAL); + errno = 0; + ASSERT (func (BASE "b", BASE "link") == -1); + ASSERT (errno == EEXIST); + ASSERT (rename (BASE "b", BASE "a") == 0); + errno = 0; + ASSERT (func (BASE "link/", BASE "b") == -1); + ASSERT (errno == ENOTDIR || errno == EEXIST || errno == EINVAL); + + /* Clean up. */ + ASSERT (unlink (BASE "a") == 0); + ASSERT (unlink (BASE "link") == 0); + + return 0; +} |