]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea.c
35048a3cc5cfa8dd43e2989aa103ded410508e83
[netatalk.git] / libatalk / vfs / ea.c
1 /*
2   $Id: ea.c,v 1.15 2009-11-18 08:02:33 didg 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_FORCE;
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         LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
919         return AFPERR_MISC;
920     }
921
922     while (count < ea.ea_count) {
923         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
924             uint32 = htonl((*ea.ea_entries)[count].ea_size);
925             memcpy(rbuf, &uint32, 4);
926             *rbuflen += 4;
927             ret = AFP_OK;
928
929             LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
930                 attruname, (*ea.ea_entries)[count].ea_size);
931             break;
932         }
933         count++;
934     }
935
936     if ((ea_close(&ea)) != 0) {
937         LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
938         return AFPERR_MISC;
939     }
940
941     return ret;
942 }
943
944 /*
945  * Function: get_eacontent
946  *
947  * Purpose: copy EA into rbuf
948  *
949  * Arguments:
950  *
951  *    vol          (r) current volume
952  *    rbuf         (w) DSI reply buffer
953  *    rbuflen      (rw) current length of data in reply buffer
954  *    uname        (r) filename
955  *    oflag        (r) link and create flag
956  *    attruname    (r) name of attribute
957  *    maxreply     (r) maximum EA size as of current specs/real-life
958  *
959  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
960  *
961  * Effects:
962  *
963  * Copies EA into rbuf. Increments *rbuflen accordingly.
964  */
965 int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
966 {
967     int ret = AFPERR_MISC, fd = -1;
968     unsigned int count = 0;
969     uint32_t uint32;
970     size_t toread;
971     struct ea ea;
972     char *eafile;
973
974     LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
975
976     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
977         LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
978         return AFPERR_MISC;
979     }
980
981     while (count < ea.ea_count) {
982         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
983             if ( (eafile = ea_path(&ea, attruname)) == NULL) {
984                 ret = AFPERR_MISC;
985                 break;
986             }
987
988             if ((fd = open(eafile, O_RDONLY)) == -1) {
989                 ret = AFPERR_MISC;
990                 break;
991             }
992
993             /* Check how much the client wants, give him what we think is right */
994             maxreply -= MAX_REPLY_EXTRA_BYTES;
995             if (maxreply > MAX_EA_SIZE)
996                 maxreply = MAX_EA_SIZE;
997             toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
998             LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
999
1000             /* Put length of EA data in reply buffer */
1001             uint32 = htonl(toread);
1002             memcpy(rbuf, &uint32, 4);
1003             rbuf += 4;
1004             *rbuflen += 4;
1005
1006             if (read(fd, rbuf, toread) != (ssize_t)toread) {
1007                 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1008                 ret = AFPERR_MISC;
1009                 break;
1010             }
1011             *rbuflen += toread;
1012             close(fd);
1013
1014             ret = AFP_OK;
1015             break;
1016         }
1017         count++;
1018     }
1019
1020     if ((ea_close(&ea)) != 0) {
1021         LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
1022         return AFPERR_MISC;
1023     }
1024
1025     return ret;
1026
1027 }
1028
1029 /*
1030  * Function: list_eas
1031  *
1032  * Purpose: copy names of EAs into attrnamebuf
1033  *
1034  * Arguments:
1035  *
1036  *    vol          (r) current volume
1037  *    attrnamebuf  (w) store names a consecutive C strings here
1038  *    buflen       (rw) length of names in attrnamebuf
1039  *    uname        (r) filename
1040  *    oflag        (r) link and create flag
1041  *
1042  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1043  *
1044  * Effects:
1045  *
1046  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1047  * Increments *buflen accordingly.
1048  */
1049 int list_eas(VFS_FUNC_ARGS_EA_LIST)
1050 {
1051     unsigned int count = 0;
1052     int attrbuflen = *buflen, ret = AFP_OK, len;
1053     char *buf = attrnamebuf;
1054     struct ea ea;
1055
1056     LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1057
1058     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1059         if (errno != ENOENT) {
1060             LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1061             return AFPERR_MISC;
1062         }
1063         else
1064             return AFP_OK;
1065     }
1066
1067     while (count < ea.ea_count) {
1068         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1069         if ( ( len = convert_string(vol->v_volcharset,
1070                                     CH_UTF8_MAC, 
1071                                     (*ea.ea_entries)[count].ea_name,
1072                                     (*ea.ea_entries)[count].ea_namelen,
1073                                     buf + attrbuflen,
1074                                     255))
1075              <= 0 ) {
1076             ret = AFPERR_MISC;
1077             goto exit;
1078         }
1079         if (len == 255)
1080             /* convert_string didn't 0-terminate */
1081             attrnamebuf[attrbuflen + 255] = 0;
1082
1083         LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1084             uname, (*ea.ea_entries)[count].ea_name);
1085
1086         attrbuflen += len + 1;
1087         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1088             /* Next EA name could overflow, so bail out with error.
1089                FIXME: evantually malloc/memcpy/realloc whatever.
1090                Is it worth it ? */
1091             LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1092             ret = AFPERR_MISC;
1093             goto exit;
1094         }
1095         count++;
1096     }
1097
1098 exit:
1099     *buflen = attrbuflen;
1100
1101     if ((ea_close(&ea)) != 0) {
1102         LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1103         return AFPERR_MISC;
1104     }
1105
1106     return ret;
1107 }
1108
1109 /*
1110  * Function: set_ea
1111  *
1112  * Purpose: set a Solaris native EA
1113  *
1114  * Arguments:
1115  *
1116  *    vol          (r) current volume
1117  *    uname        (r) filename
1118  *    attruname    (r) EA name
1119  *    ibuf         (r) buffer with EA content
1120  *    attrsize     (r) length EA in ibuf
1121  *    oflag        (r) link and create flag
1122  *
1123  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1124  *
1125  * Effects:
1126  *
1127  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1128  * Increments *rbuflen accordingly.
1129  */
1130 int set_ea(VFS_FUNC_ARGS_EA_SET)
1131 {
1132     int ret = AFP_OK;
1133     struct ea ea;
1134
1135     LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1136
1137     if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1138         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1139         return AFPERR_MISC;
1140     }
1141
1142     if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1143         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1144         ret = AFPERR_MISC;
1145         goto exit;
1146     }
1147
1148     if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1149         LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1150         ret = AFPERR_MISC;
1151         goto exit;
1152     }
1153
1154 exit:
1155     if ((ea_close(&ea)) != 0) {
1156         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1157         ret = AFPERR_MISC;
1158         goto exit;
1159     }
1160
1161     return ret;
1162 }
1163
1164 /*
1165  * Function: remove_ea
1166  *
1167  * Purpose: remove a EA from a file
1168  *
1169  * Arguments:
1170  *
1171  *    vol          (r) current volume
1172  *    uname        (r) filename
1173  *    attruname    (r) EA name
1174  *    oflag        (r) link and create flag
1175  *
1176  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1177  *
1178  * Effects:
1179  *
1180  * Removes EA attruname from file uname.
1181  */
1182 int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
1183 {
1184     int ret = AFP_OK;
1185     struct ea ea;
1186
1187     LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1188
1189     if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1190         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1191         return AFPERR_MISC;
1192     }
1193
1194     if ((ea_delentry(&ea, attruname)) == -1) {
1195         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1196         ret = AFPERR_MISC;
1197         goto exit;
1198     }
1199
1200     if ((delete_ea_file(&ea, attruname)) != 0) {
1201         LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1202         ret = AFPERR_MISC;
1203         goto exit;
1204     }
1205
1206 exit:
1207     if ((ea_close(&ea)) != 0) {
1208         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1209         ret = AFPERR_MISC;
1210         goto exit;
1211     }
1212
1213     return ret;
1214 }
1215
1216 /******************************************************************************************
1217  * EA VFS funcs that deal with file/dir cp/mv/rm
1218  ******************************************************************************************/
1219
1220 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1221 {
1222     unsigned int count = 0;
1223     int ret = AFP_OK;
1224     struct ea ea;
1225
1226     LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1227
1228     /* Open EA stuff */
1229     if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
1230         if (errno == ENOENT)
1231             /* no EA files, nothing to do */
1232             return AFP_OK;
1233         else {
1234             LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1235             return AFPERR_MISC;
1236         }
1237     }
1238
1239     while (count < ea.ea_count) {
1240         if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1241             ret = AFPERR_MISC;
1242             continue;
1243         }
1244         free((*ea.ea_entries)[count].ea_name);
1245         (*ea.ea_entries)[count].ea_name = NULL;
1246         count++;
1247     }
1248
1249     /* ea_close removes the EA header file for us because all names are NULL */
1250     if ((ea_close(&ea)) != 0) {
1251         LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1252         return AFPERR_MISC;
1253     }
1254
1255     return ret;
1256 }
1257
1258 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1259 {
1260     unsigned int count = 0;
1261     int    ret = AFP_OK;
1262     size_t easize;
1263     char   srceapath[ MAXPATHLEN + 1];
1264     char   *eapath;
1265     char   *eaname;
1266     struct ea srcea;
1267     struct ea dstea;
1268     struct adouble ad;
1269
1270     LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1271             
1272
1273     /* Open EA stuff */
1274     if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1275         if (errno == ENOENT)
1276             /* no EA files, nothing to do */
1277             return AFP_OK;
1278         else {
1279             LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1280             return AFPERR_MISC;
1281         }
1282     }
1283
1284     if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1285         if (errno == ENOENT) {
1286             /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1287             ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
1288             if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1289                 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1290                 ret = AFPERR_MISC;
1291                 goto exit;
1292             }
1293             ad_close(&ad, ADFLAGS_HF);
1294             if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1295                 ret = AFPERR_MISC;
1296                 goto exit;
1297             }
1298         }
1299     }
1300
1301     /* Loop through all EAs: */
1302     while (count < srcea.ea_count) {
1303         /* Move EA */
1304         eaname = (*srcea.ea_entries)[count].ea_name;
1305         easize = (*srcea.ea_entries)[count].ea_size;
1306
1307         /* Build src and dst paths for rename() */
1308         if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1309             ret = AFPERR_MISC;
1310             goto exit;
1311         }
1312         strcpy(srceapath, eapath);
1313         if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1314             ret = AFPERR_MISC;
1315             goto exit;
1316         }
1317
1318         LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1319             src, dst, srceapath, eapath);
1320
1321         /* Add EA to dstea */
1322         if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1323             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1324                 src, dst, srceapath, eapath);
1325             ret = AFPERR_MISC;
1326             goto exit;
1327         }
1328
1329         /* Remove EA entry from srcea */
1330         if ((ea_delentry(&srcea, eaname)) == -1) {
1331             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1332                 src, dst, srceapath, eapath);
1333             ea_delentry(&dstea, eaname);
1334             ret = AFPERR_MISC;
1335             goto exit;
1336         }
1337
1338         /* Now rename the EA */
1339         if ((rename( srceapath, eapath)) < 0) {
1340             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1341                 src, dst, srceapath, eapath);
1342             ret = AFPERR_MISC;
1343             goto exit;
1344         }
1345
1346         count++;
1347     }
1348
1349
1350 exit:
1351     ea_close(&srcea);
1352     ea_close(&dstea);
1353         return ret;
1354 }
1355
1356 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1357 {
1358     unsigned int count = 0;
1359     int    ret = AFP_OK;
1360     size_t easize;
1361     char   srceapath[ MAXPATHLEN + 1];
1362     char   *eapath;
1363     char   *eaname;
1364     struct ea srcea;
1365     struct ea dstea;
1366     struct adouble ad;
1367
1368     LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1369
1370     /* Open EA stuff */
1371     if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1372         if (errno == ENOENT)
1373             /* no EA files, nothing to do */
1374             return AFP_OK;
1375         else {
1376             LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1377             return AFPERR_MISC;
1378         }
1379     }
1380
1381     if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1382         if (errno == ENOENT) {
1383             /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1384             ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
1385             if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1386                 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1387                 ret = AFPERR_MISC;
1388                 goto exit;
1389             }
1390             ad_close(&ad, ADFLAGS_HF);
1391             if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1392                 ret = AFPERR_MISC;
1393                 goto exit;
1394             }
1395         }
1396     }
1397
1398     /* Loop through all EAs: */
1399     while (count < srcea.ea_count) {
1400         /* Copy EA */
1401         eaname = (*srcea.ea_entries)[count].ea_name;
1402         easize = (*srcea.ea_entries)[count].ea_size;
1403
1404         /* Build src and dst paths for copy_file() */
1405         if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1406             ret = AFPERR_MISC;
1407             goto exit;
1408         }
1409         strcpy(srceapath, eapath);
1410         if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1411             ret = AFPERR_MISC;
1412             goto exit;
1413         }
1414
1415         LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1416             src, dst, srceapath, eapath);
1417
1418         /* Add EA to dstea */
1419         if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1420             LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
1421                 src, dst, eaname);
1422             ret = AFPERR_MISC;
1423             goto exit;
1424         }
1425
1426         /* Now copy the EA */
1427         if ((copy_file( srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1428             LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1429                 src, dst, srceapath, eapath);
1430             ret = AFPERR_MISC;
1431             goto exit;
1432         }
1433
1434         count++;
1435     }
1436
1437 exit:
1438     ea_close(&srcea);
1439     ea_close(&dstea);
1440         return ret;
1441 }
1442
1443 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1444 {
1445
1446     unsigned int count = 0;
1447     int ret = AFP_OK;
1448     char *eaname;
1449     struct ea ea;
1450
1451     LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1452     /* Open EA stuff */
1453     if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1454         if (errno == ENOENT)
1455             /* no EA files, nothing to do */
1456             return AFP_OK;
1457         else {
1458             LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1459             return AFPERR_MISC;
1460         }
1461     }
1462
1463     if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
1464         switch (errno) {
1465         case EPERM:
1466         case EACCES:
1467             ret = AFPERR_ACCESS;
1468             goto exit;
1469         default:
1470             ret = AFPERR_MISC;
1471             goto exit;
1472         }
1473     }
1474
1475     while (count < ea.ea_count) {
1476         if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1477             ret = AFPERR_MISC;
1478             goto exit;
1479         }
1480         if ((chown(eaname, uid, gid)) != 0) {
1481             switch (errno) {
1482             case EPERM:
1483             case EACCES:
1484                 ret = AFPERR_ACCESS;
1485                 goto exit;
1486             default:
1487                 ret = AFPERR_MISC;
1488                 goto exit;
1489             }
1490             continue;
1491         }
1492
1493         count++;
1494     }
1495
1496 exit:
1497     if ((ea_close(&ea)) != 0) {
1498         LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
1499         return AFPERR_MISC;
1500     }
1501
1502     return ret;
1503 }
1504
1505 int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
1506 {
1507
1508     unsigned int count = 0;
1509     int ret = AFP_OK;
1510     const char *eaname;
1511     struct ea ea;
1512
1513     LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
1514     /* Open EA stuff */
1515     if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1516         if (errno == ENOENT)
1517             /* no EA files, nothing to do */
1518             return AFP_OK;
1519         else
1520             return AFPERR_MISC;
1521     }
1522
1523     /* Set mode on EA header file */
1524     if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1525         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1526         switch (errno) {
1527         case EPERM:
1528         case EACCES:
1529             ret = AFPERR_ACCESS;
1530             goto exit;
1531         default:
1532             ret = AFPERR_MISC;
1533             goto exit;
1534         }
1535     }
1536
1537     /* Set mode on EA files */
1538     while (count < ea.ea_count) {
1539         if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1540             ret = AFPERR_MISC;
1541             goto exit;
1542         }
1543         if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1544             LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
1545             switch (errno) {
1546             case EPERM:
1547             case EACCES:
1548                 ret = AFPERR_ACCESS;
1549                 goto exit;
1550             default:
1551                 ret = AFPERR_MISC;
1552                 goto exit;
1553             }
1554             continue;
1555         }
1556
1557         count++;
1558     }
1559
1560 exit:
1561     if ((ea_close(&ea)) != 0) {
1562         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
1563         return AFPERR_MISC;
1564     }
1565
1566     return ret;
1567 }
1568
1569 int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
1570 {
1571
1572     int ret = AFP_OK;
1573     unsigned int count = 0;
1574     uid_t uid;
1575     const char *eaname;
1576     const char *eaname_safe = NULL;
1577     struct ea ea;
1578
1579     LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
1580     /* .AppleDouble already might be inaccesible, so we must run as id 0 */
1581     uid = geteuid();
1582     if (seteuid(0)) {
1583         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
1584         return AFPERR_MISC;
1585     }
1586
1587     /* Open EA stuff */
1588     if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1589         /* ENOENT --> no EA files, nothing to do */
1590         if (errno != ENOENT)
1591             ret = AFPERR_MISC;
1592         if (seteuid(uid) < 0) {
1593             LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1594             exit(EXITERR_SYS);
1595         }
1596         return ret;
1597     }
1598
1599     /* Set mode on EA header */
1600     if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1601         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1602         switch (errno) {
1603         case EPERM:
1604         case EACCES:
1605             ret = AFPERR_ACCESS;
1606             goto exit;
1607         default:
1608             ret = AFPERR_MISC;
1609             goto exit;
1610         }
1611     }
1612
1613     /* Set mode on EA files */
1614     while (count < ea.ea_count) {
1615         eaname = (*ea.ea_entries)[count].ea_name;
1616         /*
1617          * Be careful with EA names from the EA header!
1618          * Eg NFS users might have access to them, can inject paths using ../ or /.....
1619          * FIXME:
1620          * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
1621          */
1622         if ((eaname_safe = strrchr(eaname, '/'))) {
1623             LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
1624             eaname = eaname_safe;
1625         }
1626         if ((eaname = ea_path(&ea, eaname)) == NULL) {
1627             ret = AFPERR_MISC;
1628             goto exit;
1629         }
1630         if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1631             LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
1632             switch (errno) {
1633             case EPERM:
1634             case EACCES:
1635                 ret = AFPERR_ACCESS;
1636                 goto exit;
1637             default:
1638                 ret = AFPERR_MISC;
1639                 goto exit;
1640             }
1641             continue;
1642         }
1643
1644         count++;
1645     }
1646
1647 exit:
1648     if (seteuid(uid) < 0) {
1649         LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1650         exit(EXITERR_SYS);
1651     }
1652
1653     if ((ea_close(&ea)) != 0) {
1654         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);
1655         return AFPERR_MISC;
1656     }
1657
1658     return ret;
1659 }