1 /* Copy POSIX 1003.1e draft 17 (abandoned) ACLs between files. */
2
3 /* Copyright (C) 2002 Andreas Gruenbacher <agruen@suse.de>, SuSE Linux AG.
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (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 GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #if defined (HAVE_CONFIG_H)
21 #include "config.h"
22 #endif
23 #if defined(HAVE_LIBACL_LIBACL_H)
24 # include "libacl.h"
25 #endif
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #if defined(HAVE_SYS_ACL_H)
34 #include <sys/acl.h>
35 #endif
36
37 #if defined(HAVE_ACL_LIBACL_H)
38 #include <acl/libacl.h>
39 #endif
40
41 #define ERROR_CONTEXT_MACROS
42 #ifdef HAVE_ATTR_ERROR_CONTEXT_H
43 #include <attr/error_context.h>
44 #else
45 #include "error_context.h"
46 #endif
47
48 #if !defined(ENOTSUP)
49 # define ENOTSUP (-1)
50 #endif
51
52 #if !defined(HAVE_ACL_FREE)
53 static int
54 acl_free(void *obj_p)
55 {
56 free (obj_p);
57 return 0;
58 }
59 #endif
60
61 #if !defined(HAVE_ACL_ENTRIES)
62 static int
63 acl_entries(acl_t acl)
64 {
65 # if defined(HAVE_ACL_GET_ENTRY)
66 /* POSIX 1003.1e draft 17 (abandoned) compatible version. */
67 acl_entry_t entry;
68 int entries = 0;
69
70 int entries = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
71 if (entries > 0) {
72 while (acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) > 0)
73 entries++;
74 }
75 return entries;
76 # else
77 return -1;
78 # endif
79 }
80 #endif
81
82 #if !defined(HAVE_ACL_FROM_MODE) && defined(HAVE_ACL_FROM_TEXT)
83 # define HAVE_ACL_FROM_MODE
84 static acl_t
85 acl_from_mode(mode_t mode)
86 {
87 char acl_text[] = "u::---,g::---,o::---";
88 acl_t acl;
89
90 if (mode & S_IRUSR) acl_text[ 3] = 'r';
91 if (mode & S_IWUSR) acl_text[ 4] = 'w';
92 if (mode & S_IXUSR) acl_text[ 5] = 'x';
93 if (mode & S_IRGRP) acl_text[10] = 'r';
94 if (mode & S_IWGRP) acl_text[11] = 'w';
95 if (mode & S_IXGRP) acl_text[12] = 'x';
96 if (mode & S_IROTH) acl_text[17] = 'r';
97 if (mode & S_IWOTH) acl_text[18] = 'w';
98 if (mode & S_IXOTH) acl_text[19] = 'x';
99
100 return acl_from_text (acl_text);
101 }
102 #endif
103
104 /* Set the access control list of path to the permissions defined by mode. */
105 static int
106 set_acl (char const *path, mode_t mode, struct error_context *ctx)
107 {
108 int ret = 0;
109 #if defined(HAVE_ACL_FROM_MODE) && defined(HAVE_ACL_SET_FILE)
110 /* POSIX 1003.1e draft 17 (abandoned) specific version. */
111 acl_t acl = acl_from_mode (mode);
112 if (!acl) {
113 error (ctx, "");
114 return -1;
115 }
116
117 if (acl_set_file (path, ACL_TYPE_ACCESS, acl) != 0) {
118 ret = -1;
119 if (errno == ENOTSUP || errno == ENOSYS) {
120 (void) acl_free (acl);
121 goto chmod_only;
122 } else {
123 const char *qpath = quote (ctx, path);
124 error (ctx, _("setting permissions for %s"), qpath);
125 quote_free (ctx, qpath);
126 }
127 }
128 (void) acl_free (acl);
129 if (ret == 0 && S_ISDIR (mode)) {
130 # if defined(HAVE_ACL_DELETE_DEF_FILE)
131 ret = acl_delete_def_file (path);
132 # else
133 acl = acl_init (0);
134 ret = acl_set_file (path, ACL_TYPE_DEFAULT, acl);
135 (void) acl_free (acl);
136 # endif
137 if (ret != 0) {
138 const char *qpath = quote (ctx, path);
139 error (ctx, _( "setting permissions for %s"), qpath);
140 quote_free (ctx, qpath);
141 }
142 }
143 return ret;
144 #endif
145
146 chmod_only:
147 ret = chmod (path, mode);
148 if (ret != 0) {
149 const char *qpath = quote (ctx, path);
150 error (ctx, _("setting permissions for %s"), qpath);
151 quote_free (ctx, qpath);
152 }
153 return ret;
154 }
155
156 /* Copy the permissions of src_path to dst_path. This includes the
157 file mode permission bits and ACLs. File ownership is not copied.
158 */
159 int
160 perm_copy_file (const char *src_path, const char *dst_path,
161 struct error_context *ctx)
162 {
163 #if defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE)
164 acl_t acl;
165 #endif
166 struct stat st;
167 int ret = 0;
168
169 ret = stat(src_path, &st);
170 if (ret != 0) {
171 const char *qpath = quote (ctx, src_path);
172 error (ctx, "%s", qpath);
173 quote_free (ctx, qpath);
174 return -1;
175 }
176 #if defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE)
177 /* POSIX 1003.1e draft 17 (abandoned) specific version. */
178 acl = acl_get_file (src_path, ACL_TYPE_ACCESS);
179 if (acl == NULL) {
180 ret = -1;
181 if (errno == ENOSYS || errno == ENOTSUP)
182 ret = set_acl (dst_path, st.st_mode, ctx);
183 else {
184 const char *qpath = quote (ctx, src_path);
185 error (ctx, "%s", qpath);
186 quote_free (ctx, qpath);
187 }
188 return ret;
189 }
190
191 if (acl_set_file (dst_path, ACL_TYPE_ACCESS, acl) != 0) {
192 int saved_errno = errno;
193 __apply_mask_to_mode(&st.st_mode, acl);
194 ret = chmod (dst_path, st.st_mode);
195 if ((errno != ENOSYS && errno != ENOTSUP) ||
196 acl_entries (acl) != 3) {
197 const char *qpath = quote (ctx, dst_path);
198 errno = saved_errno;
199 error (ctx, _("preserving permissions for %s"), qpath);
200 quote_free (ctx, qpath);
201 ret = -1;
202 }
203 }
204 (void) acl_free (acl);
205
206 if (ret == 0 && S_ISDIR (st.st_mode)) {
207 acl = acl_get_file (src_path, ACL_TYPE_DEFAULT);
208 if (acl == NULL) {
209 const char *qpath = quote (ctx, src_path);
210 error (ctx, "%s", qpath);
211 quote_free (ctx, qpath);
212 return -1;
213 }
214 # if defined(HAVE_ACL_DELETE_DEF_FILE)
215 if (acl_entries(acl) == 0)
216 ret = acl_delete_def_file(dst_path);
217 else
218 ret = acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl);
219 # else
220 ret = acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl);
221 # endif
222 if (ret != 0) {
223 const char *qpath = quote (ctx, dst_path);
224 error (ctx, _("preserving permissions for %s"), qpath);
225 quote_free (ctx, qpath);
226 }
227 (void) acl_free(acl);
228 }
229 return ret;
230 #else
231 /* POSIX.1 version. */
232 ret = chmod (dst_path, st.st_mode);
233 if (ret != 0) {
234 const char *qpath = quote (ctx, dst_path);
235 error (ctx, _("setting permissions for %s"), qpath);
236 quote_free (ctx, qpath);
237 }
238 return ret;
239 #endif
240 }
241