Split off copy_acl function to separate file.
[gnulib.git] / lib / copy-acl.c
1 /* copy-acl.c - copy access control list from one file to another file
2
3    Copyright (C) 2002-2003, 2005-2008 Free Software Foundation, Inc.
4
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.
9
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.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18    Written by Paul Eggert and Andreas Gruenbacher.  */
19
20 #include <config.h>
21
22 #include "acl.h"
23
24 #include "acl-internal.h"
25
26 /* Copy access control lists from one file to another. If SOURCE_DESC is
27    a valid file descriptor, use file descriptor operations, else use
28    filename based operations on SRC_NAME. Likewise for DEST_DESC and
29    DST_NAME.
30    If access control lists are not available, fchmod the target file to
31    MODE.  Also sets the non-permission bits of the destination file
32    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
33    Return 0 if successful, otherwise output a diagnostic and return -1.  */
34
35 int
36 copy_acl (const char *src_name, int source_desc, const char *dst_name,
37           int dest_desc, mode_t mode)
38 {
39   int ret;
40
41 #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
42   /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
43
44   acl_t acl;
45   if (HAVE_ACL_GET_FD && source_desc != -1)
46     acl = acl_get_fd (source_desc);
47   else
48     acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
49   if (acl == NULL)
50     {
51       if (ACL_NOT_WELL_SUPPORTED (errno))
52         return set_acl (dst_name, dest_desc, mode);
53       else
54         {
55           error (0, errno, "%s", quote (src_name));
56           return -1;
57         }
58     }
59
60   if (HAVE_ACL_SET_FD && dest_desc != -1)
61     ret = acl_set_fd (dest_desc, acl);
62   else
63     ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
64   if (ret != 0)
65     {
66       int saved_errno = errno;
67
68       if (ACL_NOT_WELL_SUPPORTED (errno))
69         {
70           int n = acl_entries (acl);
71
72           acl_free (acl);
73           /* On most hosts an ACL is trivial if n == 3, and it cannot be
74              less than 3.  On IRIX 6.5 it is also trivial if n == -1.
75              For simplicity and safety, assume the ACL is trivial if n <= 3.
76              Also see file-has-acl.c for some of the other possibilities;
77              it's not clear whether that complexity is needed here.  */
78           if (n <= 3)
79             {
80               if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
81                 saved_errno = errno;
82               else
83                 return 0;
84             }
85           else
86             chmod_or_fchmod (dst_name, dest_desc, mode);
87         }
88       else
89         {
90           acl_free (acl);
91           chmod_or_fchmod (dst_name, dest_desc, mode);
92         }
93       error (0, saved_errno, _("preserving permissions for %s"),
94              quote (dst_name));
95       return -1;
96     }
97   else
98     acl_free (acl);
99
100   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
101     {
102       /* We did not call chmod so far, so the special bits have not yet
103          been set.  */
104
105       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
106         {
107           error (0, errno, _("preserving permissions for %s"),
108                  quote (dst_name));
109           return -1;
110         }
111     }
112
113   if (S_ISDIR (mode))
114     {
115       acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
116       if (acl == NULL)
117         {
118           error (0, errno, "%s", quote (src_name));
119           return -1;
120         }
121
122       if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
123         {
124           error (0, errno, _("preserving permissions for %s"),
125                  quote (dst_name));
126           acl_free (acl);
127           return -1;
128         }
129       else
130         acl_free (acl);
131     }
132   return 0;
133
134 #else
135
136 # if USE_ACL && defined ACL_NO_TRIVIAL
137   /* Solaris 10 NFSv4 ACLs.  */
138   acl_t *aclp = NULL;
139   ret = (source_desc < 0
140          ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp)
141          : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp));
142   if (ret != 0 && errno != ENOSYS)
143     {
144       error (0, errno, "%s", quote (src_name));
145       return ret;
146     }
147 # endif
148
149   ret = qset_acl (dst_name, dest_desc, mode);
150   if (ret != 0)
151     error (0, errno, _("preserving permissions for %s"), quote (dst_name));
152
153 # if USE_ACL && defined ACL_NO_TRIVIAL
154   if (ret == 0 && aclp)
155     {
156       ret = (dest_desc < 0
157              ? acl_set (dst_name, aclp)
158              : facl_set (dest_desc, aclp));
159       if (ret != 0)
160         error (0, errno, _("preserving permissions for %s"), quote (dst_name));
161       acl_free (aclp);
162     }
163 # endif
164
165   return ret;
166 #endif
167 }