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