1 /* Work around unlink bugs.
3 Copyright (C) 2009-2020 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
30 #if defined _WIN32 && !defined __CYGWIN__
31 # define unlink _unlink
35 Return 0 if successful, -1 if not. */
38 rpl_unlink (char const *name
)
40 /* Work around Solaris 9 bug where unlink("file/") succeeds. */
41 size_t len
= strlen (name
);
43 if (len
&& ISSLASH (name
[len
- 1]))
45 /* We can't unlink(2) something if it doesn't exist. If it does
46 exist, then it resolved to a directory, due to the trailing
47 slash, and POSIX requires that the unlink attempt to remove
48 that directory (which would leave the symlink dangling).
49 Unfortunately, Solaris 9 is one of the platforms where the
50 root user can unlink directories, and we don't want to
51 cripple this behavior on real directories, even if it is
52 seldom needed (at any rate, it's nicer to let coreutils'
53 unlink(1) give the correct errno for non-root users). But we
54 don't know whether name was an actual directory, or a symlink
55 to a directory; and due to the bug of ignoring trailing
56 slash, Solaris 9 would end up successfully unlinking the
57 symlink instead of the directory. Technically, we could use
58 realpath to find the canonical directory name to attempt
59 deletion on. But that is a lot of work for a corner case; so
60 we instead just use an lstat on the shortened name, and
61 reject symlinks with trailing slashes. The root user of
62 unlink(1) will just have to live with the rule that they
63 can't delete a directory via a symlink. */
65 result
= lstat (name
, &st
);
68 /* Trailing NUL will overwrite the trailing slash. */
69 char *short_name
= malloc (len
);
72 memcpy (short_name
, name
, len
);
73 while (len
&& ISSLASH (short_name
[len
- 1]))
74 short_name
[--len
] = '\0';
75 if (len
&& (lstat (short_name
, &st
) || S_ISLNK (st
.st_mode
)))
87 if (len
>= 2 && name
[len
- 1] == '.' && name
[len
- 2] == '.'
88 && (len
== 2 || ISSLASH (name
[len
- 3])))
90 errno
= EISDIR
; /* could also use EPERM */
94 result
= unlink (name
);