1 /*
2 Copyright (C) 2002 Andreas Gruenbacher <agruen@suse.de>, SuSE Linux AG.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this manual. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /* Copy extended attributes between files. */
19
20 #if defined (HAVE_CONFIG_H)
21 #include "config.h"
22 #endif
23
24 #include <sys/types.h>
25 # include <sys/xattr.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29
30 #if defined(HAVE_ALLOCA_H)
31 # include <alloca.h>
32 #endif
33
34 #if defined(HAVE_ATTR_LIBATTR_H)
35 # include "attr/libattr.h"
36 #endif
37
38 #define ERROR_CONTEXT_MACROS
39 #include "error_context.h"
40 #include "nls.h"
41
42 #if !defined(ENOTSUP)
43 # define ENOTSUP (-1)
44 #endif
45
46 #if defined(HAVE_ALLOCA)
47 # define my_alloc(size) alloca (size)
48 # define my_free(ptr) do { } while(0)
49 #else
50 # define my_alloc(size) malloc (size)
51 # define my_free(ptr) free (ptr)
52 #endif
53
54 /* Copy extended attributes from src_path to dst_path. If the file
55 has an extended Access ACL (system.posix_acl_access) and that is
56 copied successfully, the file mode permission bits are copied as
57 a side effect. This may not always the case, so the file mode
58 and/or ownership must be copied separately. */
59 int
60 attr_copy_file(const char *src_path, const char *dst_path,
61 int (*check) (const char *, struct error_context *),
62 struct error_context *ctx)
63 {
64 #if defined(HAVE_LISTXATTR) && defined(HAVE_GETXATTR) && defined(HAVE_SETXATTR)
65 int ret = 0;
66 ssize_t size;
67 char *names = NULL, *end_names, *name, *value = NULL;
68 unsigned int setxattr_ENOTSUP = 0;
69
70 /* ignore acls by default */
71 if (check == NULL)
72 check = attr_copy_check_permissions;
73
74 size = llistxattr (src_path, NULL, 0);
75 if (size < 0) {
76 if (errno != ENOSYS && errno != ENOTSUP) {
77 const char *qpath = quote (ctx, src_path);
78 error (ctx, _("listing attributes of %s"), qpath);
79 quote_free (ctx, qpath);
80 ret = -1;
81 }
82 goto getout;
83 }
84 names = (char *) my_alloc (size+1);
85 if (names == NULL) {
86 error (ctx, "");
87 ret = -1;
88 goto getout;
89 }
90 size = llistxattr (src_path, names, size);
91 if (size < 0) {
92 const char *qpath = quote (ctx, src_path);
93 error (ctx, _("listing attributes of %s"), qpath);
94 quote_free (ctx, qpath);
95 ret = -1;
96 goto getout;
97 } else {
98 names[size] = '\0';
99 end_names = names + size;
100 }
101
102 for (name = names; name != end_names; name = strchr(name, '\0') + 1) {
103 void *old_value;
104
105 /* check if this attribute shall be preserved */
106 if (!*name || !check(name, ctx))
107 continue;
108
109 size = lgetxattr (src_path, name, NULL, 0);
110 if (size < 0) {
111 const char *qpath = quote (ctx, src_path);
112 const char *qname = quote (ctx, name);
113 error (ctx, _("getting attribute %s of %s"),
114 qname, qpath);
115 quote_free (ctx, qname);
116 quote_free (ctx, qpath);
117 ret = -1;
118 continue;
119 }
120 value = (char *) realloc (old_value = value, size);
121 if (size != 0 && value == NULL) {
122 free(old_value);
123 error (ctx, "");
124 ret = -1;
125 }
126 size = lgetxattr (src_path, name, value, size);
127 if (size < 0) {
128 const char *qpath = quote (ctx, src_path);
129 const char *qname = quote (ctx, name);
130 error (ctx, _("getting attribute %s of %s"),
131 qname, qpath);
132 quote_free (ctx, qname);
133 quote_free (ctx, qpath);
134 ret = -1;
135 continue;
136 }
137 if (lsetxattr (dst_path, name, value, size, 0) != 0) {
138 if (errno == ENOTSUP)
139 setxattr_ENOTSUP++;
140 else {
141 const char *qpath = quote (ctx, dst_path);
142 if (errno == ENOSYS) {
143 error (ctx, _("setting attributes for "
144 "%s"), qpath);
145 ret = -1;
146 /* no hope of getting any further */
147 break;
148 } else {
149 const char *qname = quote (ctx, name);
150 error (ctx, _("setting attribute %s for "
151 "%s"), qname, qpath);
152 quote_free (ctx, qname);
153 ret = -1;
154 }
155 quote_free (ctx, qpath);
156 }
157 }
158 }
159 if (setxattr_ENOTSUP) {
160 const char *qpath = quote (ctx, dst_path);
161 errno = ENOTSUP;
162 error (ctx, _("setting attributes for %s"), qpath);
163 ret = -1;
164 quote_free (ctx, qpath);
165 }
166 getout:
167 free (value);
168 my_free (names);
169 return ret;
170 #else
171 return 0;
172 #endif
173 }
174