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 attruname); 519 return -1; 520 } 521 522 while (count < ea->ea_count) { 523 /* search matching EA */ 524 if ((*ea->ea_entries)[count].ea_name && 525 strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { 526 free((*ea->ea_entries)[count].ea_name); 527 (*ea->ea_entries)[count].ea_name = NULL; 528 529 LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u", 530 attruname, count + 1, ea->ea_count); 531 532 break; 533 } 534 count++; 535 } 536 537 return ret; 538} 539 540/* 541 * Function: delete_ea_file 542 * 543 * Purpose: delete EA file from disk 544 * 545 * Arguments: 546 * 547 * ea (r) struct ea handle 548 * attruname (r) EA name 549 * 550 * Returns: 0 on success, -1 on error 551 */ 552static int delete_ea_file(const struct ea * restrict ea, const char *eaname) 553{ 554 int ret = 0; 555 char *eafile; 556 struct stat st; 557 558 if ((eafile = ea_path(ea, eaname, 1)) == NULL) { 559 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname); 560 return -1; 561 } 562 563 /* Check if it exists, remove if yes*/ 564 if ((stat(eafile, &st)) == 0) { 565 if ((unlink(eafile)) != 0) { 566 LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s", 567 eafile, strerror(errno)); 568 ret = -1; 569 } else 570 LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile); 571 } 572 573 return ret; 574} 575 576/************************************************************************************* 577 * ea_path, ea_open and ea_close are only global so that dbd can call them 578 *************************************************************************************/ 579 580/* 581 * Function: ea_path 582 * 583 * Purpose: return name of ea header filename 584 * 585 * Arguments: 586 * 587 * ea (r) ea handle 588 * eaname (r) name of EA or NULL 589 * macname (r) if != 0 call mtoupath on eaname 590 * 591 * Returns: pointer to name in static buffer, NULL on error 592 * 593 * Effects: 594 * 595 * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme 596 * Files: "file" -> "file/.AppleDouble/file::EA" 597 * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA" 598 * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA" 599 */ 600char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname) 601{ 602 char *adname; 603 static char pathbuf[MAXPATHLEN + 1]; 604 605 /* get name of a adouble file from uname */ 606 adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0); 607 /* copy it so we can work with it */ 608 strlcpy(pathbuf, adname, MAXPATHLEN + 1); 609 /* append "::EA" */ 610 strlcat(pathbuf, "::EA", MAXPATHLEN + 1); 611 612 if (eaname) { 613 strlcat(pathbuf, "::", MAXPATHLEN + 1); 614 if (macname) 615 if ((eaname = mtoupath(ea->vol, eaname)) == NULL) 616 return NULL; 617 strlcat(pathbuf, eaname, MAXPATHLEN + 1); 618 } 619 620 return pathbuf; 621} 622 623/* 624 * Function: ea_open 625 * 626 * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE 627 * 628 * Arguments: 629 * 630 * vol (r) current volume 631 * uname (r) filename for which we have to open a header 632 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) 633 * EA_RDONLY: open read only 634 * EA_RDWR: open read/write 635 * Eiterh EA_RDONLY or EA_RDWR MUST be requested 636 * ea (w) pointer to a struct ea that we fill 637 * 638 * Returns: 0 on success 639 * -1 on misc error with errno = EFAULT 640 * -2 if no EA header exists with errno = ENOENT 641 * 642 * Effects: 643 * 644 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. 645 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. 646 * file is either read or write locked depending on the open flags. 647 * When you're done with struct ea you must call ea_close on it. 648 */ 649int ea_open(const struct vol * restrict vol, 650 const char * restrict uname, 651 eaflags_t eaflags, 652 struct ea * restrict ea) 653{ 654 int ret = 0; 655 char *eaname; 656 struct stat st; 657 658 /* Enforce usage rules! */ 659 if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) { 660 LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname); 661 return -1; 662 } 663 664 /* Set it all to 0 */ 665 memset(ea, 0, sizeof(struct ea)); 666 667 ea->vol = vol; /* ea_close needs it */ 668 ea->ea_flags = eaflags; 669 ea->dirfd = -1; /* no *at (cf openat) semantics by default */ 670 671 /* Dont care for errors, eg when removing the file is already gone */ 672 if (!stat(uname, &st) && S_ISDIR(st.st_mode)) 673 ea->ea_flags |= EA_DIR; 674 675 if ( ! (ea->filename = strdup(uname))) { 676 LOG(log_error, logtype_afpd, "ea_open: OOM"); 677 return -1; 678 } 679 680 eaname = ea_path(ea, NULL, 0); 681 LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname); 682 683 /* Check if it exists, if not create it if EA_CREATE is in eaflags */ 684 if ((stat(eaname, &st)) != 0) { 685 if (errno == ENOENT) { 686 687 /* It doesnt exist */ 688 689 if ( ! (eaflags & EA_CREATE)) { 690 /* creation was not requested, so return with error */ 691 ret = -2; 692 goto exit; 693 } 694 695 /* Now create a header file */ 696 697 /* malloc buffer for minimal on disk data */ 698 ea->ea_data = malloc(EA_HEADER_SIZE); 699 if (! ea->ea_data) { 700 LOG(log_error, logtype_afpd, "ea_open: OOM"); 701 ret = -1; 702 goto exit; 703 } 704 705 /* create it */ 706 ea->ea_fd = create_ea_header(eaname, ea); 707 if (ea->ea_fd == -1) { 708 ret = -1; 709 goto exit; 710 } 711 712 return 0; 713 714 } else {/* errno != ENOENT */ 715 ret = -1; 716 goto exit; 717 } 718 } 719 720 /* header file exists, so read and parse it */ 721 722 /* malloc buffer where we read disk file into */ 723 if (st.st_size < EA_HEADER_SIZE) { 724 LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname); 725 ret = -1; 726 goto exit; 727 } 728 ea->ea_size = st.st_size; 729 ea->ea_data = malloc(st.st_size); 730 if (! ea->ea_data) { 731 LOG(log_error, logtype_afpd, "ea_open: OOM"); 732 ret = -1; 733 goto exit; 734 } 735 736 /* Now lock, open and read header file from disk */ 737 if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) { 738 LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno)); 739 ret = -1; 740 goto exit; 741 } 742 743 /* lock it */ 744 if (ea->ea_flags & EA_RDONLY) { 745 /* read lock */ 746 if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) { 747 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname); 748 ret = -1; 749 goto exit; 750 } 751 } else { /* EA_RDWR */ 752 /* write lock */ 753 if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) { 754 LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname); 755 ret = -1; 756 goto exit; 757 } 758 } 759 760 /* read it */ 761 if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { 762 LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname); 763 ret = -1; 764 goto exit; 765 } 766 767 if ((unpack_header(ea)) != 0) { 768 LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname); 769 ret = -1; 770 goto exit; 771 } 772 773exit: 774 switch (ret) { 775 case 0: 776 ea->ea_inited = EA_INITED; 777 break; 778 case -1: 779 errno = EFAULT; /* force some errno distinguishable from ENOENT */ 780 /* fall through */ 781 case -2: 782 if (ea->ea_data) { 783 free(ea->ea_data); 784 ea->ea_data = NULL; 785 } 786 if (ea->ea_fd) { 787 close(ea->ea_fd); 788 ea->ea_fd = -1; 789 } 790 break; 791 } 792 793 return ret; 794} 795 796/* 797 * Function: ea_openat 798 * 799 * Purpose: openat like wrapper for ea_open, takes a additional file descriptor 800 * 801 * Arguments: 802 * 803 * vol (r) current volume 804 * sfd (r) openat like file descriptor 805 * uname (r) filename for which we have to open a header 806 * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) 807 * EA_RDONLY: open read only 808 * EA_RDWR: open read/write 809 * Eiterh EA_RDONLY or EA_RDWR MUST be requested 810 * ea (w) pointer to a struct ea that we fill 811 * 812 * Returns: 0 on success 813 * -1 on misc error with errno = EFAULT 814 * -2 if no EA header exists with errno = ENOENT 815 * 816 * Effects: 817 * 818 * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. 819 * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. 820 * file is either read or write locked depending on the open flags. 821 * When you're done with struct ea you must call ea_close on it. 822 */ 823int ea_openat(const struct vol * restrict vol, 824 int dirfd, 825 const char * restrict uname, 826 eaflags_t eaflags, 827 struct ea * restrict ea) 828{ 829 int ret = 0; 830 int cwdfd = -1; 831 832 if (dirfd != -1) { 833 if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { 834 ret = -1; 835 goto exit; 836 } 837 } 838 839 ret = ea_open(vol, uname, eaflags, ea); 840 ea->dirfd = dirfd; 841 842 if (dirfd != -1) { 843 if (fchdir(cwdfd) != 0) { 844 LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting"); 845 exit(EXITERR_SYS); 846 } 847 } 848 849 850exit: 851 if (cwdfd != -1) 852 close(cwdfd); 853 854 return ret; 855 856} 857 858/* 859 * Function: ea_close 860 * 861 * Purpose: flushes and closes an ea handle 862 * 863 * Arguments: 864 * 865 * ea (rw) pointer to ea handle 866 * 867 * Returns: 0 on success, -1 on error 868 * 869 * Effects: 870 * 871 * Flushes and then closes and frees all resouces held by ea handle. 872 * Pack data in ea into ea_data, then write ea_data to disk 873 */ 874int ea_close(struct ea * restrict ea) 875{ 876 int ret = 0; 877 unsigned int count = 0; 878 char *eaname; 879 struct stat st; 880 881 LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename); 882 883 if (ea->ea_inited != EA_INITED) { 884 LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename); 885 return 0; 886 } 887 888 /* pack header and write it to disk if it was opened EA_RDWR*/ 889 if (ea->ea_flags & EA_RDWR) { 890 if ((pack_header(ea)) != 0) { 891 LOG(log_error, logtype_afpd, "ea_close: pack header"); 892 ret = -1; 893 } else { 894 if (ea->ea_count == 0) { 895 /* Check if EA header exists and remove it */ 896 eaname = ea_path(ea, NULL, 0); 897 if ((statat(ea->dirfd, eaname, &st)) == 0) { 898 if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) { 899 LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s", 900 eaname, strerror(errno)); 901 ret = -1; 902 } 903 else 904 LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname); 905 } else { 906 /* stat error */ 907 if (errno != ENOENT) { 908 LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s", 909 eaname, strerror(errno)); 910 ret = -1; 911 } 912 } 913 } else { /* ea->ea_count > 0 */ 914 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) { 915 LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno)); 916 ret = -1; 917 goto exit; 918 } 919 920 if ((ftruncate(ea->ea_fd, 0)) == -1) { 921 LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno)); 922 ret = -1; 923 goto exit; 924 } 925 926 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { 927 LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno)); 928 ret = -1; 929 } 930 } 931 } 932 } 933 934exit: 935 /* free names */ 936 while(count < ea->ea_count) { 937 if ( (*ea->ea_entries)[count].ea_name ) { 938 free((*ea->ea_entries)[count].ea_name); 939 (*ea->ea_entries)[count].ea_name = NULL; 940 } 941 count++; 942 } 943 ea->ea_count = 0; 944 945 if (ea->filename) { 946 free(ea->filename); 947 ea->filename = NULL; 948 } 949 950 if (ea->ea_entries) { 951 free(ea->ea_entries); 952 ea->ea_entries = NULL; 953 } 954 955 if (ea->ea_data) { 956 free(ea->ea_data); 957 ea->ea_data = NULL; 958 } 959 if (ea->ea_fd != -1) { 960 close(ea->ea_fd); /* also releases the fcntl lock */ 961 ea->ea_fd = -1; 962 } 963 964 return 0; 965} 966 967 968 969/************************************************************************************ 970 * VFS funcs called from afp_ea* funcs 971 ************************************************************************************/ 972 973/* 974 * Function: get_easize 975 * 976 * Purpose: get size of an EA 977 * 978 * Arguments: 979 * 980 * vol (r) current volume 981 * rbuf (w) DSI reply buffer 982 * rbuflen (rw) current length of data in reply buffer 983 * uname (r) filename 984 * oflag (r) link and create flag 985 * attruname (r) name of attribute 986 * 987 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 988 * 989 * Effects: 990 * 991 * Copies EA size into rbuf in network order. Increments *rbuflen +4. 992 */ 993int get_easize(VFS_FUNC_ARGS_EA_GETSIZE) 994{ 995 int ret = AFPERR_MISC; 996 unsigned int count = 0; 997 uint32_t uint32; 998 struct ea ea; 999 1000 LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname); 1001 1002 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { 1003 if (errno != ENOENT) 1004 LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname); 1005 1006 memset(rbuf, 0, 4); 1007 *rbuflen += 4; 1008 return ret; 1009 } 1010 1011 while (count < ea.ea_count) { 1012 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { 1013 uint32 = htonl((*ea.ea_entries)[count].ea_size); 1014 memcpy(rbuf, &uint32, 4); 1015 *rbuflen += 4; 1016 ret = AFP_OK; 1017 1018 LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u", 1019 attruname, (*ea.ea_entries)[count].ea_size); 1020 break; 1021 } 1022 count++; 1023 } 1024 1025 if ((ea_close(&ea)) != 0) { 1026 LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname); 1027 return AFPERR_MISC; 1028 } 1029 1030 return ret; 1031} 1032 1033/* 1034 * Function: get_eacontent 1035 * 1036 * Purpose: copy EA into rbuf 1037 * 1038 * Arguments: 1039 * 1040 * vol (r) current volume 1041 * rbuf (w) DSI reply buffer 1042 * rbuflen (rw) current length of data in reply buffer 1043 * uname (r) filename 1044 * oflag (r) link and create flag 1045 * attruname (r) name of attribute 1046 * maxreply (r) maximum EA size as of current specs/real-life 1047 * 1048 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 1049 * 1050 * Effects: 1051 * 1052 * Copies EA into rbuf. Increments *rbuflen accordingly. 1053 */ 1054int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT) 1055{ 1056 int ret = AFPERR_MISC, fd = -1; 1057 unsigned int count = 0; 1058 uint32_t uint32; 1059 size_t toread; 1060 struct ea ea; 1061 char *eafile; 1062 1063 LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname); 1064 1065 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { 1066 if (errno != ENOENT) 1067 LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname); 1068 memset(rbuf, 0, 4); 1069 *rbuflen += 4; 1070 return ret; 1071 } 1072 1073 while (count < ea.ea_count) { 1074 if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { 1075 if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) { 1076 ret = AFPERR_MISC; 1077 break; 1078 } 1079 1080 if ((fd = open(eafile, O_RDONLY)) == -1) { 1081 LOG(log_error, logtype_afpd, "get_eacontent('%s'): open error: %s", uname, strerror(errno)); 1082 ret = AFPERR_MISC; 1083 break; 1084 } 1085 1086 /* Check how much the client wants, give him what we think is right */ 1087 maxreply -= MAX_REPLY_EXTRA_BYTES; 1088 if (maxreply > MAX_EA_SIZE) 1089 maxreply = MAX_EA_SIZE; 1090 toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size; 1091 LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread); 1092 1093 /* Put length of EA data in reply buffer */ 1094 uint32 = htonl(toread); 1095 memcpy(rbuf, &uint32, 4); 1096 rbuf += 4; 1097 *rbuflen += 4; 1098 1099 if (read(fd, rbuf, toread) != (ssize_t)toread) { 1100 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname); 1101 close(fd); 1102 ret = AFPERR_MISC; 1103 break; 1104 } 1105 *rbuflen += toread; 1106 close(fd); 1107 1108 ret = AFP_OK; 1109 break; 1110 } 1111 count++; 1112 } 1113 1114 if ((ea_close(&ea)) != 0) { 1115 LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname); 1116 return AFPERR_MISC; 1117 } 1118 1119 return ret; 1120 1121} 1122 1123/* 1124 * Function: list_eas 1125 * 1126 * Purpose: copy names of EAs into attrnamebuf 1127 * 1128 * Arguments: 1129 * 1130 * vol (r) current volume 1131 * attrnamebuf (w) store names a consecutive C strings here 1132 * buflen (rw) length of names in attrnamebuf 1133 * uname (r) filename 1134 * oflag (r) link and create flag 1135 * 1136 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 1137 * 1138 * Effects: 1139 * 1140 * Copies names of all EAs of uname as consecutive C strings into rbuf. 1141 * Increments *buflen accordingly. 1142 */ 1143int list_eas(VFS_FUNC_ARGS_EA_LIST) 1144{ 1145 unsigned int count = 0; 1146 int attrbuflen = *buflen, ret = AFP_OK, len; 1147 char *buf = attrnamebuf; 1148 struct ea ea; 1149 1150 LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname); 1151 1152 if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { 1153 if (errno != ENOENT) { 1154 LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname); 1155 return AFPERR_MISC; 1156 } 1157 else 1158 return AFP_OK; 1159 } 1160 1161 while (count < ea.ea_count) { 1162 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */ 1163 if ( ( len = convert_string(vol->v_volcharset, 1164 CH_UTF8_MAC, 1165 (*ea.ea_entries)[count].ea_name, 1166 (*ea.ea_entries)[count].ea_namelen, 1167 buf + attrbuflen, 1168 255)) 1169 <= 0 ) { 1170 ret = AFPERR_MISC; 1171 goto exit; 1172 } 1173 if (len == 255) 1174 /* convert_string didn't 0-terminate */ 1175 attrnamebuf[attrbuflen + 255] = 0; 1176 1177 LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s", 1178 uname, (*ea.ea_entries)[count].ea_name); 1179 1180 attrbuflen += len + 1; 1181 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) { 1182 /* Next EA name could overflow, so bail out with error. 1183 FIXME: evantually malloc/memcpy/realloc whatever. 1184 Is it worth it ? */ 1185 LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname); 1186 ret = AFPERR_MISC; 1187 goto exit; 1188 } 1189 count++; 1190 } 1191 1192exit: 1193 *buflen = attrbuflen; 1194 1195 if ((ea_close(&ea)) != 0) { 1196 LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname); 1197 return AFPERR_MISC; 1198 } 1199 1200 return ret; 1201} 1202 1203/* 1204 * Function: set_ea 1205 * 1206 * Purpose: set a Solaris native EA 1207 * 1208 * Arguments: 1209 * 1210 * vol (r) current volume 1211 * uname (r) filename 1212 * attruname (r) EA name 1213 * ibuf (r) buffer with EA content 1214 * attrsize (r) length EA in ibuf 1215 * oflag (r) link and create flag 1216 * 1217 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 1218 * 1219 * Effects: 1220 * 1221 * Copies names of all EAs of uname as consecutive C strings into rbuf. 1222 * Increments *rbuflen accordingly. 1223 */ 1224int set_ea(VFS_FUNC_ARGS_EA_SET) 1225{ 1226 int ret = AFP_OK; 1227 struct ea ea; 1228 1229 LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname); 1230 1231 if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) { 1232 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname); 1233 return AFPERR_MISC; 1234 } 1235 1236 if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) { 1237 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname); 1238 ret = AFPERR_MISC; 1239 goto exit; 1240 } 1241 1242 if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) { 1243 LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname); 1244 ret = AFPERR_MISC; 1245 goto exit; 1246 } 1247 1248exit: 1249 if ((ea_close(&ea)) != 0) { 1250 LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname); 1251 ret = AFPERR_MISC; 1252 goto exit; 1253 } 1254 1255 return ret; 1256} 1257 1258/* 1259 * Function: remove_ea 1260 * 1261 * Purpose: remove a EA from a file 1262 * 1263 * Arguments: 1264 * 1265 * vol (r) current volume 1266 * uname (r) filename 1267 * attruname (r) EA name 1268 * oflag (r) link and create flag 1269 * 1270 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 1271 * 1272 * Effects: 1273 * 1274 * Removes EA attruname from file uname. 1275 */ 1276int remove_ea(VFS_FUNC_ARGS_EA_REMOVE) 1277{ 1278 int ret = AFP_OK; 1279 struct ea ea; 1280 1281 LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname); 1282 1283 if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) { 1284 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname); 1285 return AFPERR_MISC; 1286 } 1287 1288 if ((ea_delentry(&ea, attruname)) == -1) { 1289 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname); 1290 ret = AFPERR_MISC; 1291 goto exit; 1292 } 1293 1294 if ((delete_ea_file(&ea, attruname)) != 0) { 1295 LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname); 1296 ret = AFPERR_MISC; 1297 goto exit; 1298 } 1299 1300exit: 1301 if ((ea_close(&ea)) != 0) { 1302 LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname); 1303 ret = AFPERR_MISC; 1304 goto exit; 1305 } 1306 1307 return ret; 1308} 1309 1310/****************************************************************************************** 1311 * EA VFS funcs that deal with file/dir cp/mv/rm 1312 ******************************************************************************************/ 1313 1314int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE) 1315{ 1316 unsigned int count = 0; 1317 int ret = AFP_OK; 1318 int cwd = -1; 1319 struct ea ea; 1320 1321 LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file); 1322 1323 /* Open EA stuff */ 1324 if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) { 1325 if (errno == ENOENT) 1326 /* no EA files, nothing to do */ 1327 return AFP_OK; 1328 else { 1329 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file); 1330 return AFPERR_MISC; 1331 } 1332 } 1333 1334 if (dirfd != -1) { 1335 if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { 1336 ret = AFPERR_MISC; 1337 goto exit; 1338 } 1339 } 1340 1341 while (count < ea.ea_count) { 1342 if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) { 1343 ret = AFPERR_MISC; 1344 continue; 1345 } 1346 free((*ea.ea_entries)[count].ea_name); 1347 (*ea.ea_entries)[count].ea_name = NULL; 1348 count++; 1349 } 1350 1351 /* ea_close removes the EA header file for us because all names are NULL */ 1352 if ((ea_close(&ea)) != 0) { 1353 LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file); 1354 ret = AFPERR_MISC; 1355 } 1356 1357 if (dirfd != -1 && fchdir(cwd) != 0) { 1358 LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!"); 1359 exit(EXITERR_SYS); 1360 } 1361 1362exit: 1363 if (cwd != -1) 1364 close(cwd); 1365 1366 return ret; 1367} 1368 1369int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE) 1370{ 1371 unsigned int count = 0; 1372 int ret = AFP_OK; 1373 size_t easize; 1374 char srceapath[ MAXPATHLEN + 1]; 1375 char *eapath; 1376 char *eaname; 1377 struct ea srcea; 1378 struct ea dstea; 1379 struct adouble ad; 1380 1381 LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst); 1382 1383 1384 /* Open EA stuff */ 1385 if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) { 1386 if (errno == ENOENT) 1387 /* no EA files, nothing to do */ 1388 return AFP_OK; 1389 else { 1390 LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src); 1391 return AFPERR_MISC; 1392 } 1393 } 1394 1395 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { 1396 if (errno == ENOENT) { 1397 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */ 1398 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 1399 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) { 1400 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst); 1401 ret = AFPERR_MISC; 1402 goto exit; 1403 } 1404 ad_close(&ad, ADFLAGS_HF); 1405 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { 1406 ret = AFPERR_MISC; 1407 goto exit; 1408 } 1409 } 1410 } 1411 1412 /* Loop through all EAs: */ 1413 while (count < srcea.ea_count) { 1414 /* Move EA */ 1415 eaname = (*srcea.ea_entries)[count].ea_name; 1416 easize = (*srcea.ea_entries)[count].ea_size; 1417 1418 /* Build src and dst paths for rename() */ 1419 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { 1420 ret = AFPERR_MISC; 1421 goto exit; 1422 } 1423 strcpy(srceapath, eapath); 1424 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { 1425 ret = AFPERR_MISC; 1426 goto exit; 1427 } 1428 1429 LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", 1430 src, dst, srceapath, eapath); 1431 1432 /* Add EA to dstea */ 1433 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) { 1434 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", 1435 src, dst, srceapath, eapath); 1436 ret = AFPERR_MISC; 1437 goto exit; 1438 } 1439 1440 /* Remove EA entry from srcea */ 1441 if ((ea_delentry(&srcea, eaname)) == -1) { 1442 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", 1443 src, dst, srceapath, eapath); 1444 ea_delentry(&dstea, eaname); 1445 ret = AFPERR_MISC; 1446 goto exit; 1447 } 1448 1449 /* Now rename the EA */ 1450 if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) { 1451 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", 1452 src, dst, srceapath, eapath); 1453 ret = AFPERR_MISC; 1454 goto exit; 1455 } 1456 1457 count++; 1458 } 1459 1460 1461exit: 1462 ea_close(&srcea); 1463 ea_close(&dstea); 1464 return ret; 1465} 1466 1467int ea_copyfile(VFS_FUNC_ARGS_COPYFILE) 1468{ 1469 unsigned int count = 0; 1470 int ret = AFP_OK; 1471 size_t easize; 1472 char srceapath[ MAXPATHLEN + 1]; 1473 char *eapath; 1474 char *eaname; 1475 struct ea srcea; 1476 struct ea dstea; 1477 struct adouble ad; 1478 1479 LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst); 1480 1481 /* Open EA stuff */ 1482 if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) { 1483 if (errno == ENOENT) 1484 /* no EA files, nothing to do */ 1485 return AFP_OK; 1486 else { 1487 LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src); 1488 return AFPERR_MISC; 1489 } 1490 } 1491 1492 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { 1493 if (errno == ENOENT) { 1494 /* Possibly the .AppleDouble folder didn't exist, we create it and try again */ 1495 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 1496 if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) { 1497 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst); 1498 ret = AFPERR_MISC; 1499 goto exit; 1500 } 1501 ad_close(&ad, ADFLAGS_HF); 1502 if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { 1503 ret = AFPERR_MISC; 1504 goto exit; 1505 } 1506 } 1507 } 1508 1509 /* Loop through all EAs: */ 1510 while (count < srcea.ea_count) { 1511 /* Copy EA */ 1512 eaname = (*srcea.ea_entries)[count].ea_name; 1513 easize = (*srcea.ea_entries)[count].ea_size; 1514 1515 /* Build src and dst paths for copy_file() */ 1516 if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { 1517 ret = AFPERR_MISC; 1518 goto exit; 1519 } 1520 strcpy(srceapath, eapath); 1521 if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { 1522 ret = AFPERR_MISC; 1523 goto exit; 1524 } 1525 1526 LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", 1527 src, dst, srceapath, eapath); 1528 1529 /* Add EA to dstea */ 1530 if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) { 1531 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error", 1532 src, dst, eaname); 1533 ret = AFPERR_MISC; 1534 goto exit; 1535 } 1536 1537 /* Now copy the EA */ 1538 if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) { 1539 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", 1540 src, dst, srceapath, eapath); 1541 ret = AFPERR_MISC; 1542 goto exit; 1543 } 1544 1545 count++; 1546 } 1547 1548exit: 1549 ea_close(&srcea); 1550 ea_close(&dstea); 1551 return ret; 1552} 1553 1554int ea_chown(VFS_FUNC_ARGS_CHOWN) 1555{ 1556 1557 unsigned int count = 0; 1558 int ret = AFP_OK; 1559 char *eaname; 1560 struct ea ea; 1561 1562 LOG(log_debug, logtype_afpd, "ea_chown('%s')", path); 1563 /* Open EA stuff */ 1564 if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) { 1565 if (errno == ENOENT) 1566 /* no EA files, nothing to do */ 1567 return AFP_OK; 1568 else { 1569 LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path); 1570 return AFPERR_MISC; 1571 } 1572 } 1573 1574 if ((ochown(ea_path(&ea, NULL, 0), uid, gid, vol_syml_opt(vol))) != 0) { 1575 switch (errno) { 1576 case EPERM: 1577 case EACCES: 1578 ret = AFPERR_ACCESS; 1579 goto exit; 1580 default: 1581 ret = AFPERR_MISC; 1582 goto exit; 1583 } 1584 } 1585 1586 while (count < ea.ea_count) { 1587 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { 1588 ret = AFPERR_MISC; 1589 goto exit; 1590 } 1591 if ((ochown(eaname, uid, gid, vol_syml_opt(vol))) != 0) { 1592 switch (errno) { 1593 case EPERM: 1594 case EACCES: 1595 ret = AFPERR_ACCESS; 1596 goto exit; 1597 default: 1598 ret = AFPERR_MISC; 1599 goto exit; 1600 } 1601 continue; 1602 } 1603 1604 count++; 1605 } 1606 1607exit: 1608 if ((ea_close(&ea)) != 0) { 1609 LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path); 1610 return AFPERR_MISC; 1611 } 1612 1613 return ret; 1614} 1615 1616int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) 1617{ 1618 1619 unsigned int count = 0; 1620 int ret = AFP_OK; 1621 const char *eaname; 1622 struct ea ea; 1623 1624 LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name); 1625 /* Open EA stuff */ 1626 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { 1627 if (errno == ENOENT) 1628 /* no EA files, nothing to do */ 1629 return AFP_OK; 1630 else 1631 return AFPERR_MISC; 1632 } 1633 1634 /* Set mode on EA header file */ 1635 if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) { 1636 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); 1637 switch (errno) { 1638 case EPERM: 1639 case EACCES: 1640 ret = AFPERR_ACCESS; 1641 goto exit; 1642 default: 1643 ret = AFPERR_MISC; 1644 goto exit; 1645 } 1646 } 1647 1648 /* Set mode on EA files */ 1649 while (count < ea.ea_count) { 1650 if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { 1651 ret = AFPERR_MISC; 1652 goto exit; 1653 } 1654 if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) { 1655 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno)); 1656 switch (errno) { 1657 case EPERM: 1658 case EACCES: 1659 ret = AFPERR_ACCESS; 1660 goto exit; 1661 default: 1662 ret = AFPERR_MISC; 1663 goto exit; 1664 } 1665 continue; 1666 } 1667 1668 count++; 1669 } 1670 1671exit: 1672 if ((ea_close(&ea)) != 0) { 1673 LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name); 1674 return AFPERR_MISC; 1675 } 1676 1677 return ret; 1678} 1679 1680int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) 1681{ 1682 1683 int ret = AFP_OK; 1684 unsigned int count = 0; 1685 uid_t uid; 1686 const char *eaname; 1687 const char *eaname_safe = NULL; 1688 struct ea ea; 1689 1690 LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name); 1691 /* .AppleDouble already might be inaccesible, so we must run as id 0 */ 1692 uid = geteuid(); 1693 if (seteuid(0)) { 1694 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno)); 1695 return AFPERR_MISC; 1696 } 1697 1698 /* Open EA stuff */ 1699 if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { 1700 /* ENOENT --> no EA files, nothing to do */ 1701 if (errno != ENOENT) 1702 ret = AFPERR_MISC; 1703 if (seteuid(uid) < 0) { 1704 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); 1705 exit(EXITERR_SYS); 1706 } 1707 return ret; 1708 } 1709 1710 /* Set mode on EA header */ 1711 if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) { 1712 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); 1713 switch (errno) { 1714 case EPERM: 1715 case EACCES: 1716 ret = AFPERR_ACCESS; 1717 goto exit; 1718 default: 1719 ret = AFPERR_MISC; 1720 goto exit; 1721 } 1722 } 1723 1724 /* Set mode on EA files */ 1725 while (count < ea.ea_count) { 1726 eaname = (*ea.ea_entries)[count].ea_name; 1727 /* 1728 * Be careful with EA names from the EA header! 1729 * Eg NFS users might have access to them, can inject paths using ../ or /..... 1730 * FIXME: 1731 * Until the EA code escapes / in EA name requests from the client, these therefor wont work. 1732 */ 1733 if ((eaname_safe = strrchr(eaname, '/'))) { 1734 LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname); 1735 eaname = eaname_safe; 1736 } 1737 if ((eaname = ea_path(&ea, eaname, 1)) == NULL) { 1738 ret = AFPERR_MISC; 1739 goto exit; 1740 } 1741 if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) { 1742 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno)); 1743 switch (errno) { 1744 case EPERM: 1745 case EACCES: 1746 ret = AFPERR_ACCESS; 1747 goto exit; 1748 default: 1749 ret = AFPERR_MISC; 1750 goto exit; 1751 } 1752 continue; 1753 } 1754 1755 count++; 1756 } 1757 1758exit: 1759 if (seteuid(uid) < 0) { 1760 LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); 1761 exit(EXITERR_SYS); 1762 } 1763 1764 if ((ea_close(&ea)) != 0) { 1765 LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name); 1766 return AFPERR_MISC; 1767 } 1768 1769 return ret; 1770} 1771