Some improvements to the solaris xattr routines.
[rsync/rsync.git] / lib / sysxattrs.c
1 /*
2  * Extended attribute support for rsync.
3  *
4  * Copyright (C) 2004 Red Hat, Inc.
5  * Copyright (C) 2003-2008 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 #if defined HAVE_LINUX_XATTRS
28
29 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
30 {
31         return lgetxattr(path, name, value, size);
32 }
33
34 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
35 {
36         return fgetxattr(filedes, name, value, size);
37 }
38
39 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
40 {
41         return lsetxattr(path, name, value, size, 0);
42 }
43
44 int sys_lremovexattr(const char *path, const char *name)
45 {
46         return lremovexattr(path, name);
47 }
48
49 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
50 {
51         return llistxattr(path, list, size);
52 }
53
54 #elif HAVE_OSX_XATTRS
55
56 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
57 {
58         return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
59 }
60
61 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
62 {
63         return fgetxattr(filedes, name, value, size, 0, 0);
64 }
65
66 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
67 {
68         return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
69 }
70
71 int sys_lremovexattr(const char *path, const char *name)
72 {
73         return removexattr(path, name, XATTR_NOFOLLOW);
74 }
75
76 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
77 {
78         return listxattr(path, list, size, XATTR_NOFOLLOW);
79 }
80
81 #elif HAVE_FREEBSD_XATTRS
82
83 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
84 {
85         return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
86 }
87
88 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
89 {
90         return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
91 }
92
93 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
94 {
95         return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
96 }
97
98 int sys_lremovexattr(const char *path, const char *name)
99 {
100         return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
101 }
102
103 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
104 {
105         unsigned char keylen;
106         ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
107
108         if (len <= 0 || (size_t)len > size)
109                 return len;
110
111         /* FreeBSD puts a single-byte length before each string, with no '\0'
112          * terminator.  We need to change this into a series of null-terminted
113          * strings.  Since the size is the same, we can simply transform the
114          * output in place. */
115         for (off = 0; off < len; off += keylen + 1) {
116                 keylen = ((unsigned char*)list)[off];
117                 if (off + keylen >= len) {
118                         /* Should be impossible, but kernel bugs happen! */
119                         errno = EINVAL;
120                         return -1;
121                 }
122                 memmove(list+off, list+off+1, keylen);
123                 list[off+keylen] = '\0';
124         }
125
126         return len;
127 }
128
129 #elif HAVE_SOLARIS_XATTRS
130
131 static ssize_t read_xattr(int attrfd, void *buf, size_t buflen)
132 {
133         STRUCT_STAT sb;
134         ssize_t ret;
135
136         if (fstat(fd, &sb) < 0)
137                 ret = -1;
138         else if (sb.st_size > SSIZE_MAX) {
139                 errno = ERANGE;
140                 ret = -1;
141         } else if (size == 0)
142                 ret = sb.st_size;
143         else if (sb.st_size > size) {
144                 errno = ERANGE;
145                 ret = -1;
146         } else {
147                 size_t bufpos;
148                 for (bufpos = 0; bufpos < buflen; ) {
149                         ssize_t cnt = read(attrfd, buf + bufpos, buflen - bufpos);
150                         if (cnt <= 0) {
151                                 if (cnt < 0 && errno == EINTR)
152                                         continue;
153                                 bufpos = -1;
154                                 break;
155                         }
156                         bufpos += cnt;
157                 }
158                 ret = bufpos;
159         }
160
161         close(attrfd);
162
163         return ret;
164 }
165
166 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
167 {
168         int attrfd;
169
170         if ((attrfd = attropen(path, name, O_RDONLY)) < 0) {
171                 errno = ENOATTR;
172                 return -1;
173         }
174
175         return read_xattr(attrfd, value, size);
176 }
177
178 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
179 {
180         int attrfd;
181
182         if ((attrfd = openat(filedes, name, O_RDONLY|O_XATTR, 0)) < 0) {
183                 errno = ENOATTR;
184                 return -1;
185         }
186
187         return read_xattr(attrfd, value, size);
188 }
189
190 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
191 {
192         int attrfd;
193         size_t bufpos;
194         mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
195
196         if ((attrfd = attropen(path, name, O_CREAT|O_WRONLY|O_NOFOLLOW, mode)) < 0)
197                 return -1;
198
199         for (bufpos = 0; bufpos < size; ) {
200                 ssize_t cnt = write(attrfd, value+bufpos, size);
201                 if (cnt <= 0) {
202                         if (cnt < 0 && errno == EINTR)
203                                 continue;
204                         bufpos = -1;
205                         break;
206                 }
207                 bufpos += cnt;
208         }
209
210         close(attrfd);
211
212         return bufpos > 0 ? 0 : -1;
213 }
214
215 int sys_lremovexattr(const char *path, const char *name)
216 {
217         int attrdirfd;
218         int ret;
219
220         if ((attrdirfd = attropen(path, ".", O_RDWR)) < 0)
221                 return -1;
222
223         ret = unlinkat(attrdirfd, name, 0);
224
225         close(attrdirfd);
226
227         return ret;
228 }
229
230 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
231 {
232         int attrdirfd;
233         DIR *dirp;
234         struct dirent *dp;
235         ssize_t ret = 0;
236
237         if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0) {
238                 errno = ENOTSUP;
239                 return -1;
240         }
241
242         if ((dirp = fdopendir(attrdirfd)) == NULL) {
243                 close(attrdirfd);
244                 return -1;
245         }
246
247         while ((dp = readdir(dirp))) {
248                 int len = strlen(dp->d_name);
249
250                 if (dp->d_name[0] == '.' && (len == 1 || (len == 2 && dp->d_name[1] == '.')))
251                         continue;
252                 if (len == 11 && dp->d_name[0] == 'S' && strncmp(dp->d_name, "SUNWattr_r", 10) == 0
253                  && (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
254                         continue;
255
256                 if ((ret += len+1) > size) {
257                         ret = -1;
258                         errno = ERANGE;
259                         break;
260                 }
261                 memcpy(list, dp->d_name, len+1);
262                 list += len+1;
263         }
264
265         closedir(dirp);
266         close(attrdirfd);
267
268         return ret;
269 }
270
271 #else
272
273 #error You need to create xattr compatibility functions.
274
275 #endif
276
277 #endif /* SUPPORT_XATTRS */