Preparing for release of 3.2.4pre1
[rsync.git] / lib / sysxattrs.c
1 /*
2  * Extended attribute support for rsync.
3  *
4  * Copyright (C) 2004 Red Hat, Inc.
5  * Copyright (C) 2003-2020 Wayne Davison
6  * Written by Jay Fenlason.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, visit the http://fsf.org website.
20  */
21
22 #include "rsync.h"
23 #include "sysxattrs.h"
24
25 #ifdef SUPPORT_XATTRS
26
27 #ifdef HAVE_OSX_XATTRS
28 #define GETXATTR_FETCH_LIMIT (64*1024*1024)
29 #endif
30
31 #if defined HAVE_LINUX_XATTRS
32
33 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
34 {
35         return lgetxattr(path, name, value, size);
36 }
37
38 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
39 {
40         return fgetxattr(filedes, name, value, size);
41 }
42
43 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
44 {
45         return lsetxattr(path, name, value, size, 0);
46 }
47
48 int sys_lremovexattr(const char *path, const char *name)
49 {
50         return lremovexattr(path, name);
51 }
52
53 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
54 {
55         return llistxattr(path, list, size);
56 }
57
58 #elif HAVE_OSX_XATTRS
59
60 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
61 {
62         ssize_t len = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
63
64         /* If we're retrieving data, handle resource forks > 64MB specially */
65         if (value != NULL && len == GETXATTR_FETCH_LIMIT && (size_t)len < size) {
66                 /* getxattr will only return 64MB of data at a time, need to call again with a new offset */
67                 u_int32_t offset = len;
68                 size_t data_retrieved = len;
69                 while (data_retrieved < size) {
70                         len = getxattr(path, name, (char*)value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
71                         if (len <= 0)
72                                 break;
73                         data_retrieved += len;
74                         offset += (u_int32_t)len;
75                 }
76                 len = data_retrieved;
77         }
78
79         return len;
80 }
81
82 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
83 {
84         return fgetxattr(filedes, name, value, size, 0, 0);
85 }
86
87 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
88 {
89         return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
90 }
91
92 int sys_lremovexattr(const char *path, const char *name)
93 {
94         return removexattr(path, name, XATTR_NOFOLLOW);
95 }
96
97 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
98 {
99         return listxattr(path, list, size, XATTR_NOFOLLOW);
100 }
101
102 #elif HAVE_FREEBSD_XATTRS
103
104 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
105 {
106         return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
107 }
108
109 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
110 {
111         return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
112 }
113
114 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
115 {
116         return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
117 }
118
119 int sys_lremovexattr(const char *path, const char *name)
120 {
121         return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
122 }
123
124 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
125 {
126         unsigned char keylen;
127         ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
128
129         if (len <= 0 || (size_t)len > size)
130                 return len;
131
132         /* FreeBSD puts a single-byte length before each string, with no '\0'
133          * terminator.  We need to change this into a series of null-terminted
134          * strings.  Since the size is the same, we can simply transform the
135          * output in place. */
136         for (off = 0; off < len; off += keylen + 1) {
137                 keylen = ((unsigned char*)list)[off];
138                 if (off + keylen >= len) {
139                         /* Should be impossible, but kernel bugs happen! */
140                         errno = EINVAL;
141                         return -1;
142                 }
143                 memmove(list+off, list+off+1, keylen);
144                 list[off+keylen] = '\0';
145         }
146
147         return len;
148 }
149
150 #elif HAVE_SOLARIS_XATTRS
151
152 static ssize_t read_xattr(int attrfd, void *buf, size_t buflen)
153 {
154         STRUCT_STAT sb;
155         ssize_t ret;
156
157         if (fstat(attrfd, &sb) < 0)
158                 ret = -1;
159         else if (sb.st_size > SSIZE_MAX) {
160                 errno = ERANGE;
161                 ret = -1;
162         } else if (buflen == 0)
163                 ret = sb.st_size;
164         else if (sb.st_size > buflen) {
165                 errno = ERANGE;
166                 ret = -1;
167         } else {
168                 size_t bufpos;
169                 for (bufpos = 0; bufpos < sb.st_size; ) {
170                         ssize_t cnt = read(attrfd, (char*)buf + bufpos, sb.st_size - bufpos);
171                         if (cnt <= 0) {
172                                 if (cnt < 0 && errno == EINTR)
173                                         continue;
174                                 bufpos = -1;
175                                 break;
176                         }
177                         bufpos += cnt;
178                 }
179                 ret = bufpos;
180         }
181
182         close(attrfd);
183
184         return ret;
185 }
186
187 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
188 {
189         int attrfd;
190
191         if ((attrfd = attropen(path, name, O_RDONLY)) < 0) {
192                 errno = ENOATTR;
193                 return -1;
194         }
195
196         return read_xattr(attrfd, value, size);
197 }
198
199 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
200 {
201         int attrfd;
202
203         if ((attrfd = openat(filedes, name, O_RDONLY|O_XATTR, 0)) < 0) {
204                 errno = ENOATTR;
205                 return -1;
206         }
207
208         return read_xattr(attrfd, value, size);
209 }
210
211 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
212 {
213         int attrfd;
214         size_t bufpos;
215         mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
216
217         if ((attrfd = attropen(path, name, O_CREAT|O_TRUNC|O_WRONLY, mode)) < 0)
218                 return -1;
219
220         for (bufpos = 0; bufpos < size; ) {
221                 ssize_t cnt = write(attrfd, (char*)value + bufpos, size);
222                 if (cnt <= 0) {
223                         if (cnt < 0 && errno == EINTR)
224                                 continue;
225                         bufpos = -1;
226                         break;
227                 }
228                 bufpos += cnt;
229         }
230
231         close(attrfd);
232
233         return bufpos > 0 ? 0 : -1;
234 }
235
236 int sys_lremovexattr(const char *path, const char *name)
237 {
238         int attrdirfd;
239         int ret;
240
241         if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0)
242                 return -1;
243
244         ret = unlinkat(attrdirfd, name, 0);
245
246         close(attrdirfd);
247
248         return ret;
249 }
250
251 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
252 {
253         int attrdirfd;
254         DIR *dirp;
255         struct dirent *dp;
256         ssize_t ret = 0;
257
258         if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0) {
259                 errno = ENOTSUP;
260                 return -1;
261         }
262
263         if ((dirp = fdopendir(attrdirfd)) == NULL) {
264                 close(attrdirfd);
265                 return -1;
266         }
267
268         while ((dp = readdir(dirp))) {
269                 int len = strlen(dp->d_name);
270
271                 if (dp->d_name[0] == '.' && (len == 1 || (len == 2 && dp->d_name[1] == '.')))
272                         continue;
273                 if (len == 11 && dp->d_name[0] == 'S' && strncmp(dp->d_name, "SUNWattr_r", 10) == 0
274                  && (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
275                         continue;
276
277                 ret += len + 1;
278                 if ((size_t)ret > size) {
279                         if (size == 0)
280                                 continue;
281                         ret = -1;
282                         errno = ERANGE;
283                         break;
284                 }
285                 memcpy(list, dp->d_name, len+1);
286                 list += len+1;
287         }
288
289         closedir(dirp);
290         close(attrdirfd);
291
292         return ret;
293 }
294
295 #else
296
297 #error You need to create xattr compatibility functions.
298
299 #endif
300
301 #endif /* SUPPORT_XATTRS */