| 1727 | Return false if this is not necessarily true (though it might be true). |
| 1728 | Keep it simple, and do not inspect the file system. */ |
| 1729 | ATTRIBUTE_PURE_114833 |
| 1730 | static bool |
| 1731 | same_parent_dirs(char const *a, char const *b) |
| 1732 | { |
| 1733 | for (; *a == *b; a++, b++) |
| 1734 | if (!*a) |
| 1735 | return true; |
| 1736 | return ! (strchr(a, '/') || strchr(b, '/')); |
| 1737 | } |
| 1738 | |
| 1739 | static void |
| 1740 | dolink(char const *target, char const *linkname, bool staysymlink) |
| 1741 | { |
| 1742 | bool linkdirs_made = false; |
| 1743 | int link_errno; |
| 1744 | char *tempname = NULL; |
| 1745 | char const *outname = linkname; |
| 1746 | int targetissym = -2, linknameissym = -2; |
| 1747 | |
| 1748 | if (strcmp(target, "-") == 0) { |
| 1749 | if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR) |
| 1750 | return; |
| 1751 | else { |
| 1752 | char const *e = strerror(errno); |
| 1753 | fprintf(stderr, _("%s: Can't remove %s%s%s: %s\n"), |
| 1754 | progname, diagdir(linkname), diagslash(linkname), linkname, |
| 1755 | e); |
| 1756 | exit(EXIT_FAILURE); |
| 1757 | } |
| 1758 | } |
| 1759 | |
| 1760 | for (;; check_for_signal()) { |
| 1761 | if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW) |
| 1762 | == 0) { |
| 1763 | link_errno = 0; |
| 1764 | break; |
| 1765 | } |
| 1766 | link_errno = errno; |
| 1767 | /* Linux 2.6.16 and 2.6.17 mishandle AT_SYMLINK_FOLLOW. */ |
| 1768 | if (link_errno == EINVAL) |
| 1769 | link_errno = ENOTSUP; |
| 1770 | #if HAVE_LINK |
| 1771 | /* If linkat is not supported, fall back on link(A, B). |
| 1772 | However, skip this if A is a relative symlink |
| 1773 | and A and B might not have the same parent directory. |
| 1774 | On some platforms link(A, B) does not follow a symlink A, |
| 1775 | and if A is relative it might misbehave elsewhere. */ |
| 1776 | if (link_errno == ENOTSUP |
| 1777 | && (same_parent_dirs(target, outname) |
| 1778 | || 0 <= itssymlink(target, &targetissym))) { |
| 1779 | if (link(target, outname) == 0) { |
| 1780 | link_errno = 0; |
| 1781 | break; |
| 1782 | } |
| 1783 | link_errno = errno; |
| 1784 | /* When hard links are not supported, some MS-Windows file system |
| 1785 | drivers fail with EINVAL, contrary to the intent of |
| 1786 | MS-FSA 42.0 (2025) section 2.1.5.15.7. */ |
no test coverage detected