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