1 /* Work around unlink bugs. 2 3 Copyright (C) 2009-2021 Free Software Foundation, Inc. 4 5 This file is free software: you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1 of the 8 License, or (at your option) any later version. 9 10 This file 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 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 17 18 #include <config.h> 19 20 #include <unistd.h> 21 22 #include <errno.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <sys/stat.h> 26 27 #include "filename.h" 28 29 #undef unlink 30 #if defined _WIN32 && !defined __CYGWIN__ 31 # define unlink _unlink 32 #endif 33 34 /* Remove file NAME. 35 Return 0 if successful, -1 if not. */ 36 37 int 38 rpl_unlink (char const *name) /* */ 39 { 40 /* Work around Solaris 9 bug where unlink("file/") succeeds. */ 41 size_t len = strlen (name); 42 int result = 0; 43 if (len && ISSLASH (name[len - 1])) 44 { 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. */ 64 struct stat st; 65 result = lstat (name, &st); 66 if (result == 0 || errno == EOVERFLOW) 67 { 68 /* Trailing NUL will overwrite the trailing slash. */ 69 char *short_name = malloc (len); 70 if (!short_name) 71 return -1; 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))) 76 { 77 free (short_name); 78 errno = EPERM; 79 return -1; 80 } 81 free (short_name); 82 result = 0; 83 } 84 } 85 if (!result) 86 { 87 #if UNLINK_PARENT_BUG 88 if (len >= 2 && name[len - 1] == '.' && name[len - 2] == '.' 89 && (len == 2 || ISSLASH (name[len - 3]))) 90 { 91 errno = EISDIR; /* could also use EPERM */ 92 return -1; 93 } 94 #endif 95 result = unlink (name); 96 } 97 return result; 98 }