Fix a warning about a %d not getting an int (on some platforms).
[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 read_xattr(int attrfd, void *buf, size_t buflen)
132 {
133         size_t bufpos;
134
135         for (bufpos = 0; bufpos < buflen; )  {
136                 size_t r = read(attrfd, buf + bufpos, buflen - bufpos);
137                 if (r <= 0) {
138                         if (r < 0)
139                                 bufpos = -1;
140                         break;
141                 }
142                 bufpos += r;
143         }
144
145         close(attrfd);
146
147         return bufpos;
148 }
149
150 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
151 {
152         int attrfd;
153
154         if ((attrfd = attropen(path, name, O_RDONLY)) < 0) {
155                 errno = ENOATTR;
156                 return -1;
157         }
158
159         return read_xattr(attrfd, value, size);
160 }
161
162 ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
163 {
164         int attrfd;
165
166         if ((attrfd = openat(filedes,name,O_RDONLY)) < 0) {
167                 errno = ENOATTR;
168                 return -1;
169         }
170
171         return read_xattr(attrfd, value, size);
172
173 }
174
175 int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
176 {
177         int attrfd;
178         size_t bufpos;
179         mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
180
181         if ((attrfd = attropen(path, name, AT_SYMLINK_NOFOLLOW|O_CREAT|O_RDWR, mode)) < 0)
182                 return -1;
183
184         for (bufpos = 0; bufpos < size; ) {
185                 size_t w = write(attrfd, value+bufpos, size);
186                 if (w <= 0) {
187                         bufpos = -1;
188                         break;
189                 }
190                 bufpos += w;
191
192         }
193
194         close(attrfd);
195
196         return bufpos;
197 }
198
199 int sys_lremovexattr(const char *path, const char *name)
200 {
201         int attrdirfd;
202         int ret;
203
204         if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0)
205                 return -1;
206
207         ret = unlinkat(attrdirfd, name, 0);
208
209         close(attrdirfd);
210
211         return ret;
212 }
213
214 ssize_t sys_llistxattr(const char *path, char *list, size_t size)
215 {
216         int attrdirfd;
217         DIR *dirp;
218         struct dirent *dp;
219         int len = 0;
220
221         if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0) {
222                 errno = ENOTSUP;
223                 return -1;
224         }
225
226         if ((dirp = fdopendir(attrdirfd)) == NULL) {
227                 close(attrdirfd);
228                 return -1;
229         }
230
231         while ((dp = readdir(dirp))) {
232                 int namelen = strlen(dp->d_name);
233
234                 if (dp->d_name[0] == '.' && (namelen == 1 || (namelen == 2 && dp->d_name[1] == '.')))
235                         continue;
236                 if (namelen == 11 && dp->d_name[0] == 'S' && strncmp(dp->d_name, "SUNWattr_r", 10) == 0
237                  && (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
238                         continue;
239
240                 memcpy(list, dp->d_name, namelen+1);
241                 list += namelen;
242                 len += namelen;
243         }
244
245         closedir(dirp);
246         close(attrdirfd);
247
248         return len;
249 }
250
251 #else
252
253 #error You need to create xattr compatibility functions.
254
255 #endif
256
257 #endif /* SUPPORT_XATTRS */