• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.0/libatalk/vfs/
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