]> git.cworth.org Git - tar/blob - gnu/unlink.c
Imported Upstream version 1.24
[tar] / gnu / unlink.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* Work around unlink bugs.
4
5    Copyright (C) 2009, 2010 Free Software Foundation, Inc.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20 #include <config.h>
21
22 #include <unistd.h>
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28
29 #undef unlink
30
31 /* Remove file NAME.
32    Return 0 if successful, -1 if not.  */
33
34 int
35 rpl_unlink (char const *name)
36 {
37   /* Work around Solaris 9 bug where unlink("file/") succeeds.  */
38   size_t len = strlen (name);
39   int result = 0;
40   if (len && ISSLASH (name[len - 1]))
41     {
42       /* We can't unlink(2) something if it doesn't exist.  If it does
43          exist, then it resolved to a directory, due to the trailing
44          slash, and POSIX requires that the unlink attempt to remove
45          that directory (which would leave the symlink dangling).
46          Unfortunately, Solaris 9 is one of the platforms where the
47          root user can unlink directories, and we don't want to
48          cripple this behavior on real directories, even if it is
49          seldom needed (at any rate, it's nicer to let coreutils'
50          unlink(1) give the correct errno for non-root users).  But we
51          don't know whether name was an actual directory, or a symlink
52          to a directory; and due to the bug of ignoring trailing
53          slash, Solaris 9 would end up successfully unlinking the
54          symlink instead of the directory.  Technically, we could use
55          realpath to find the canonical directory name to attempt
56          deletion on.  But that is a lot of work for a corner case; so
57          we instead just use an lstat on the shortened name, and
58          reject symlinks with trailing slashes.  The root user of
59          unlink(1) will just have to live with the rule that they
60          can't delete a directory via a symlink.  */
61       struct stat st;
62       result = lstat (name, &st);
63       if (result == 0)
64         {
65           /* Trailing NUL will overwrite the trailing slash.  */
66           char *short_name = malloc (len);
67           if (!short_name)
68             {
69               errno = EPERM;
70               return -1;
71             }
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         }
83     }
84   if (!result)
85     {
86 #if UNLINK_PARENT_BUG
87       if (len >= 2 && name[len - 1] == '.' && name[len - 2] == '.'
88           && (len == 2 || ISSLASH (name[len - 3])))
89         {
90           errno = EISDIR; /* could also use EPERM */
91           return -1;
92         }
93 #endif
94       result = unlink (name);
95     }
96   return result;
97 }