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