| 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(attrfd, &sb) < 0) |
| 137 | ret = -1; |
| 138 | else if (sb.st_size > SSIZE_MAX) { |
| 139 | errno = ERANGE; |
| 140 | ret = -1; |
| 141 | } else if (buflen == 0) |
| 142 | ret = sb.st_size; |
| 143 | else if (sb.st_size > buflen) { |
| 144 | errno = ERANGE; |
| 145 | ret = -1; |
| 146 | } else { |
| 147 | size_t bufpos; |
| 148 | for (bufpos = 0; bufpos < sb.st_size; ) { |
| 149 | ssize_t cnt = read(attrfd, buf + bufpos, sb.st_size - 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_TRUNC|O_WRONLY, 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_RDONLY)) < 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 | if (size == 0) |
| 258 | continue; |
| 259 | ret = -1; |
| 260 | errno = ERANGE; |
| 261 | break; |
| 262 | } |
| 263 | memcpy(list, dp->d_name, len+1); |
| 264 | list += len+1; |
| 265 | } |
| 266 | |
| 267 | closedir(dirp); |
| 268 | close(attrdirfd); |
| 269 | |
| 270 | return ret; |
| 271 | } |
| 272 | |
| 273 | #else |
| 274 | |
| 275 | #error You need to create xattr compatibility functions. |
| 276 | |
| 277 | #endif |
| 278 | |
| 279 | #endif /* SUPPORT_XATTRS */ |