1/* 2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com> 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13*/ 14 15#ifdef HAVE_CONFIG_H 16#include "config.h" 17#endif /* HAVE_CONFIG_H */ 18 19#include <unistd.h> 20#include <stdint.h> 21#include <errno.h> 22#include <stdlib.h> 23#include <string.h> 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <fcntl.h> 27#include <dirent.h> 28 29#include <atalk/adouble.h> 30#include <atalk/ea.h> 31#include <atalk/afp.h> 32#include <atalk/logger.h> 33#include <atalk/volume.h> 34#include <atalk/vfs.h> 35#include <atalk/util.h> 36#include <atalk/unix.h> 37 38/* 39 * Store Extended Attributes inside .AppleDouble folders as follows: 40 * 41 * filename "fileWithEAs" with EAs "testEA1" and "testEA2" 42 * 43 * - create header with with the format struct adouble_ea_ondisk, the file is written to 44 * ".AppleDouble/fileWithEAs::EA" 45 * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2" 46 */ 47 48/* 49 * Build mode for EA header from file mode 50 */ 51static inline mode_t ea_header_mode(mode_t mode) 52{ 53 /* Same as ad_hf_mode(mode) */ 54 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); 55 /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/ 56 mode |= S_IRUSR | S_IWUSR; 57 return mode; 58} 59 60/* 61 * Build mode for EA file from file mode 62 */ 63static inline mode_t ea_mode(mode_t mode) 64{ 65 /* Same as ad_hf_mode(mode) */ 66 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); 67 return mode; 68} 69 70/* 71 Taken form afpd/desktop.c 72*/ 73static char *mtoupath(const struct vol *vol, const char *mpath) 74{ 75 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */ 76 const char *m; 77 char *u; 78 size_t inplen; 79 size_t outlen; 80 uint16_t flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON; 81 82 if (!mpath) 83 return NULL; 84 85 if ( *mpath == '\0' ) { 86 return( "." ); 87 } 88 89 m = mpath; 90 u = upath; 91 92 inplen = strlen(m); 93 outlen = MAXPATHLEN; 94 95 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC, 96 vol->v_volcharset, 97 vol->v_maccharset, 98 m, inplen, u, outlen, &flags)) ) { 99 return NULL; 100 } 101 102 return( upath ); 103} 104 105 106/* 107 * Function: unpack_header 108 * 109 * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea 110 * 111 * Arguments: 112 * 113 * ea (rw) handle to struct ea 114 * 115 * Returns: 0 on success, -1 on error 116 * 117 * Effects: 118 * 119 * Verifies magic and version. 120 */ 121static int unpack_header(struct ea * __restrict ea) 122{ 123 int ret = 0; 124 unsigned int count = 0; 125 uint32_t uint32; 126 char *buf; 127 128 /* Check magic and version */ 129 buf = ea->ea_data; 130 if (*(uint32_t *)buf != htonl(EA_MAGIC)) { 131 LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf); 132 ret = -1; 133 goto exit; 134 } 135 buf += 4; 136 if (*(uint16_t *)buf != htons(EA_VERSION)) { 137 LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf); 138 ret = -1; 139 goto exit; 140 } 141 buf += 2; 142 143 /* Get EA count */ 144 ea->ea_count = ntohs(*(uint16_t *)buf); 145 LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count); 146 buf += 2; 147 148 if (ea->ea_count == 0) 149 return 0; 150 151 /* Allocate storage for the ea_entries array */ 152 ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count); 153 if ( ! ea->ea_entries) { 154 LOG(log_error, logtype_afpd, "unpack_header: OOM"); 155 ret = -1; 156 goto exit; 157 } 158 159 buf = ea->ea_data + EA_HEADER_SIZE; 160 while (count < ea->ea_count) { 161 memcpy(&uint32, buf, 4); /* EA size */ 162 buf += 4; 163 (*(ea->ea_entries))[count].ea_size = ntohl(uint32); 164 (*(ea->ea_entries))[count].ea_name = strdup(buf); 165 if (! (*(ea->ea_entries))[count].ea_name) { 166 LOG(log_error, logtype_afpd, "unpack_header: OOM"); 167 ret = -1; 168 goto exit; 169 } 170 (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name); 171 buf += (*(ea->ea_entries))[count].ea_namelen + 1; 172 173 LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count, 174 (*(ea->ea_entries))[count].ea_name, 175 (*(ea->ea_entries))[count].ea_size, 176 (*(ea->ea_entries))[count].ea_namelen); 177 178 count++; 179 } 180 181exit: 182 return ret; 183} 184 185/* 186 * Function: pack_header 187 * 188 * Purpose: pack everything from struct ea into buffer at ea->ea_data 189 * 190 * Arguments: 191 * 192 * ea (rw) handle to struct ea 193 * 194 * Returns: 0 on success, -1 on error 195 * 196 * Effects: 197 * 198 * adjust ea->ea_count in case an ea entry deletetion is detected 199 */ 200static int pack_header(struct ea * __restrict ea) 201{ 202 unsigned int count = 0, eacount = 0; 203 uint16_t uint16; 204 uint32_t uint32; 205 size_t bufsize = EA_HEADER_SIZE; 206 207 char *buf = ea->ea_data + EA_HEADER_SIZE; 208 209 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u", 210 ea->filename, ea->ea_count, ea->ea_size); 211 212 if (ea->ea_count == 0) 213 /* nothing to do, magic, version and count are still valid in buffer */ 214 return 0; 215 216 while(count < ea->ea_count) { /* the names */ 217 /* Check if its a deleted entry */ 218 if ( ! ((*ea->ea_entries)[count].ea_name)) { 219 count++; 220 continue; 221 } 222 223 bufsize += (*(ea->ea_entries))[count].ea_namelen + 1; 224 count++; 225 eacount++; 226 } 227 228 bufsize += (eacount * 4); /* header + ea_size for each EA */ 229 if (bufsize > ea->ea_size) { 230 /* we must realloc */ 231 if ( ! (buf = realloc(ea->ea_data, bufsize)) ) { 232 LOG(log_error, logtype_afpd, "pack_header: OOM"); 233 return -1; 234 } 235 ea->ea_data = buf; 236 } 237 ea->ea_size = bufsize; 238 239 /* copy count */ 240 uint16 = htons(eacount); 241 memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2); 242 243 count = 0; 244 buf = ea->ea_data + EA_HEADER_SIZE; 245 while (count < ea->ea_count) { 246 /* Check if its a deleted entry */ 247 if ( ! ((*ea->ea_entries)[count].ea_name)) { 248 count++; 249 continue; 250 } 251 252 /* First: EA size */ 253 uint32 = htonl((*(ea->ea_entries))[count].ea_size); 254 memcpy(buf, &uint32, 4); 255 buf += 4; 256 257 /* Second: EA name as C-string */ 258 strcpy(buf, (*(ea->ea_entries))[count].ea_name); 259 buf += (*(ea->ea_entries))[count].ea_namelen + 1; 260 261 LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count, 262 (*(ea->ea_entries))[count].ea_name, 263 (*(ea->ea_entries))[count].ea_size, 264 (*(ea->ea_entries))[count].ea_namelen); 265 266 count++; 267 } 268 269 ea->ea_count = eacount; 270 271 LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u", 272 ea->filename, ea->ea_count, ea->ea_size); 273 274 return 0; 275} 276 277/* 278 * Function: ea_addentry 279 * 280 * Purpose: add one EA into ea->ea_entries[] 281 * 282 * Arguments: 283 * 284 * ea (rw) pointer to struct ea 285 * attruname (r) name of EA 286 * attrsize (r) size of ea 287 * bitmap (r) bitmap from FP func 288 * 289 * Returns: new number of EA entries, -1 on error 290 * 291 * Effects: 292 * 293 * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating. 294 * Otherwise realloc and put entry at the end. Increments ea->ea_count. 295 */ 296static int ea_addentry(struct ea * __restrict ea, 297 const char * __restrict attruname, 298 size_t attrsize, 299 int bitmap) 300{ 301 int ea_existed = 0; 302 unsigned int count = 0; 303 void *tmprealloc; 304 305 /* First check if an EA of the requested name already exist */ 306 if (ea->ea_count > 0) { 307 while (count < ea->ea_count) { 308 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { 309 ea_existed = 1; 310 LOG(log_debug, logtype_afpd, "ea_addentry('%s', bitmap:0x%x): exists", attruname, bitmap); 311 if (bitmap & kXAttrCreate) 312 /* its like O_CREAT|O_EXCL -> fail */ 313 return -1; 314 (*(ea->ea_entries))[count].ea_size = attrsize; 315 return 0; 316 } 317 count++; 318 } 319 } 320 321 if ((bitmap & kXAttrReplace) && ! ea_existed) 322 /* replace was requested, but EA didn't exist */ 323 return -1; 324 325 if (ea->ea_count == 0) { 326 ea->ea_entries = malloc(sizeof(struct ea_entry)); 327 if ( ! ea->ea_entries) { 328 LOG(log_error, logtype_afpd, "ea_addentry: OOM"); 329 return -1; 330 } 331 } else if (! ea_existed) { 332 tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1)); 333 if ( ! tmprealloc) { 334 LOG(log_error, logtype_afpd, "ea_addentry: OOM"); 335 return -1; 336 } 337 ea->ea_entries = tmprealloc; 338 } 339 340 /* We've grown the array, now store the entry */ 341 (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize; 342 (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname); 343 if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) { 344 LOG(log_error, logtype_afpd, "ea_addentry: OOM"); 345 goto error; 346 } 347 (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname); 348 349 ea->ea_count++; 350 return ea->ea_count; 351 352error: 353 if (ea->ea_count == 0 && ea->ea_entries) { 354 /* We just allocated storage but had an error somewhere -> free storage*/ 355 free(ea->ea_entries); 356 ea->ea_entries = NULL; 357 } 358 ea->ea_count = 0; 359 return -1; 360} 361 362/* 363 * Function: create_ea_header 364 * 365 * Purpose: create EA header file, only called from ea_open 366 * 367 * Arguments: 368 * 369 * uname (r) filename for which we have to create a header 370 * ea (rw) ea handle with already allocated storage pointed to 371 * by ea->ea_data 372 * 373 * Returns: fd of open header file on success, -1 on error, errno semantics: 374 * EEXIST: open with O_CREAT | O_EXCL failed 375 * 376 * Effects: 377 * 378 * Creates EA header file and initialize ea->ea_data buffer. 379 * Possibe race condition with other afpd processes: 380 * we were called because header file didn't exist in eg. ea_open. We then 381 * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic. 382 * What do we do then? Someone else is in the process of creating the header too, but 383 * it might not have finished it. That means we cant just open, read and use it! 384 * We therefor currently just break with an error. 385 * On return the header file is still r/w locked. 386 */ 387static int create_ea_header(const char * __restrict uname, 388 struct ea * __restrict ea) 389{ 390 int fd = -1, err = 0; 391 char *ptr; 392 393 if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) { 394 LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname); 395 return -1; 396 } 397 398 /* lock it */ 399 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) { 400 LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname); 401 err = -1; 402 goto exit; 403 } 404 405 /* Now init it */ 406 ptr = ea->ea_data; 407 *(uint32_t *)ptr = htonl(EA_MAGIC); 408 ptr += EA_MAGIC_LEN; 409 *(uint16_t *)ptr = htons(EA_VERSION); 410 ptr += EA_VERSION_LEN; 411 *(uint16_t *)ptr = 0; /* count */ 412 413 ea->ea_size = EA_HEADER_SIZE; 414 ea->ea_inited = EA_INITED; 415 416exit: 417 if (err != 0) { 418 close(fd); 419 fd = -1; 420 } 421 return fd; 422} 423 424/* 425 * Function: write_ea 426 * 427 * Purpose: write an EA to disk 428 * 429 * Arguments: 430 * 431 * ea (r) struct ea handle 432 * attruname (r) EA name 433 * ibuf (r) buffer with EA content 434 * attrsize (r) size of EA 435 * 436 * Returns: 0 on success, -1 on error 437 * 438 * Effects: 439 * 440 * Creates/overwrites EA file. 441 * 442 */ 443static int write_ea(const struct ea * __restrict ea, 444 const char * __restrict attruname, 445 const char * __restrict ibuf, 446 size_t attrsize) 447{ 448 int fd = -1, ret = AFP_OK; 449 struct stat st; 450 char *eaname; 451 452 if ((eaname = ea_path(ea, attruname, 1)) == NULL) { 453 LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname); 454 return AFPERR_MISC; 455 } 456 457 LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname); 458 459 /* Check if it exists, remove if yes*/ 460 if ((stat(eaname, &st)) == 0) { 461 if ((unlink(eaname)) != 0) { 462 if (errno == EACCES) 463 return AFPERR_ACCESS; 464 else 465 return AFPERR_MISC; 466 } 467 } 468 469 if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) { 470 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname); 471 return -1; 472 } 473 474 /* lock it */ 475 if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) { 476 LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname); 477 ret = -1; 478 goto exit; 479 } 480 481 if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) { 482 LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno)); 483 ret = -1; 484 goto exit; 485 } 486 487exit: 488 if (fd != -1) 489 close(fd); /* and unlock */ 490 return ret; 491} 492 493/* 494 * Function: ea_delentry 495 * 496 * Purpose: delete one EA from ea->ea_entries[] 497 * 498 * Arguments: 499 * 500 * ea (rw) pointer to struct ea 501 * attruname (r) EA name 502 * 503 * Returns: new number of EA entries, -1 on error 504 * 505 * Effects: 506 * 507 * Remove entry from ea->ea_entries[]. Decrement ea->ea_count. 508 * Marks it as unused just by freeing name and setting it to NULL. 509 * ea_close and pack_buffer must honor this. 510 */ 511static int ea_delentry(struct ea * __restrict ea, const char * __restrict attruname) 512{ 513 int ret = 0; 514 unsigned int count = 0; 515 516 if (ea->ea_count == 0) { 517 LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion"); 518 return -1; 519 } 520 521 while (count < ea->ea_count) { 522 /* search matching EA */ 523 if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { 524 free((*ea->ea_entries)[count].ea_name); 525 (*ea->ea_entries)[count].ea_name = NULL; 526 527 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u", 528 attruname, count + 1, ea->ea_count); 529 530 break; 531 } 532 count++; 533 } 534 535 return ret; 536} 537 538/* 539 * Function: delete_ea_file 540 * 541 * Purpose: delete EA file from disk 542 * 543 * Arguments: 544 * 545 * ea (r) struct ea handle 546 * attruname (r) EA name 547 * 548 * Returns: 0 on success, -1 on error 549 */ 550static int delete_ea_file(const struct ea * __restrict ea, const char *eaname) 551{ 552 int ret = 0; 553 char *eafile; 554 struct stat st; 555 556 if ((eafile = ea_path(ea, eaname, 1)) == NULL) { 557 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname); 558 return -1; 559 } 560 561 /* Check if it exists, remove if yes*/ 562 if ((stat(eafile, &st)) == 0) { 563 if ((unlink(eafile)) != 0) { 564 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s", 565 eafile, strerror(errno)); 566 ret = -1; 567 } else 568 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile); 569 } 570 571 return ret; 572} 573 574/************************************************************************************* 575 * ea_path, ea_open and ea_close are only global so that dbd can call them 576 *************************************************************************************/ 577 578/* 579 * Function: ea_path 580 * 581 * Purpose: return name of ea header filename 582 * 583 * Arguments: 584 * 585 * ea (r) ea handle 586 * eaname (r) name of EA or NULL 587 * macname (r) if != 0 call mtoupath on eaname 588 * 589 * Returns: pointer to name in static buffer, NULL on error 590 * 591 * Effects: 592 * 593 * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme 594 * Files: "file" -> "file/.AppleDouble/file::EA" 595 * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA" 596 * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA" 597 */ 598char *ea_path(const struct ea * __restrict ea, const char * __restrict eaname, int macname) 599{ 600 char *adname; 601 static char pathbuf[MAXPATHLEN + 1]; 602 603 /* get name of a adouble file from uname */ 604 adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0); 605 /* copy it so we can work with it */ 606 strlcpy(pathbuf, adname, MAXPATHLEN + 1); 607 /* append "::EA" */ 608 strlcat(pathbuf, "::EA", MAXPATHLEN + 1); 609 610 if (eaname) { 611 strlcat(pathbuf, "::", MAXPATHLEN + 1); 612 if (macname) 613 if ((eaname = mtoupath(ea->vol, eaname)) == NULL) 614 return NULL; 615 strlcat(pathbuf, eaname, MAXPATHLEN + 1); 616 } 617 618 return pathbuf; 619} 620 621/* 622 * Function: ea_open 623 * 624 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE 625 * 626 * Arguments: 627 * 628 * vol (r) current volume 629 * uname (r) filename for which we have to open a header 630 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) 631 * EA_RDONLY: open read only 632 * EA_RDWR: open read/write 633 * Eiterh EA_RDONLY or EA_RDWR MUST be requested 634 * ea (w) pointer to a struct ea that we fill 635 * 636 * Returns: 0 on success 637 * -1 on misc error with errno = EFAULT 638 * -2 if no EA header exists with errno = ENOENT 639 * 640 * Effects: 641 * 642 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. 643 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. 644 * file is either read or write locked depending on the open flags. 645 * When you're done with struct ea you must call ea_close on it. 646 */ 647int ea_open(const struct vol * __restrict vol, 648 const char * __restrict uname, 649 eaflags_t eaflags, 650 struct ea * __restrict ea) 651{ 652 int ret = 0; 653 char *eaname; 654 struct stat st; 655 656 /* Enforce usage rules! */ 657 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) { 658 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname); 659 return -1; 660 } 661 662 /* Set it all to 0 */ 663 memset(ea, 0, sizeof(struct ea)); 664 665 ea->vol = vol; /* ea_close needs it */ 666 ea->ea_flags = eaflags; 667 ea->dirfd = -1; /* no *at (cf openat) semantics by default */ 668 669 /* Dont care for errors, eg when removing the file is already gone */ 670 if (!stat(uname, &st) && S_ISDIR(st.st_mode)) 671 ea->ea_flags |= EA_DIR; 672 673 if ( ! (ea->filename = strdup(uname))) { 674 LOG(log_error, logtype_afpd, "ea_open: OOM"); 675 return -1; 676 } 677 678 eaname = ea_path(ea, NULL, 0); 679 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname); 680 681 /* Check if it exists, if not create it if EA_CREATE is in eaflags */ 682 if ((stat(eaname, &st)) != 0) { 683 if (errno == ENOENT) { 684 685 /* It doesnt exist */ 686 687 if ( ! (eaflags & EA_CREATE)) { 688 /* creation was not requested, so return with error */ 689 ret = -2; 690 goto exit; 691 } 692 693 /* Now create a header file */ 694 695 /* malloc buffer for minimal on disk data */ 696 ea->ea_data = malloc(EA_HEADER_SIZE); 697 if (! ea->ea_data) { 698 LOG(log_error, logtype_afpd, "ea_open: OOM"); 699 ret = -1; 700 goto exit; 701 } 702 703 /* create it */ 704 ea->ea_fd = create_ea_header(eaname, ea); 705 if (ea->ea_fd == -1) { 706 ret = -1; 707 goto exit; 708 } 709 710 return 0; 711 712 } else {/* errno != ENOENT */ 713 ret = -1; 714 goto exit; 715 } 716 } 717 718 /* header file exists, so read and parse it */ 719 720 /* malloc buffer where we read disk file into */ 721 if (st.st_size < EA_HEADER_SIZE) { 722 LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname); 723 ret = -1; 724 goto exit; 725 } 726 ea->ea_size = st.st_size; 727 ea->ea_data = malloc(st.st_size); 728 if (! ea->ea_data) { 729 LOG(log_error, logtype_afpd, "ea_open: OOM"); 730 ret = -1; 731 goto exit; 732 } 733 734 /* Now lock, open and read header file from disk */ 735 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) { 736 LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno)); 737 ret = -1; 738 goto exit; 739 } 740 741 /* lock it */ 742 if (ea->ea_flags & EA_RDONLY) { 743 /* read lock */ 744 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) { 745 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname); 746 ret = -1; 747 goto exit; 748 } 749 } else { /* EA_RDWR */ 750 /* write lock */ 751 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) { 752 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname); 753 ret = -1; 754 goto exit; 755 } 756 } 757 758 /* read it */ 759 if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { 760 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname); 761 ret = -1; 762 goto exit; 763 } 764 765 if ((unpack_header(ea)) != 0) { 766 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname); 767 ret = -1; 768 goto exit; 769 } 770 771exit: 772 switch (ret) { 773 case 0: 774 ea->ea_inited = EA_INITED; 775 break; 776 case -1: 777 errno = EFAULT; /* force some errno distinguishable from ENOENT */ 778 /* fall through */ 779 case -2: 780 if (ea->ea_data) { 781 free(ea->ea_data); 782 ea->ea_data = NULL; 783 } 784 if (ea->ea_fd) { 785 close(ea->ea_fd); 786 ea->ea_fd = -1; 787 } 788 break; 789 } 790 791 return ret; 792} 793 794/* 795 * Function: ea_openat 796 * 797 * Purpose: openat like wrapper for ea_open, takes a additional file descriptor 798 * 799 * Arguments: 800 * 801 * vol (r) current volume 802 * sfd (r) openat like file descriptor 803 * uname (r) filename for which we have to open a header 804 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) 805 * EA_RDONLY: open read only 806 * EA_RDWR: open read/write 807 * Eiterh EA_RDONLY or EA_RDWR MUST be requested 808 * ea (w) pointer to a struct ea that we fill 809 * 810 * Returns: 0 on success 811 * -1 on misc error with errno = EFAULT 812 * -2 if no EA header exists with errno = ENOENT 813 * 814 * Effects: 815 * 816 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. 817 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. 818 * file is either read or write locked depending on the open flags. 819 * When you're done with struct ea you must call ea_close on it. 820 */ 821int ea_openat(const struct vol * __restrict vol, 822 int dirfd, 823 const char * __restrict uname, 824 eaflags_t eaflags, 825 struct ea * __restrict ea) 826{ 827 int ret = 0; 828 int cwdfd = -1; 829 830 if (dirfd != -1) { 831 if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { 832 ret = -1; 833 goto exit; 834 } 835 } 836 837 ret = ea_open(vol, uname, eaflags, ea); 838 ea->dirfd = dirfd; 839 840 if (dirfd != -1) { 841 if (fchdir(cwdfd) != 0) { 842 LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting"); 843 exit(EXITERR_SYS); 844 } 845 } 846 847 848exit: 849 if (cwdfd != -1) 850 close(cwdfd); 851 852 return ret; 853 854} 855 856/* 857 * Function: ea_close 858 * 859 * Purpose: flushes and closes an ea handle 860 * 861 * Arguments: 862 * 863 * ea (rw) pointer to ea handle 864 * 865 * Returns: 0 on success, -1 on error 866 * 867 * Effects: 868 * 869 * Flushes and then closes and frees all resouces held by ea handle. 870 * Pack data in ea into ea_data, then write ea_data to disk 871 */ 872int ea_close(struct ea * __restrict ea) 873{ 874 int ret = 0; 875 unsigned int count = 0; 876 char *eaname; 877 struct stat st; 878 879 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename); 880 881 if (ea->ea_inited != EA_INITED) { 882 LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename); 883 return 0; 884 } 885 886 /* pack header and write it to disk if it was opened EA_RDWR*/ 887 if (ea->ea_flags & EA_RDWR) { 888 if ((pack_header(ea)) != 0) { 889 LOG(log_error, logtype_afpd, "ea_close: pack header"); 890 ret = -1; 891 } else { 892 if (ea->ea_count == 0) { 893 /* Check if EA header exists and remove it */ 894 eaname = ea_path(ea, NULL, 0); 895 if ((lstatat(ea->dirfd, eaname, &st)) == 0) { 896 if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) { 897 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s", 898 eaname, strerror(errno)); 899 ret = -1; 900 } 901 else 902 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname); 903 } else { 904 /* stat error */ 905 if (errno != ENOENT) { 906 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s", 907 eaname, strerror(errno)); 908 ret = -1; 909 } 910 } 911 } else { /* ea->ea_count > 0 */ 912 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) { 913 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno)); 914 ret = -1; 915 goto exit; 916 } 917 918 if ((ftruncate(ea->ea_fd, 0)) == -1) { 919 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno)); 920 ret = -1; 921 goto exit; 922 } 923 924 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { 925 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno)); 926 ret = -1; 927 } 928 } 929 } 930 } 931 932exit: 933 /* free names */ 934 while(count < ea->ea_count) { 935 if ( (*ea->ea_entries)[count].ea_name ) { 936 free((*ea->ea_entries)[count].ea_name); 937 (*ea->ea_entries)[count].ea_name = NULL; 938 } 939 count++; 940 } 941 ea->ea_count = 0; 942 943 if (ea->filename) { 944 free(ea->filename); 945 ea->filename = NULL; 946 } 947 948 if (ea->ea_entries) { 949 free(ea->ea_entries); 950 ea->ea_entries = NULL; 951 } 952 953 if (ea->ea_data) { 954 free(ea->ea_data); 955 ea->ea_data = NULL; 956 } 957 if (ea->ea_fd != -1) { 958 close(ea->ea_fd); /* also releases the fcntl lock */ 959 ea->ea_fd = -1; 960 } 961 962 return 0; 963} 964 965 966 967/************************************************************************************ 968 * VFS funcs called from afp_ea* funcs 969 ************************************************************************************/ 970 971/* 972 * Function: get_easize 973 * 974 * Purpose: get size of an EA 975 * 976 * Arguments: 977 * 978 * vol (r) current volume 979 * rbuf (w) DSI reply buffer 980 * rbuflen (rw) current length of data in reply buffer 981 * uname (r) filename 982 * oflag (r) link and create flag 983 * attruname (r) name of attribute 984 * 985 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 986 * 987 * Effects: 988 * 989 * Copies EA size into rbuf in network order. Increments *rbuflen +4. 990 */ 991int get_easize(VFS_FUNC_ARGS_EA_GETSIZE) 992{ 993 int ret = AFPERR_MISC; 994 unsigned int count = 0; 995 uint32_t uint32; 996 struct ea ea; 997 998 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname); 999 1000 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { 1001 if (errno != ENOENT) 1002 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname); 1003 1004 memset(rbuf, 0, 4); 1005 *rbuflen += 4; 1006 return ret; 1007 } 1008 1009 while (count < ea.ea_count) { 1010 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { 1011 uint32 = htonl((*ea.ea_entries)[count].ea_size); 1012 memcpy(rbuf, &uint32, 4); 1013 *rbuflen += 4; 1014 ret = AFP_OK; 1015 1016 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u", 1017 attruname, (*ea.ea_entries)[count].ea_size); 1018 break; 1019 } 1020 count++; 1021 } 1022 1023 if ((ea_close(&ea)) != 0) { 1024 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname); 1025 return AFPERR_MISC; 1026 } 1027 1028 return ret; 1029} 1030 1031/* 1032 * Function: get_eacontent 1033 * 1034 * Purpose: copy EA into rbuf 1035 * 1036 * Arguments: 1037 * 1038 * vol (r) current volume 1039 * rbuf (w) DSI reply buffer 1040 * rbuflen (rw) current length of data in reply buffer 1041 * uname (r) filename 1042 * oflag (r) link and create flag 1043 * attruname (r) name of attribute 1044 * maxreply (r) maximum EA size as of current specs/real-life 1045 * 1046 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 1047 * 1048 * Effects: 1049 * 1050 * Copies EA into rbuf. Increments *rbuflen accordingly. 1051 */ 1052int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT) 1053{ 1054 int ret = AFPERR_MISC, fd = -1; 1055 unsigned int count = 0; 1056 uint32_t uint32; 1057 size_t toread; 1058 struct ea ea; 1059 char *eafile; 1060 1061 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname); 1062 1063 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { 1064 if (errno != ENOENT) 1065 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname); 1066 memset(rbuf, 0, 4); 1067 *rbuflen += 4; 1068 return ret; 1069 } 1070 1071 while (count < ea.ea_count) { 1072 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { 1073 if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) { 1074 ret = AFPERR_MISC; 1075 break; 1076 } 1077 1078 if ((fd = open(eafile, O_RDONLY)) == -1) { 1079 LOG(log_error, logtype_afpd, "get_eacontent('%s'): open error: %s", uname, strerror(errno)); 1080 ret = AFPERR_MISC; 1081 break; 1082 } 1083 1084 /* Check how much the client wants, give him what we think is right */ 1085 maxreply -= MAX_REPLY_EXTRA_BYTES; 1086 if (maxreply > MAX_EA_SIZE) 1087 maxreply = MAX_EA_SIZE; 1088 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size; 1089 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread); 1090 1091 /* Put length of EA data in reply buffer */ 1092 uint32 = htonl(toread); 1093 memcpy(rbuf, &uint32, 4); 1094 rbuf += 4; 1095 *rbuflen += 4; 1096 1097 if (read(fd, rbuf, toread) != (ssize_t)toread) { 1098 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname); 1099 close(fd); 1100 ret = AFPERR_MISC; 1101 break; 1102 } 1103 *rbuflen += toread; 1104 close(fd); 1105 1106 ret = AFP_OK; 1107 break; 1108 } 1109 count++; 1110 } 1111 1112 if ((ea_close(&ea)) != 0) { 1113 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname); 1114 return AFPERR_MISC; 1115 } 1116 1117 return ret; 1118 1119} 1120 1121/* 1122 * Function: list_eas 1123 * 1124 * Purpose: copy names of EAs into attrnamebuf 1125 * 1126 * Arguments: 1127 * 1128 * vol (r) current volume 1129 * attrnamebuf (w) store names a consecutive C strings here 1130 * buflen (rw) length of names in attrnamebuf 1131 * uname (r) filename 1132 * oflag (r) link and create flag 1133 * 1134 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 1135 * 1136 * Effects: 1137 * 1138 * Copies names of all EAs of uname as consecutive C strings into rbuf. 1139 * Increments *buflen accordingly. 1140 */ 1141int list_eas(VFS_FUNC_ARGS_EA_LIST) 1142{ 1143 unsigned int count = 0; 1144 int attrbuflen = *buflen, ret = AFP_OK, len; 1145 char *buf = attrnamebuf; 1146 struct ea ea; 1147 1148 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname); 1149 1150 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { 1151 if (errno != ENOENT) { 1152 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname); 1153 return AFPERR_MISC; 1154 } 1155 else 1156 return AFP_OK; 1157 } 1158 1159 while (count < ea.ea_count) { 1160 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */ 1161 if ( ( len = convert_string(vol->v_volcharset, 1162 CH_UTF8_MAC, 1163 (*ea.ea_entries)[count].ea_name, 1164 (*ea.ea_entries)[count].ea_namelen, 1165 buf + attrbuflen, 1166 255)) 1167 <= 0 ) { 1168 ret = AFPERR_MISC; 1169 goto exit; 1170 } 1171 if (len == 255) 1172 /* convert_string didn't 0-terminate */ 1173 attrnamebuf[attrbuflen + 255] = 0; 1174 1175 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s", 1176 uname, (*ea.ea_entries)[count].ea_name); 1177 1178 attrbuflen += len + 1; 1179 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) { 1180 /* Next EA name could overflow, so bail out with error. 1181 FIXME: evantually malloc/memcpy/realloc whatever. 1182 Is it worth it ? */ 1183 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname); 1184 ret = AFPERR_MISC; 1185 goto exit; 1186 } 1187 count++; 1188 } 1189 1190exit: 1191 *buflen = attrbuflen; 1192 1193 if ((ea_close(&ea)) != 0) { 1194 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname); 1195 return AFPERR_MISC; 1196 } 1197 1198 return ret; 1199} 1200 1201/* 1202 * Function: set_ea 1203 * 1204 * Purpose: set a Solaris native EA 1205 * 1206 * Arguments: 1207 * 1208 * vol (r) current volume 1209 * uname (r) filename 1210 * attruname (r) EA name 1211 * ibuf (r) buffer with EA content 1212 * attrsize (r) length EA in ibuf 1213 * oflag (r) link and create flag 1214 * 1215 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 1216 * 1217 * Effects: 1218 * 1219 * Copies names of all EAs of uname as consecutive C strings into rbuf. 1220 * Increments *rbuflen accordingly. 1221 */ 1222int set_ea(VFS_FUNC_ARGS_EA_SET) 1223{ 1224 int ret = AFP_OK; 1225 struct ea ea; 1226 1227 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname); 1228 1229 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) { 1230 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname); 1231 return AFPERR_MISC; 1232 } 1233 1234 if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) { 1235 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname); 1236 ret = AFPERR_MISC; 1237 goto exit; 1238 } 1239 1240 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) { 1241 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname); 1242 ret = AFPERR_MISC; 1243 goto exit; 1244 } 1245 1246exit: 1247 if ((ea_close(&ea)) != 0) { 1248 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname); 1249 ret = AFPERR_MISC; 1250 goto exit; 1251 } 1252 1253 return ret; 1254} 1255 1256/* 1257 * Function: remove_ea 1258 * 1259 * Purpose: remove a EA from a file 1260 * 1261 * Arguments: 1262 * 1263 * vol (r) current volume 1264 * uname (r) filename 1265 * attruname (r) EA name 1266 * oflag (r) link and create flag 1267 * 1268 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 1269 * 1270 * Effects: 1271 * 1272 * Removes EA attruname from file uname. 1273 */ 1274int remove_ea(VFS_FUNC_ARGS_EA_REMOVE) 1275{ 1276 int ret = AFP_OK; 1277 struct ea ea; 1278 1279 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname); 1280 1281 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) { 1282 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname); 1283 return AFPERR_MISC; 1284 } 1285 1286 if ((ea_delentry(&ea, attruname)) == -1) { 1287 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname); 1288 ret = AFPERR_MISC; 1289 goto exit; 1290 } 1291 1292 if ((delete_ea_file(&ea, attruname)) != 0) { 1293 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname); 1294 ret = AFPERR_MISC; 1295 goto exit; 1296 } 1297 1298exit: 1299 if ((ea_close(&ea)) != 0) { 1300 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname); 1301 ret = AFPERR_MISC; 1302 goto exit; 1303 } 1304 1305 return ret; 1306} 1307 1308/****************************************************************************************** 1309 * EA VFS funcs that deal with file/dir cp/mv/rm 1310 ******************************************************************************************/ 1311 1312int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE) 1313{ 1314 unsigned int count = 0; 1315 int ret = AFP_OK; 1316 int cwd = -1; 1317 struct ea ea; 1318 1319 LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file); 1320 1321 /* Open EA stuff */ 1322 if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) { 1323 if (errno == ENOENT) 1324 /* no EA files, nothing to do */ 1325 return AFP_OK; 1326 else { 1327 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file); 1328 return AFPERR_MISC; 1329 } 1330 } 1331 1332 if (dirfd != -1) { 1333 if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { 1334 ret = AFPERR_MISC; 1335 goto exit; 1336 } 1337 } 1338 1339 while (count < ea.ea_count) { 1340 if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) { 1341 ret = AFPERR_MISC; 1342 continue; 1343 } 1344 free((*ea.ea_entries)[count].ea_name); 1345 (*ea.ea_entries)[count].ea_name = NULL; 1346 count++; 1347 } 1348 1349 /* ea_close removes the EA header file for us because all names are NULL */ 1350 if ((ea_close(&ea)) != 0) { 1351 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file); 1352 ret = AFPERR_MISC; 1353 } 1354 1355 if (dirfd != -1 && fchdir(cwd) != 0) { 1356 LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!"); 1357 exit(EXITERR_SYS); 1358 } 1359 1360exit: 1361 if (cwd != -1) 1362 close(cwd); 1363 1364 return ret; 1365} 1366 1367int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE) 1368{ 1369 unsigned int count = 0; 1370 int ret = AFP_OK; 1371 size_t easize; 1372 char srceapath[ MAXPATHLEN + 1]; 1373 char *eapath; 1374 char *eaname; 1375 struct ea srcea; 1376 struct ea dstea; 1377 struct adouble ad; 1378 1379 LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst); 1380 1381 1382 /* Open EA stuff */ 1383 if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) { 1384 if (errno == ENOENT) 1385 /* no EA files, nothing to do */ 1386 return AFP_OK; 1387 else { 1388 LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src); 1389 return AFPERR_MISC; 1390 } 1391 } 1392 1393 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { 1394 if (errno == ENOENT) { 1395 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */ 1396 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 1397 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) { 1398 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst); 1399 ret = AFPERR_MISC; 1400 goto exit; 1401 } 1402 ad_close(&ad, ADFLAGS_HF); 1403 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { 1404 ret = AFPERR_MISC; 1405 goto exit; 1406 } 1407 } 1408 } 1409 1410 /* Loop through all EAs: */ 1411 while (count < srcea.ea_count) { 1412 /* Move EA */ 1413 eaname = (*srcea.ea_entries)[count].ea_name; 1414 easize = (*srcea.ea_entries)[count].ea_size; 1415 1416 /* Build src and dst paths for rename() */ 1417 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { 1418 ret = AFPERR_MISC; 1419 goto exit; 1420 } 1421 strcpy(srceapath, eapath); 1422 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { 1423 ret = AFPERR_MISC; 1424 goto exit; 1425 } 1426 1427 LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", 1428 src, dst, srceapath, eapath); 1429 1430 /* Add EA to dstea */ 1431 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) { 1432 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", 1433 src, dst, srceapath, eapath); 1434 ret = AFPERR_MISC; 1435 goto exit; 1436 } 1437 1438 /* Remove EA entry from srcea */ 1439 if ((ea_delentry(&srcea, eaname)) == -1) { 1440 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", 1441 src, dst, srceapath, eapath); 1442 ea_delentry(&dstea, eaname); 1443 ret = AFPERR_MISC; 1444 goto exit; 1445 } 1446 1447 /* Now rename the EA */ 1448 if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) { 1449 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", 1450 src, dst, srceapath, eapath); 1451 ret = AFPERR_MISC; 1452 goto exit; 1453 } 1454 1455 count++; 1456 } 1457 1458 1459exit: 1460 ea_close(&srcea); 1461 ea_close(&dstea); 1462 return ret; 1463} 1464 1465int ea_copyfile(VFS_FUNC_ARGS_COPYFILE) 1466{ 1467 unsigned int count = 0; 1468 int ret = AFP_OK; 1469 size_t easize; 1470 char srceapath[ MAXPATHLEN + 1]; 1471 char *eapath; 1472 char *eaname; 1473 struct ea srcea; 1474 struct ea dstea; 1475 struct adouble ad; 1476 1477 LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst); 1478 1479 /* Open EA stuff */ 1480 if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) { 1481 if (errno == ENOENT) 1482 /* no EA files, nothing to do */ 1483 return AFP_OK; 1484 else { 1485 LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src); 1486 return AFPERR_MISC; 1487 } 1488 } 1489 1490 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { 1491 if (errno == ENOENT) { 1492 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */ 1493 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 1494 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) { 1495 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst); 1496 ret = AFPERR_MISC; 1497 goto exit; 1498 } 1499 ad_close(&ad, ADFLAGS_HF); 1500 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { 1501 ret = AFPERR_MISC; 1502 goto exit; 1503 } 1504 } 1505 } 1506 1507 /* Loop through all EAs: */ 1508 while (count < srcea.ea_count) { 1509 /* Copy EA */ 1510 eaname = (*srcea.ea_entries)[count].ea_name; 1511 easize = (*srcea.ea_entries)[count].ea_size; 1512 1513 /* Build src and dst paths for copy_file() */ 1514 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { 1515 ret = AFPERR_MISC; 1516 goto exit; 1517 } 1518 strcpy(srceapath, eapath); 1519 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { 1520 ret = AFPERR_MISC; 1521 goto exit; 1522 } 1523 1524 LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", 1525 src, dst, srceapath, eapath); 1526 1527 /* Add EA to dstea */ 1528 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) { 1529 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error", 1530 src, dst, eaname); 1531 ret = AFPERR_MISC; 1532 goto exit; 1533 } 1534 1535 /* Now copy the EA */ 1536 if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) { 1537 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", 1538 src, dst, srceapath, eapath); 1539 ret = AFPERR_MISC; 1540 goto exit; 1541 } 1542 1543 count++; 1544 } 1545 1546exit: 1547 ea_close(&srcea); 1548 ea_close(&dstea); 1549 return ret; 1550} 1551 1552int ea_chown(VFS_FUNC_ARGS_CHOWN) 1553{ 1554 1555 unsigned int count = 0; 1556 int ret = AFP_OK; 1557 char *eaname; 1558 struct ea ea; 1559 1560 LOG(log_debug, logtype_afpd, "ea_chown('%s')", path); 1561 /* Open EA stuff */ 1562 if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) { 1563 if (errno == ENOENT) 1564 /* no EA files, nothing to do */ 1565 return AFP_OK; 1566 else { 1567 LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path); 1568 return AFPERR_MISC; 1569 } 1570 } 1571 1572 if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) { 1573 switch (errno) { 1574 case EPERM: 1575 case EACCES: 1576 ret = AFPERR_ACCESS; 1577 goto exit; 1578 default: 1579 ret = AFPERR_MISC; 1580 goto exit; 1581 } 1582 } 1583 1584 while (count < ea.ea_count) { 1585 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { 1586 ret = AFPERR_MISC; 1587 goto exit; 1588 } 1589 if ((lchown(eaname, uid, gid)) != 0) { 1590 switch (errno) { 1591 case EPERM: 1592 case EACCES: 1593 ret = AFPERR_ACCESS; 1594 goto exit; 1595 default: 1596 ret = AFPERR_MISC; 1597 goto exit; 1598 } 1599 continue; 1600 } 1601 1602 count++; 1603 } 1604 1605exit: 1606 if ((ea_close(&ea)) != 0) { 1607 LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path); 1608 return AFPERR_MISC; 1609 } 1610 1611 return ret; 1612} 1613 1614int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) 1615{ 1616 1617 unsigned int count = 0; 1618 int ret = AFP_OK; 1619 const char *eaname; 1620 struct ea ea; 1621 1622 LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name); 1623 /* Open EA stuff */ 1624 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { 1625 if (errno == ENOENT) 1626 /* no EA files, nothing to do */ 1627 return AFP_OK; 1628 else 1629 return AFPERR_MISC; 1630 } 1631 1632 /* Set mode on EA header file */ 1633 if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { 1634 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); 1635 switch (errno) { 1636 case EPERM: 1637 case EACCES: 1638 ret = AFPERR_ACCESS; 1639 goto exit; 1640 default: 1641 ret = AFPERR_MISC; 1642 goto exit; 1643 } 1644 } 1645 1646 /* Set mode on EA files */ 1647 while (count < ea.ea_count) { 1648 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { 1649 ret = AFPERR_MISC; 1650 goto exit; 1651 } 1652 if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) { 1653 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno)); 1654 switch (errno) { 1655 case EPERM: 1656 case EACCES: 1657 ret = AFPERR_ACCESS; 1658 goto exit; 1659 default: 1660 ret = AFPERR_MISC; 1661 goto exit; 1662 } 1663 continue; 1664 } 1665 1666 count++; 1667 } 1668 1669exit: 1670 if ((ea_close(&ea)) != 0) { 1671 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name); 1672 return AFPERR_MISC; 1673 } 1674 1675 return ret; 1676} 1677 1678int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) 1679{ 1680 1681 int ret = AFP_OK; 1682 unsigned int count = 0; 1683 uid_t uid; 1684 const char *eaname; 1685 const char *eaname_safe = NULL; 1686 struct ea ea; 1687 1688 LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name); 1689 /* .AppleDouble already might be inaccesible, so we must run as id 0 */ 1690 uid = geteuid(); 1691 if (seteuid(0)) { 1692 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno)); 1693 return AFPERR_MISC; 1694 } 1695 1696 /* Open EA stuff */ 1697 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { 1698 /* ENOENT --> no EA files, nothing to do */ 1699 if (errno != ENOENT) 1700 ret = AFPERR_MISC; 1701 if (seteuid(uid) < 0) { 1702 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); 1703 exit(EXITERR_SYS); 1704 } 1705 return ret; 1706 } 1707 1708 /* Set mode on EA header */ 1709 if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { 1710 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); 1711 switch (errno) { 1712 case EPERM: 1713 case EACCES: 1714 ret = AFPERR_ACCESS; 1715 goto exit; 1716 default: 1717 ret = AFPERR_MISC; 1718 goto exit; 1719 } 1720 } 1721 1722 /* Set mode on EA files */ 1723 while (count < ea.ea_count) { 1724 eaname = (*ea.ea_entries)[count].ea_name; 1725 /* 1726 * Be careful with EA names from the EA header! 1727 * Eg NFS users might have access to them, can inject paths using ../ or /..... 1728 * FIXME: 1729 * Until the EA code escapes / in EA name requests from the client, these therefor wont work. 1730 */ 1731 if ((eaname_safe = strrchr(eaname, '/'))) { 1732 LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname); 1733 eaname = eaname_safe; 1734 } 1735 if ((eaname = ea_path(&ea, eaname, 1)) == NULL) { 1736 ret = AFPERR_MISC; 1737 goto exit; 1738 } 1739 if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) { 1740 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno)); 1741 switch (errno) { 1742 case EPERM: 1743 case EACCES: 1744 ret = AFPERR_ACCESS; 1745 goto exit; 1746 default: 1747 ret = AFPERR_MISC; 1748 goto exit; 1749 } 1750 continue; 1751 } 1752 1753 count++; 1754 } 1755 1756exit: 1757 if (seteuid(uid) < 0) { 1758 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); 1759 exit(EXITERR_SYS); 1760 } 1761 1762 if ((ea_close(&ea)) != 0) { 1763 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name); 1764 return AFPERR_MISC; 1765 } 1766 1767 return ret; 1768} 1769