1#include "extattr_os.h" 2 3#ifdef EXTATTR_BSD 4 5#include <errno.h> 6 7#include "EXTERN.h" 8#include "perl.h" 9#include "XSUB.h" 10 11#include "flags.h" 12 13static int 14valid_namespace (struct hv *flags, int *pattrnamespace) 15{ 16 const size_t NAMESPACE_KEYLEN = strlen(NAMESPACE_KEY); 17 SV **psv_ns; 18 char *ns = NULL; 19 int ok = 1; /* Default is valid */ 20 int attrnamespace = EXTATTR_NAMESPACE_USER; 21 22 if (flags && (psv_ns = hv_fetch(flags, NAMESPACE_KEY, NAMESPACE_KEYLEN, 0))) 23 { 24 /* 25 * Undefined => default. Otherwise "user" and "system" are valid. 26 */ 27 if (SvOK(*psv_ns)) 28 { 29 char *s; 30 STRLEN len = 0; 31 32 s = SvPV(*psv_ns, len); 33 34 if (len) 35 { 36 if (memcmp(NAMESPACE_USER, s, len) == 0) 37 attrnamespace = EXTATTR_NAMESPACE_USER; 38 else if (memcmp(NAMESPACE_SYSTEM, s, len) == 0) 39 attrnamespace = EXTATTR_NAMESPACE_SYSTEM; 40 else 41 ok = 0; 42 } 43 else 44 { 45 ok = 0; 46 } 47 } 48 } 49 50 if (ok) 51 *pattrnamespace = attrnamespace; 52 53 return ok; 54} 55 56/* Helper to convert number of bytes written into success/failure code. */ 57static inline int 58bsd_extattr_set_succeeded (const int expected, const int actual) 59{ 60 int ret = 0; 61 62 if (actual == -1) { 63 ret = -errno; 64 } else if (actual != expected) { 65 /* Pretend there's not enough space for the data. */ 66 ret = -ENOBUFS; 67 } 68 69 return ret; 70} 71 72int 73bsd_setxattr (const char *path, 74 const char *attrname, 75 const char *attrvalue, 76 const size_t slen, 77 struct hv *flags) 78{ 79 int attrnamespace = -1; 80 int ret = 0; 81 82 if (!valid_namespace(flags, &attrnamespace)) 83 { 84 ret = -EOPNOTSUPP; 85 } 86 87 if (ret == 0) 88 { 89 File_ExtAttr_setflags_t setflags = File_ExtAttr_flags2setflags(flags); 90 switch (setflags) 91 { 92 case SET_CREATEIFNEEDED: 93 case SET_REPLACE: 94 /* Default behaviour */ 95 break; 96 97 case SET_CREATE: 98 /* 99 * This needs to be emulated, since the default *BSD calls 100 * don't provide a way of failing if the attribute exists. 101 * This emulation is inherently racy. 102 */ 103 { 104 ssize_t sz = extattr_get_file(path, attrnamespace, attrname, NULL, 0); 105 if (sz >= 0) 106 { 107 /* Attribute already exists => fail. */ 108 ret = -EEXIST; 109 } 110 } 111 break; 112 } 113 } 114 115 if (ret == 0) 116 { 117 ret = extattr_set_file(path, attrnamespace, attrname, attrvalue, slen); 118 ret = bsd_extattr_set_succeeded(slen, ret); 119 } 120 121 return ret; 122} 123 124int 125bsd_fsetxattr (const int fd, 126 const char *attrname, 127 const char *attrvalue, 128 const size_t slen, 129 struct hv *flags) 130{ 131 int attrnamespace = -1; 132 int ret = 0; 133 134 if (!valid_namespace(flags, &attrnamespace)) 135 { 136 ret = -EOPNOTSUPP; 137 } 138 139 if (ret == 0) 140 { 141 File_ExtAttr_setflags_t setflags = File_ExtAttr_flags2setflags(flags); 142 switch (setflags) 143 { 144 case SET_CREATEIFNEEDED: 145 case SET_REPLACE: 146 /* Default behaviour */ 147 break; 148 149 case SET_CREATE: 150 /* 151 * This needs to be emulated, since the default *BSD calls 152 * don't provide a way of failing if the attribute exists. 153 * This emulation is inherently racy. 154 */ 155 { 156 ssize_t sz = extattr_get_fd(fd, attrnamespace, attrname, NULL, 0); 157 if (sz >= 0) 158 { 159 /* Attribute already exists => fail. */ 160 ret = -EEXIST; 161 } 162 } 163 break; 164 } 165 } 166 167 if (ret == 0) 168 { 169 ret = extattr_set_fd(fd, attrnamespace, attrname, attrvalue, slen); 170 ret = bsd_extattr_set_succeeded(slen, ret); 171 } 172 173 return ret; 174} 175 176int 177bsd_getxattr (const char *path, 178 const char *attrname, 179 void *attrvalue, 180 const size_t slen, 181 struct hv *flags) 182{ 183 int attrnamespace = -1; 184 int ret = 0; 185 186 if (!valid_namespace(flags, &attrnamespace)) 187 { 188 ret = -EOPNOTSUPP; 189 } 190 191 if (ret == 0) { 192 ret = extattr_get_file(path, attrnamespace, attrname, attrvalue, slen); 193 if (ret < 0) { 194 ret = -errno; 195 } 196 } 197 198 return ret; 199} 200 201int 202bsd_fgetxattr (const int fd, 203 const char *attrname, 204 void *attrvalue, 205 const size_t slen, 206 struct hv *flags) 207{ 208 int attrnamespace = -1; 209 int ret = 0; 210 211 if (!valid_namespace(flags, &attrnamespace)) 212 { 213 ret = -EOPNOTSUPP; 214 } 215 216 if (ret == 0) { 217 ret = extattr_get_fd(fd, attrnamespace, attrname, attrvalue, slen); 218 if (ret < 0) { 219 ret = -errno; 220 } 221 } 222 223 return ret; 224} 225 226int 227bsd_removexattr (const char *path, 228 const char *attrname, 229 struct hv *flags) 230{ 231 int attrnamespace = -1; 232 int ret = 0; 233 234 if (!valid_namespace(flags, &attrnamespace)) 235 { 236 ret = -EOPNOTSUPP; 237 } 238 239 if (ret == 0) { 240 ret = extattr_delete_file(path, attrnamespace, attrname); 241 if (ret < 0) { 242 ret = -errno; 243 } 244 } 245 246 return ret; 247} 248 249int 250bsd_fremovexattr (const int fd, 251 const char *attrname, 252 struct hv *flags) 253{ 254 int attrnamespace = -1; 255 int ret = 0; 256 257 if (!valid_namespace(flags, &attrnamespace)) 258 { 259 ret = -EOPNOTSUPP; 260 } 261 262 if (ret == 0) { 263 ret = extattr_delete_fd(fd, attrnamespace, attrname); 264 if (ret < 0) { 265 ret = -errno; 266 } 267 } 268 269 return ret; 270} 271 272/* Convert the BSD-style list to a nul-separated list. */ 273static void 274reformat_list (char *buf, const ssize_t len) 275{ 276 ssize_t pos = 0; 277 ssize_t attrlen; 278 279 while (pos < len) 280 { 281 attrlen = (unsigned char) buf[pos]; 282 memmove(buf + pos, buf + pos + 1, attrlen); 283 buf[pos + attrlen] = '\0'; 284 pos += attrlen + 1; 285 } 286} 287 288ssize_t 289bsd_listxattr (const char *path, 290 char *buf, 291 const size_t buflen, 292 struct hv *flags) 293{ 294 int attrnamespace = -1; 295 ssize_t ret = 0; 296 297 if (!valid_namespace(flags, &attrnamespace)) 298 { 299 ret = -EOPNOTSUPP; 300 } 301 302 if (ret == 0) 303 { 304 ret = extattr_list_file(path, 305 attrnamespace, 306 /* To get the length on *BSD, pass NULL here. */ 307 buflen ? buf : NULL, 308 buflen); 309 310 if (buflen && (ret > 0)) 311 reformat_list(buf, ret); 312 313 if (ret < 0) { 314 ret = -errno; 315 } 316 } 317 318 return ret; 319} 320 321ssize_t 322bsd_flistxattr (const int fd, 323 char *buf, 324 const size_t buflen, 325 struct hv *flags) 326{ 327 int attrnamespace = -1; 328 ssize_t ret = 0; 329 330 if (!valid_namespace(flags, &attrnamespace)) 331 { 332 ret = -EOPNOTSUPP; 333 } 334 335 if (ret == 0) 336 { 337 ret = extattr_list_fd(fd, 338 attrnamespace, 339 /* To get the length on *BSD, pass NULL here. */ 340 buflen ? buf : NULL, 341 buflen); 342 343 if (buflen && (ret > 0)) 344 reformat_list(buf, ret); 345 346 if (ret < 0) { 347 ret = -errno; 348 } 349 } 350 351 return ret; 352} 353 354static ssize_t 355listxattrns (char *buf, const size_t buflen, 356 const int iHasUser, const int iHasSystem) 357{ 358 size_t len = 0; 359 ssize_t ret = 0; 360 361 if (iHasUser) 362 len += sizeof(NAMESPACE_USER); 363 if (iHasSystem) 364 len += sizeof(NAMESPACE_SYSTEM); 365 366 if (buflen >= len) 367 { 368 char *p = buf; 369 370 if (iHasUser) 371 { 372 memcpy(p, NAMESPACE_USER, sizeof(NAMESPACE_USER)); 373 p += sizeof(NAMESPACE_USER); 374 } 375 if (iHasSystem) 376 { 377 memcpy(p, NAMESPACE_SYSTEM, sizeof(NAMESPACE_SYSTEM)); 378 p += sizeof(NAMESPACE_SYSTEM); 379 } 380 381 ret = len; 382 } 383 else if (buflen == 0) 384 { 385 ret = len; 386 } 387 else 388 { 389 ret = -ERANGE; 390 } 391 392 return ret; 393} 394 395ssize_t 396bsd_listxattrns (const char *path, 397 char *buf, 398 const size_t buflen, 399 struct hv *flags) 400{ 401 int iHasUser = 0; 402 int iHasSystem = 0; 403 ssize_t ret = 0; 404 405 ret = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0); 406 if (ret > 0) 407 iHasUser = 1; 408 409 if (ret >= 0) 410 { 411 ret = extattr_list_file(path, EXTATTR_NAMESPACE_SYSTEM, NULL, 0); 412 if (ret > 0) 413 iHasSystem = 1; 414 415 /* 416 * XXX: How do we cope with EPERM? Throw an exception. 417 * For now ignore it, although this could cause problems. 418 */ 419 if (ret == -1 && errno == EPERM) 420 ret = 0; 421 } 422 423 if (ret >= 0) 424 ret = listxattrns(buf, buflen, iHasUser, iHasSystem); 425 426 return ret; 427} 428 429ssize_t 430bsd_flistxattrns (const int fd, 431 char *buf, 432 const size_t buflen, 433 struct hv *flags) 434{ 435 int iHasUser = 0; 436 int iHasSystem = 0; 437 ssize_t ret; 438 439 ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, NULL, 0); 440 if (ret > 0) 441 iHasUser = 1; 442 443 if (ret >= 0) 444 { 445 ret = extattr_list_fd(fd, EXTATTR_NAMESPACE_SYSTEM, NULL, 0); 446 if (ret > 0) 447 iHasSystem = 1; 448 449 /* 450 * XXX: How do we cope with EPERM? Throw an exception. 451 * For now ignore it, although this could cause problems. 452 */ 453 if (ret == -1 && errno == EPERM) 454 ret = 0; 455 } 456 457 if (ret >= 0) 458 ret = listxattrns(buf, buflen, iHasUser, iHasSystem); 459 460 return ret; 461} 462 463#endif /* EXTATTR_BSD */ 464