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