]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea.c
Return right error for requested but non-existing EA.
[netatalk.git] / libatalk / vfs / ea.c
1 /*
2   $Id: ea.c,v 1.17 2009-12-04 10:26:10 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_path
280  *
281  * Purpose: return name of ea header filename
282  *
283  * Arguments:
284  *
285  *    ea           (r) ea handle
286  *    eaname       (r) name of EA or NULL
287  *
288  * Returns: pointer to name in static buffer, NULL on error
289  *
290  * Effects:
291  *
292  * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
293  * Files: "file" -> "file/.AppleDouble/file::EA"
294  * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
295  * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
296  */
297 static char * ea_path(const struct ea * restrict ea,
298                       const char * restrict eaname)
299 {
300     char *adname;
301     static char pathbuf[MAXPATHLEN + 1];
302
303     /* get name of a adouble file from uname */
304     adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
305     /* copy it so we can work with it */
306     strlcpy(pathbuf, adname, MAXPATHLEN + 1);
307     /* append "::EA" */
308     strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
309
310     if (eaname) {
311         strlcat(pathbuf, "::", MAXPATHLEN + 1);
312         if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
313             return NULL;
314         strlcat(pathbuf, eaname, MAXPATHLEN + 1);
315     }
316
317     return pathbuf;
318 }
319
320 /*
321  * Function: ea_addentry
322  *
323  * Purpose: add one EA into ea->ea_entries[]
324  *
325  * Arguments:
326  *
327  *    ea            (rw) pointer to struct ea
328  *    attruname     (r) name of EA
329  *    attrsize      (r) size of ea
330  *    bitmap        (r) bitmap from FP func
331  *
332  * Returns: new number of EA entries, -1 on error
333  *
334  * Effects:
335  *
336  * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
337  * Otherwise realloc and put entry at the end. Increments ea->ea_count.
338  */
339 static int ea_addentry(struct ea * restrict ea,
340                        const char * restrict attruname,
341                        size_t attrsize,
342                        int bitmap)
343 {
344     unsigned int count = 0;
345     void *tmprealloc;
346
347     /* First check if an EA of the requested name already exist */
348     if (ea->ea_count > 0) {
349         while (count < ea->ea_count) {
350             if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
351                 LOG(log_debug, logtype_afpd, "ea_addentry('%s'): exists", attruname);
352                 if (bitmap & kXAttrCreate)
353                     /* its like O_CREAT|O_EXCL -> fail */
354                     return -1;
355                 if ( ! (bitmap & kXAttrReplace))
356                     /* replace was not requested, then its an error */
357                     return -1;
358                 break;
359             }
360             count++;
361         }
362     }
363
364     if (ea->ea_count == 0) {
365         ea->ea_entries = malloc(sizeof(struct ea_entry));
366         if ( ! ea->ea_entries) {
367             LOG(log_error, logtype_afpd, "ea_addentry: OOM");
368             return -1;
369         }
370     } else {
371         tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
372         if ( ! tmprealloc) {
373             LOG(log_error, logtype_afpd, "ea_addentry: OOM");
374             return -1;
375         }
376         ea->ea_entries = tmprealloc;
377     }
378
379     /* We've grown the array, now store the entry */
380     (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
381     (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
382     if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
383         LOG(log_error, logtype_afpd, "ea_addentry: OOM");
384         goto error;
385     }
386     (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
387
388     ea->ea_count++;
389     return ea->ea_count;
390
391 error:
392     if (ea->ea_count == 0 && ea->ea_entries) {
393         /* We just allocated storage but had an error somewhere -> free storage*/
394         free(ea->ea_entries);
395         ea->ea_entries = NULL;
396     }
397     ea->ea_count = 0;
398     return -1;
399 }
400
401 /*
402  * Function: ea_delentry
403  *
404  * Purpose: delete one EA from ea->ea_entries[]
405  *
406  * Arguments:
407  *
408  *    ea            (rw) pointer to struct ea
409  *    attruname     (r) EA name
410  *
411  * Returns: new number of EA entries, -1 on error
412  *
413  * Effects:
414  *
415  * Remove entry from  ea->ea_entries[]. Decrement ea->ea_count.
416  * Marks it as unused just by freeing name and setting it to NULL.
417  * ea_close and pack_buffer must honor this.
418  */
419 static int ea_delentry(struct ea * restrict ea,
420                        const char * restrict attruname)
421 {
422     int ret = 0;
423     unsigned int count = 0;
424
425     if (ea->ea_count == 0) {
426         LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
427         return -1;
428     }
429
430     while (count < ea->ea_count) {
431         /* search matching EA */
432         if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
433             free((*ea->ea_entries)[count].ea_name);
434             (*ea->ea_entries)[count].ea_name = NULL;
435
436             LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
437                 attruname, count + 1, ea->ea_count);
438
439             break;
440         }
441         count++;
442     }
443
444     return ret;
445 }
446
447 /*
448  * Function: create_ea_header
449  *
450  * Purpose: create EA header file, only called from ea_open
451  *
452  * Arguments:
453  *
454  *    uname       (r)  filename for which we have to create a header
455  *    ea          (rw) ea handle with already allocated storage pointed to
456  *                     by ea->ea_data
457  *
458  * Returns: fd of open header file on success, -1 on error, errno semantics:
459  *          EEXIST: open with O_CREAT | O_EXCL failed
460  *
461  * Effects:
462  *
463  * Creates EA header file and initialize ea->ea_data buffer.
464  * Possibe race condition with other afpd processes:
465  * we were called because header file didn't exist in eg. ea_open. We then
466  * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
467  * What do we do then? Someone else is in the process of creating the header too, but
468  * it might not have finished it. That means we cant just open, read and use it!
469  * We therefor currently just break with an error.
470  * On return the header file is still r/w locked.
471  */
472 static int create_ea_header(const char * restrict uname,
473                             struct ea * restrict ea)
474 {
475     int fd = -1, err = 0;
476     char *ptr;
477
478     if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
479         LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
480         return -1;
481     }
482
483     /* lock it */
484     if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
485         LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
486         err = -1;
487         goto exit;
488     }
489
490     /* Now init it */
491     ptr = ea->ea_data;
492     *(uint32_t *)ptr = htonl(EA_MAGIC);
493     ptr += EA_MAGIC_LEN;
494     *(uint16_t *)ptr = htons(EA_VERSION);
495     ptr += EA_VERSION_LEN;
496     *(uint16_t *)ptr = 0;       /* count */
497
498     ea->ea_size = EA_HEADER_SIZE;
499     ea->ea_inited = EA_INITED;
500
501 exit:
502     if (err != 0) {
503         close(fd);
504         fd = -1;
505     }
506     return fd;
507 }
508
509 /*
510  * Function: write_ea
511  *
512  * Purpose: write an EA to disk
513  *
514  * Arguments:
515  *
516  *    ea         (r) struct ea handle
517  *    attruname  (r) EA name
518  *    ibuf       (r) buffer with EA content
519  *    attrsize   (r) size of EA
520  *
521  * Returns: 0 on success, -1 on error
522  *
523  * Effects:
524  *
525  * Creates/overwrites EA file.
526  *
527  */
528 static int write_ea(const struct ea * restrict ea,
529                     const char * restrict attruname,
530                     const char * restrict ibuf,
531                     size_t attrsize)
532 {
533     int fd = -1, ret = AFP_OK;
534     struct stat st;
535     char *eaname;
536
537     if ((eaname = ea_path(ea, attruname)) == NULL) {
538         LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
539         return AFPERR_MISC;
540     }
541
542     LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname);
543
544     /* Check if it exists, remove if yes*/
545     if ((stat(eaname, &st)) == 0) {
546         if ((unlink(eaname)) != 0) {
547             if (errno == EACCES)
548                 return AFPERR_ACCESS;
549             else
550                 return AFPERR_MISC;
551         }
552     }
553
554     if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
555         LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
556         return -1;
557     }
558
559     /* lock it */
560     if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
561         LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
562         ret = -1;
563         goto exit;
564     }
565
566     if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) {
567         LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
568         ret = -1;
569         goto exit;
570     }
571
572 exit:
573     if (fd != -1)
574         close(fd); /* and unlock */
575     return ret;
576 }
577
578 /*
579  * Function: delete_ea_file
580  *
581  * Purpose: delete EA file from disk
582  *
583  * Arguments:
584  *
585  *    ea         (r) struct ea handle
586  *    attruname  (r) EA name
587  *
588  * Returns: 0 on success, -1 on error
589  */
590 static int delete_ea_file(const struct ea * restrict ea,
591                           const char *eaname)
592 {
593     int ret = 0;
594     char *eafile;
595     struct stat st;
596
597     if ((eafile = ea_path(ea, eaname)) == NULL) {
598         LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
599         return -1;
600     }
601
602     /* Check if it exists, remove if yes*/
603     if ((stat(eafile, &st)) == 0) {
604         if ((unlink(eafile)) != 0) {
605             LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
606                 eafile, strerror(errno));
607             ret = -1;
608         } else
609             LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
610     }
611
612     return ret;
613 }
614
615 /*
616  * Function: ea_open
617  *
618  * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
619  *
620  * Arguments:
621  *
622  *    vol         (r) current volume
623  *    uname       (r) filename for which we have to open a header
624  *    flags       (r) EA_CREATE: create if it doesn't exist (without it won't be created)
625  *                    EA_RDONLY: open read only
626  *                    EA_RDWR: open read/write
627  *                    Eiterh EA_RDONLY or EA_RDWR MUST be requested
628  *    ea          (w) pointer to a struct ea that we fill
629  *
630  * Returns: 0 on success, -1 on error
631  *
632  * Effects:
633  *
634  * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
635  * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
636  * file is either read or write locked depending on the open flags.
637  * When you're done with struct ea you must call ea_close on it.
638  */
639 static int ea_open(const struct vol * restrict vol,
640                    const char * restrict uname,
641                    eaflags_t eaflags,
642                    struct ea * restrict ea)
643 {
644     int ret = 0;
645     char *eaname;
646     struct stat st;
647
648     /* Enforce usage rules! */
649     if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
650         LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
651         return -1;
652     }
653
654     /* Set it all to 0 */
655     memset(ea, 0, sizeof(struct ea));
656
657     ea->vol = vol;              /* ea_close needs it */
658     ea->ea_flags = eaflags;
659     /* Dont care for errors, eg when removing the file is already gone */
660     if (!stat(uname, &st) && S_ISDIR(st.st_mode))
661         ea->ea_flags |=  EA_DIR;
662
663     if ( ! (ea->filename = strdup(uname))) {
664         LOG(log_error, logtype_afpd, "ea_open: OOM");
665         return -1;
666     }
667
668     eaname = ea_path(ea, NULL);
669     LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
670
671     /* Check if it exists, if not create it if EA_CREATE is in eaflags */
672     if ((stat(eaname, &st)) != 0) {
673         if (errno == ENOENT) {
674
675             /* It doesnt exist */
676
677             if ( ! (eaflags & EA_CREATE)) {
678                 /* creation was not requested, so return with error */
679                 ret = -1;
680                 goto exit;
681             }
682
683             /* Now create a header file */
684
685             /* malloc buffer for minimal on disk data */
686             ea->ea_data = malloc(EA_HEADER_SIZE);
687             if (! ea->ea_data) {
688                 LOG(log_error, logtype_afpd, "ea_open: OOM");
689                 ret = -1;
690                 goto exit;
691             }
692
693             /* create it */
694             ea->ea_fd = create_ea_header(eaname, ea);
695             if (ea->ea_fd == -1) {
696                 ret = -1;
697                 goto exit;
698             }
699
700             return 0;
701
702         } else {/* errno != ENOENT */
703             ret = -1;
704             goto exit;
705         }
706     }
707
708     /* header file exists, so read and parse it */
709
710     /* malloc buffer where we read disk file into */
711     ea->ea_size = st.st_size;
712     ea->ea_data = malloc(st.st_size);
713     if (! ea->ea_data) {
714         LOG(log_error, logtype_afpd, "ea_open: OOM");
715         ret = -1;
716         goto exit;
717     }
718
719     /* Now lock, open and read header file from disk */
720     if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
721         LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno));
722         ret = -1;
723         goto exit;
724     }
725
726     /* lock it */
727     if (ea->ea_flags & EA_RDONLY) {
728         /* read lock */
729         if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
730             LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
731             ret = -1;
732             goto exit;
733         }
734     } else {  /* EA_RDWR */
735         /* write lock */
736         if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
737             LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
738             ret = -1;
739             goto exit;
740         }
741     }
742
743     /* read it */
744     if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
745         LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
746         ret = -1;
747         goto exit;
748     }
749
750     if ((unpack_header(ea)) != 0) {
751         LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
752         ret = -1;
753         goto exit;
754     }
755
756 exit:
757     if (ret == 0) {
758         ea->ea_inited = EA_INITED;
759     } else {
760         if (ea->ea_data) {
761             free(ea->ea_data);
762             ea->ea_data = NULL;
763         }
764         if (ea->ea_fd) {
765             close(ea->ea_fd);
766             ea->ea_fd = -1;
767         }
768     }
769
770     return ret;
771 }
772
773 /*
774  * Function: ea_close
775  *
776  * Purpose: flushes and closes an ea handle
777  *
778  * Arguments:
779  *
780  *    ea          (rw) pointer to ea handle
781  *
782  * Returns: 0 on success, -1 on error
783  *
784  * Effects:
785  *
786  * Flushes and then closes and frees all resouces held by ea handle.
787  * Pack data in ea into ea_data, then write ea_data to disk
788  */
789 static int ea_close(struct ea * restrict ea)
790 {
791     int ret = 0; 
792     unsigned int count = 0;
793     char *eaname;
794     struct stat st;
795
796     LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
797
798     if (ea->ea_inited != EA_INITED) {
799         LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
800         return 0;
801     }
802
803     /* pack header and write it to disk if it was opened EA_RDWR*/
804     if (ea->ea_flags & EA_RDWR) {
805         if ((pack_header(ea)) != 0) {
806             LOG(log_error, logtype_afpd, "ea_close: pack header");
807             ret = -1;
808         } else {
809             if (ea->ea_count == 0) {
810                 /* Check if EA header exists and remove it */
811                 eaname = ea_path(ea, NULL);
812                 if ((stat(eaname, &st)) == 0) {
813                     if ((unlink(eaname)) != 0) {
814                         LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
815                             eaname, strerror(errno));
816                         ret = -1;
817                     }
818                     else
819                         LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
820                 } else {
821                     /* stat error */
822                     if (errno != ENOENT) {
823                         LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
824                             eaname, strerror(errno));
825                         ret = -1;
826                     }
827                 }
828             } else { /* ea->ea_count > 0 */
829                 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
830                     LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
831                     ret = -1;
832                     goto exit;
833                 }
834
835                 if ((ftruncate(ea->ea_fd, 0)) == -1) {
836                     LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
837                     ret = -1;
838                     goto exit;
839                 }
840
841                 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
842                     LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
843                     ret = -1;
844                 }
845             }
846         }
847     }
848
849 exit:
850     /* free names */
851     while(count < ea->ea_count) {
852         if ( (*ea->ea_entries)[count].ea_name ) {
853             free((*ea->ea_entries)[count].ea_name);
854             (*ea->ea_entries)[count].ea_name = NULL;
855         }
856         count++;
857     }
858     ea->ea_count = 0;
859
860     if (ea->filename) {
861         free(ea->filename);
862         ea->filename = NULL;
863     }
864
865     if (ea->ea_entries) {
866         free(ea->ea_entries);
867         ea->ea_entries = NULL;
868     }
869
870     if (ea->ea_data) {
871         free(ea->ea_data);
872         ea->ea_data = NULL;
873     }
874     if (ea->ea_fd != -1) {
875         close(ea->ea_fd);       /* also releases the fcntl lock */
876         ea->ea_fd = -1;
877     }
878
879     return 0;
880 }
881
882
883
884 /************************************************************************************
885  * VFS funcs called from afp_ea* funcs
886  ************************************************************************************/
887
888 /*
889  * Function: get_easize
890  *
891  * Purpose: get size of an EA
892  *
893  * Arguments:
894  *
895  *    vol          (r) current volume
896  *    rbuf         (w) DSI reply buffer
897  *    rbuflen      (rw) current length of data in reply buffer
898  *    uname        (r) filename
899  *    oflag        (r) link and create flag
900  *    attruname    (r) name of attribute
901  *
902  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
903  *
904  * Effects:
905  *
906  * Copies EA size into rbuf in network order. Increments *rbuflen +4.
907  */
908 int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
909 {
910     int ret = AFPERR_MISC;
911     unsigned int count = 0;
912     uint32_t uint32;
913     struct ea ea;
914
915     LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
916
917     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
918         if (errno != ENOENT)
919             LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
920
921         memset(rbuf, 0, 4);
922         *rbuflen += 4;
923         return ret;
924     }
925
926     while (count < ea.ea_count) {
927         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
928             uint32 = htonl((*ea.ea_entries)[count].ea_size);
929             memcpy(rbuf, &uint32, 4);
930             *rbuflen += 4;
931             ret = AFP_OK;
932
933             LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
934                 attruname, (*ea.ea_entries)[count].ea_size);
935             break;
936         }
937         count++;
938     }
939
940     if ((ea_close(&ea)) != 0) {
941         LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
942         return AFPERR_MISC;
943     }
944
945     return ret;
946 }
947
948 /*
949  * Function: get_eacontent
950  *
951  * Purpose: copy EA into rbuf
952  *
953  * Arguments:
954  *
955  *    vol          (r) current volume
956  *    rbuf         (w) DSI reply buffer
957  *    rbuflen      (rw) current length of data in reply buffer
958  *    uname        (r) filename
959  *    oflag        (r) link and create flag
960  *    attruname    (r) name of attribute
961  *    maxreply     (r) maximum EA size as of current specs/real-life
962  *
963  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
964  *
965  * Effects:
966  *
967  * Copies EA into rbuf. Increments *rbuflen accordingly.
968  */
969 int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
970 {
971     int ret = AFPERR_MISC, fd = -1;
972     unsigned int count = 0;
973     uint32_t uint32;
974     size_t toread;
975     struct ea ea;
976     char *eafile;
977
978     LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
979
980     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
981         if (errno != ENOENT)
982             LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
983         memset(rbuf, 0, 4);
984         *rbuflen += 4;
985         return ret;
986     }
987
988     while (count < ea.ea_count) {
989         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
990             if ( (eafile = ea_path(&ea, attruname)) == NULL) {
991                 ret = AFPERR_MISC;
992                 break;
993             }
994
995             if ((fd = open(eafile, O_RDONLY)) == -1) {
996                 ret = AFPERR_MISC;
997                 break;
998             }
999
1000             /* Check how much the client wants, give him what we think is right */
1001             maxreply -= MAX_REPLY_EXTRA_BYTES;
1002             if (maxreply > MAX_EA_SIZE)
1003                 maxreply = MAX_EA_SIZE;
1004             toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
1005             LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
1006
1007             /* Put length of EA data in reply buffer */
1008             uint32 = htonl(toread);
1009             memcpy(rbuf, &uint32, 4);
1010             rbuf += 4;
1011             *rbuflen += 4;
1012
1013             if (read(fd, rbuf, toread) != (ssize_t)toread) {
1014                 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1015                 ret = AFPERR_MISC;
1016                 break;
1017             }
1018             *rbuflen += toread;
1019             close(fd);
1020
1021             ret = AFP_OK;
1022             break;
1023         }
1024         count++;
1025     }
1026
1027     if ((ea_close(&ea)) != 0) {
1028         LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
1029         return AFPERR_MISC;
1030     }
1031
1032     return ret;
1033
1034 }
1035
1036 /*
1037  * Function: list_eas
1038  *
1039  * Purpose: copy names of EAs into attrnamebuf
1040  *
1041  * Arguments:
1042  *
1043  *    vol          (r) current volume
1044  *    attrnamebuf  (w) store names a consecutive C strings here
1045  *    buflen       (rw) length of names in attrnamebuf
1046  *    uname        (r) filename
1047  *    oflag        (r) link and create flag
1048  *
1049  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1050  *
1051  * Effects:
1052  *
1053  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1054  * Increments *buflen accordingly.
1055  */
1056 int list_eas(VFS_FUNC_ARGS_EA_LIST)
1057 {
1058     unsigned int count = 0;
1059     int attrbuflen = *buflen, ret = AFP_OK, len;
1060     char *buf = attrnamebuf;
1061     struct ea ea;
1062
1063     LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1064
1065     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1066         if (errno != ENOENT) {
1067             LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1068             return AFPERR_MISC;
1069         }
1070         else
1071             return AFP_OK;
1072     }
1073
1074     while (count < ea.ea_count) {
1075         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1076         if ( ( len = convert_string(vol->v_volcharset,
1077                                     CH_UTF8_MAC, 
1078                                     (*ea.ea_entries)[count].ea_name,
1079                                     (*ea.ea_entries)[count].ea_namelen,
1080                                     buf + attrbuflen,
1081                                     255))
1082              <= 0 ) {
1083             ret = AFPERR_MISC;
1084             goto exit;
1085         }
1086         if (len == 255)
1087             /* convert_string didn't 0-terminate */
1088             attrnamebuf[attrbuflen + 255] = 0;
1089
1090         LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1091             uname, (*ea.ea_entries)[count].ea_name);
1092
1093         attrbuflen += len + 1;
1094         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1095             /* Next EA name could overflow, so bail out with error.
1096                FIXME: evantually malloc/memcpy/realloc whatever.
1097                Is it worth it ? */
1098             LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1099             ret = AFPERR_MISC;
1100             goto exit;
1101         }
1102         count++;
1103     }
1104
1105 exit:
1106     *buflen = attrbuflen;
1107
1108     if ((ea_close(&ea)) != 0) {
1109         LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1110         return AFPERR_MISC;
1111     }
1112
1113     return ret;
1114 }
1115
1116 /*
1117  * Function: set_ea
1118  *
1119  * Purpose: set a Solaris native EA
1120  *
1121  * Arguments:
1122  *
1123  *    vol          (r) current volume
1124  *    uname        (r) filename
1125  *    attruname    (r) EA name
1126  *    ibuf         (r) buffer with EA content
1127  *    attrsize     (r) length EA in ibuf
1128  *    oflag        (r) link and create flag
1129  *
1130  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1131  *
1132  * Effects:
1133  *
1134  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1135  * Increments *rbuflen accordingly.
1136  */
1137 int set_ea(VFS_FUNC_ARGS_EA_SET)
1138 {
1139     int ret = AFP_OK;
1140     struct ea ea;
1141
1142     LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1143
1144     if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1145         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1146         return AFPERR_MISC;
1147     }
1148
1149     if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1150         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1151         ret = AFPERR_MISC;
1152         goto exit;
1153     }
1154
1155     if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1156         LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1157         ret = AFPERR_MISC;
1158         goto exit;
1159     }
1160
1161 exit:
1162     if ((ea_close(&ea)) != 0) {
1163         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1164         ret = AFPERR_MISC;
1165         goto exit;
1166     }
1167
1168     return ret;
1169 }
1170
1171 /*
1172  * Function: remove_ea
1173  *
1174  * Purpose: remove a EA from a file
1175  *
1176  * Arguments:
1177  *
1178  *    vol          (r) current volume
1179  *    uname        (r) filename
1180  *    attruname    (r) EA name
1181  *    oflag        (r) link and create flag
1182  *
1183  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1184  *
1185  * Effects:
1186  *
1187  * Removes EA attruname from file uname.
1188  */
1189 int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
1190 {
1191     int ret = AFP_OK;
1192     struct ea ea;
1193
1194     LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1195
1196     if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1197         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1198         return AFPERR_MISC;
1199     }
1200
1201     if ((ea_delentry(&ea, attruname)) == -1) {
1202         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1203         ret = AFPERR_MISC;
1204         goto exit;
1205     }
1206
1207     if ((delete_ea_file(&ea, attruname)) != 0) {
1208         LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1209         ret = AFPERR_MISC;
1210         goto exit;
1211     }
1212
1213 exit:
1214     if ((ea_close(&ea)) != 0) {
1215         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1216         ret = AFPERR_MISC;
1217         goto exit;
1218     }
1219
1220     return ret;
1221 }
1222
1223 /******************************************************************************************
1224  * EA VFS funcs that deal with file/dir cp/mv/rm
1225  ******************************************************************************************/
1226
1227 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1228 {
1229     unsigned int count = 0;
1230     int ret = AFP_OK;
1231     struct ea ea;
1232
1233     LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1234
1235     /* Open EA stuff */
1236     if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
1237         if (errno == ENOENT)
1238             /* no EA files, nothing to do */
1239             return AFP_OK;
1240         else {
1241             LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1242             return AFPERR_MISC;
1243         }
1244     }
1245
1246     while (count < ea.ea_count) {
1247         if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1248             ret = AFPERR_MISC;
1249             continue;
1250         }
1251         free((*ea.ea_entries)[count].ea_name);
1252         (*ea.ea_entries)[count].ea_name = NULL;
1253         count++;
1254     }
1255
1256     /* ea_close removes the EA header file for us because all names are NULL */
1257     if ((ea_close(&ea)) != 0) {
1258         LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1259         return AFPERR_MISC;
1260     }
1261
1262     return ret;
1263 }
1264
1265 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1266 {
1267     unsigned int count = 0;
1268     int    ret = AFP_OK;
1269     size_t easize;
1270     char   srceapath[ MAXPATHLEN + 1];
1271     char   *eapath;
1272     char   *eaname;
1273     struct ea srcea;
1274     struct ea dstea;
1275     struct adouble ad;
1276
1277     LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1278             
1279
1280     /* Open EA stuff */
1281     if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1282         if (errno == ENOENT)
1283             /* no EA files, nothing to do */
1284             return AFP_OK;
1285         else {
1286             LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1287             return AFPERR_MISC;
1288         }
1289     }
1290
1291     if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1292         if (errno == ENOENT) {
1293             /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1294             ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
1295             if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1296                 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1297                 ret = AFPERR_MISC;
1298                 goto exit;
1299             }
1300             ad_close(&ad, ADFLAGS_HF);
1301             if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1302                 ret = AFPERR_MISC;
1303                 goto exit;
1304             }
1305         }
1306     }
1307
1308     /* Loop through all EAs: */
1309     while (count < srcea.ea_count) {
1310         /* Move EA */
1311         eaname = (*srcea.ea_entries)[count].ea_name;
1312         easize = (*srcea.ea_entries)[count].ea_size;
1313
1314         /* Build src and dst paths for rename() */
1315         if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1316             ret = AFPERR_MISC;
1317             goto exit;
1318         }
1319         strcpy(srceapath, eapath);
1320         if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1321             ret = AFPERR_MISC;
1322             goto exit;
1323         }
1324
1325         LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1326             src, dst, srceapath, eapath);
1327
1328         /* Add EA to dstea */
1329         if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1330             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1331                 src, dst, srceapath, eapath);
1332             ret = AFPERR_MISC;
1333             goto exit;
1334         }
1335
1336         /* Remove EA entry from srcea */
1337         if ((ea_delentry(&srcea, eaname)) == -1) {
1338             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1339                 src, dst, srceapath, eapath);
1340             ea_delentry(&dstea, eaname);
1341             ret = AFPERR_MISC;
1342             goto exit;
1343         }
1344
1345         /* Now rename the EA */
1346         if ((rename( srceapath, eapath)) < 0) {
1347             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1348                 src, dst, srceapath, eapath);
1349             ret = AFPERR_MISC;
1350             goto exit;
1351         }
1352
1353         count++;
1354     }
1355
1356
1357 exit:
1358     ea_close(&srcea);
1359     ea_close(&dstea);
1360         return ret;
1361 }
1362
1363 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1364 {
1365     unsigned int count = 0;
1366     int    ret = AFP_OK;
1367     size_t easize;
1368     char   srceapath[ MAXPATHLEN + 1];
1369     char   *eapath;
1370     char   *eaname;
1371     struct ea srcea;
1372     struct ea dstea;
1373     struct adouble ad;
1374
1375     LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1376
1377     /* Open EA stuff */
1378     if ((ea_open(vol, 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_copyfile('%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_copyfile('%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         /* Copy 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 copy_file() */
1412         if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1413             ret = AFPERR_MISC;
1414             goto exit;
1415         }
1416         strcpy(srceapath, eapath);
1417         if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1418             ret = AFPERR_MISC;
1419             goto exit;
1420         }
1421
1422         LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying 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_copyfile('%s/%s'): ea_addentry('%s') error",
1428                 src, dst, eaname);
1429             ret = AFPERR_MISC;
1430             goto exit;
1431         }
1432
1433         /* Now copy the EA */
1434         if ((copy_file( srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1435             LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1436                 src, dst, srceapath, eapath);
1437             ret = AFPERR_MISC;
1438             goto exit;
1439         }
1440
1441         count++;
1442     }
1443
1444 exit:
1445     ea_close(&srcea);
1446     ea_close(&dstea);
1447         return ret;
1448 }
1449
1450 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1451 {
1452
1453     unsigned int count = 0;
1454     int ret = AFP_OK;
1455     char *eaname;
1456     struct ea ea;
1457
1458     LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1459     /* Open EA stuff */
1460     if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1461         if (errno == ENOENT)
1462             /* no EA files, nothing to do */
1463             return AFP_OK;
1464         else {
1465             LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1466             return AFPERR_MISC;
1467         }
1468     }
1469
1470     if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
1471         switch (errno) {
1472         case EPERM:
1473         case EACCES:
1474             ret = AFPERR_ACCESS;
1475             goto exit;
1476         default:
1477             ret = AFPERR_MISC;
1478             goto exit;
1479         }
1480     }
1481
1482     while (count < ea.ea_count) {
1483         if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1484             ret = AFPERR_MISC;
1485             goto exit;
1486         }
1487         if ((chown(eaname, uid, gid)) != 0) {
1488             switch (errno) {
1489             case EPERM:
1490             case EACCES:
1491                 ret = AFPERR_ACCESS;
1492                 goto exit;
1493             default:
1494                 ret = AFPERR_MISC;
1495                 goto exit;
1496             }
1497             continue;
1498         }
1499
1500         count++;
1501     }
1502
1503 exit:
1504     if ((ea_close(&ea)) != 0) {
1505         LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
1506         return AFPERR_MISC;
1507     }
1508
1509     return ret;
1510 }
1511
1512 int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
1513 {
1514
1515     unsigned int count = 0;
1516     int ret = AFP_OK;
1517     const char *eaname;
1518     struct ea ea;
1519
1520     LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
1521     /* Open EA stuff */
1522     if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1523         if (errno == ENOENT)
1524             /* no EA files, nothing to do */
1525             return AFP_OK;
1526         else
1527             return AFPERR_MISC;
1528     }
1529
1530     /* Set mode on EA header file */
1531     if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1532         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1533         switch (errno) {
1534         case EPERM:
1535         case EACCES:
1536             ret = AFPERR_ACCESS;
1537             goto exit;
1538         default:
1539             ret = AFPERR_MISC;
1540             goto exit;
1541         }
1542     }
1543
1544     /* Set mode on EA files */
1545     while (count < ea.ea_count) {
1546         if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1547             ret = AFPERR_MISC;
1548             goto exit;
1549         }
1550         if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1551             LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
1552             switch (errno) {
1553             case EPERM:
1554             case EACCES:
1555                 ret = AFPERR_ACCESS;
1556                 goto exit;
1557             default:
1558                 ret = AFPERR_MISC;
1559                 goto exit;
1560             }
1561             continue;
1562         }
1563
1564         count++;
1565     }
1566
1567 exit:
1568     if ((ea_close(&ea)) != 0) {
1569         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
1570         return AFPERR_MISC;
1571     }
1572
1573     return ret;
1574 }
1575
1576 int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
1577 {
1578
1579     int ret = AFP_OK;
1580     unsigned int count = 0;
1581     uid_t uid;
1582     const char *eaname;
1583     const char *eaname_safe = NULL;
1584     struct ea ea;
1585
1586     LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
1587     /* .AppleDouble already might be inaccesible, so we must run as id 0 */
1588     uid = geteuid();
1589     if (seteuid(0)) {
1590         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
1591         return AFPERR_MISC;
1592     }
1593
1594     /* Open EA stuff */
1595     if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1596         /* ENOENT --> no EA files, nothing to do */
1597         if (errno != ENOENT)
1598             ret = AFPERR_MISC;
1599         if (seteuid(uid) < 0) {
1600             LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1601             exit(EXITERR_SYS);
1602         }
1603         return ret;
1604     }
1605
1606     /* Set mode on EA header */
1607     if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1608         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1609         switch (errno) {
1610         case EPERM:
1611         case EACCES:
1612             ret = AFPERR_ACCESS;
1613             goto exit;
1614         default:
1615             ret = AFPERR_MISC;
1616             goto exit;
1617         }
1618     }
1619
1620     /* Set mode on EA files */
1621     while (count < ea.ea_count) {
1622         eaname = (*ea.ea_entries)[count].ea_name;
1623         /*
1624          * Be careful with EA names from the EA header!
1625          * Eg NFS users might have access to them, can inject paths using ../ or /.....
1626          * FIXME:
1627          * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
1628          */
1629         if ((eaname_safe = strrchr(eaname, '/'))) {
1630             LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
1631             eaname = eaname_safe;
1632         }
1633         if ((eaname = ea_path(&ea, eaname)) == NULL) {
1634             ret = AFPERR_MISC;
1635             goto exit;
1636         }
1637         if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1638             LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
1639             switch (errno) {
1640             case EPERM:
1641             case EACCES:
1642                 ret = AFPERR_ACCESS;
1643                 goto exit;
1644             default:
1645                 ret = AFPERR_MISC;
1646                 goto exit;
1647             }
1648             continue;
1649         }
1650
1651         count++;
1652     }
1653
1654 exit:
1655     if (seteuid(uid) < 0) {
1656         LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1657         exit(EXITERR_SYS);
1658     }
1659
1660     if ((ea_close(&ea)) != 0) {
1661         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);
1662         return AFPERR_MISC;
1663     }
1664
1665     return ret;
1666 }