]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea.c
Begin Solaris EA VFS cleanup
[netatalk.git] / libatalk / vfs / ea.c
1 /*
2   $Id: ea.c,v 1.10 2009-10-23 14:09:51 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->vfs->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 check for errors, eg when removing the file is already gone */
658     stat(uname, &st);
659     if (S_ISDIR(st.st_mode))
660         ea->ea_flags |=  EA_DIR;
661
662     if ( ! (ea->filename = strdup(uname))) {
663         LOG(log_error, logtype_afpd, "ea_open: OOM");
664         return -1;
665     }
666
667     eaname = ea_path(ea, NULL);
668     LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
669
670     /* Check if it exists, if not create it if EA_CREATE is in eaflags */
671     if ((stat(eaname, &st)) != 0) {
672         if (errno == ENOENT) {
673
674             /* It doesnt exist */
675
676             if ( ! (eaflags & EA_CREATE)) {
677                 /* creation was not requested, so return with error */
678                 ret = -1;
679                 goto exit;
680             }
681
682             /* Now create a header file */
683
684             /* malloc buffer for minimal on disk data */
685             ea->ea_data = malloc(EA_HEADER_SIZE);
686             if (! ea->ea_data) {
687                 LOG(log_error, logtype_afpd, "ea_open: OOM");
688                 ret = -1;
689                 goto exit;
690             }
691
692             /* create it */
693             ea->ea_fd = create_ea_header(eaname, ea);
694             if (ea->ea_fd == -1) {
695                 ret = -1;
696                 goto exit;
697             }
698
699             return 0;
700
701         } else {/* errno != ENOENT */
702             ret = -1;
703             goto exit;
704         }
705     }
706
707     /* header file exists, so read and parse it */
708
709     /* malloc buffer where we read disk file into */
710     ea->ea_size = st.st_size;
711     ea->ea_data = malloc(st.st_size);
712     if (! ea->ea_data) {
713         LOG(log_error, logtype_afpd, "ea_open: OOM");
714         ret = -1;
715         goto exit;
716     }
717
718     /* Now lock, open and read header file from disk */
719     if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
720         LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno));
721         ret = -1;
722         goto exit;
723     }
724
725     /* lock it */
726     if (ea->ea_flags & EA_RDONLY) {
727         /* read lock */
728         if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
729             LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
730             ret = -1;
731             goto exit;
732         }
733     } else {  /* EA_RDWR */
734         /* write lock */
735         if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
736             LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
737             ret = -1;
738             goto exit;
739         }
740     }
741
742     /* read it */
743     if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) {
744         LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
745         ret = -1;
746         goto exit;
747     }
748
749     if ((unpack_header(ea)) != 0) {
750         LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
751         ret = -1;
752         goto exit;
753     }
754
755 exit:
756     if (ret == 0) {
757         ea->ea_inited = EA_INITED;
758     } else {
759         if (ea->ea_data) {
760             free(ea->ea_data);
761             ea->ea_data = NULL;
762         }
763         if (ea->ea_fd) {
764             close(ea->ea_fd);
765             ea->ea_fd = -1;
766         }
767     }
768
769     return ret;
770 }
771
772 /*
773  * Function: ea_close
774  *
775  * Purpose: flushes and closes an ea handle
776  *
777  * Arguments:
778  *
779  *    ea          (rw) pointer to ea handle
780  *
781  * Returns: 0 on success, -1 on error
782  *
783  * Effects:
784  *
785  * Flushes and then closes and frees all resouces held by ea handle.
786  * Pack data in ea into ea_data, then write ea_data to disk
787  */
788 static int ea_close(struct ea * restrict ea)
789 {
790     int ret = 0, count = 0;
791     char *eaname;
792     struct stat st;
793
794     LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
795
796     if (ea->ea_inited != EA_INITED) {
797         LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
798         return 0;
799     }
800
801     /* pack header and write it to disk if it was opened EA_RDWR*/
802     if (ea->ea_flags & EA_RDWR) {
803         if ((pack_header(ea)) != 0) {
804             LOG(log_error, logtype_afpd, "ea_close: pack header");
805             ret = -1;
806         } else {
807             if (ea->ea_count == 0) {
808                 /* Check if EA header exists and remove it */
809                 eaname = ea_path(ea, NULL);
810                 if ((stat(eaname, &st)) == 0) {
811                     if ((unlink(eaname)) != 0) {
812                         LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
813                             eaname, strerror(errno));
814                         ret = -1;
815                     }
816                     else
817                         LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
818                 } else {
819                     /* stat error */
820                     if (errno != ENOENT) {
821                         LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
822                             eaname, strerror(errno));
823                         ret = -1;
824                     }
825                 }
826             } else { /* ea->ea_count > 0 */
827                 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
828                     LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
829                     ret = -1;
830                     goto exit;
831                 }
832
833                 if ((ftruncate(ea->ea_fd, 0)) == -1) {
834                     LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
835                     ret = -1;
836                     goto exit;
837                 }
838
839                 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) {
840                     LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
841                     ret = -1;
842                 }
843             }
844         }
845     }
846
847 exit:
848     /* free names */
849     while(count < ea->ea_count) {
850         if ( (*ea->ea_entries)[count].ea_name ) {
851             free((*ea->ea_entries)[count].ea_name);
852             (*ea->ea_entries)[count].ea_name = NULL;
853         }
854         count++;
855     }
856     ea->ea_count = 0;
857
858     if (ea->filename) {
859         free(ea->filename);
860         ea->filename = NULL;
861     }
862
863     if (ea->ea_entries) {
864         free(ea->ea_entries);
865         ea->ea_entries = NULL;
866     }
867
868     if (ea->ea_data) {
869         free(ea->ea_data);
870         ea->ea_data = NULL;
871     }
872     if (ea->ea_fd != -1) {
873         close(ea->ea_fd);       /* also releases the fcntl lock */
874         ea->ea_fd = -1;
875     }
876
877     return 0;
878 }
879
880
881
882 /************************************************************************************
883  * VFS funcs called from afp_ea* funcs
884  ************************************************************************************/
885
886 /*
887  * Function: get_easize
888  *
889  * Purpose: get size of an EA
890  *
891  * Arguments:
892  *
893  *    vol          (r) current volume
894  *    rbuf         (w) DSI reply buffer
895  *    rbuflen      (rw) current length of data in reply buffer
896  *    uname        (r) filename
897  *    oflag        (r) link and create flag
898  *    attruname    (r) name of attribute
899  *
900  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
901  *
902  * Effects:
903  *
904  * Copies EA size into rbuf in network order. Increments *rbuflen +4.
905  */
906 int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
907 {
908     int ret = AFPERR_MISC, count = 0;
909     uint32_t uint32;
910     struct ea ea;
911
912     LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
913
914     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
915         LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
916         return AFPERR_MISC;
917     }
918
919     while (count < ea.ea_count) {
920         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
921             uint32 = htonl((*ea.ea_entries)[count].ea_size);
922             memcpy(rbuf, &uint32, 4);
923             *rbuflen += 4;
924             ret = AFP_OK;
925
926             LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
927                 attruname, (*ea.ea_entries)[count].ea_size);
928             break;
929         }
930         count++;
931     }
932
933     if ((ea_close(&ea)) != 0) {
934         LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
935         return AFPERR_MISC;
936     }
937
938     return ret;
939 }
940
941 /*
942  * Function: get_eacontent
943  *
944  * Purpose: copy EA into rbuf
945  *
946  * Arguments:
947  *
948  *    vol          (r) current volume
949  *    rbuf         (w) DSI reply buffer
950  *    rbuflen      (rw) current length of data in reply buffer
951  *    uname        (r) filename
952  *    oflag        (r) link and create flag
953  *    attruname    (r) name of attribute
954  *    maxreply     (r) maximum EA size as of current specs/real-life
955  *
956  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
957  *
958  * Effects:
959  *
960  * Copies EA into rbuf. Increments *rbuflen accordingly.
961  */
962 int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
963 {
964     int ret = AFPERR_MISC, count = 0, fd = -1;
965     uint32_t uint32;
966     size_t toread;
967     struct ea ea;
968     char *eafile;
969
970     LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
971
972     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
973         LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
974         return AFPERR_MISC;
975     }
976
977     while (count < ea.ea_count) {
978         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
979             if ( (eafile = ea_path(&ea, attruname)) == NULL) {
980                 ret = AFPERR_MISC;
981                 break;
982             }
983
984             if ((fd = open(eafile, O_RDONLY)) == -1) {
985                 ret = AFPERR_MISC;
986                 break;
987             }
988
989             /* Check how much the client wants, give him what we think is right */
990             maxreply -= MAX_REPLY_EXTRA_BYTES;
991             if (maxreply > MAX_EA_SIZE)
992                 maxreply = MAX_EA_SIZE;
993             toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
994             LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
995
996             /* Put length of EA data in reply buffer */
997             uint32 = htonl(toread);
998             memcpy(rbuf, &uint32, 4);
999             rbuf += 4;
1000             *rbuflen += 4;
1001
1002             if ((read(fd, rbuf, toread)) != toread) {
1003                 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1004                 ret = AFPERR_MISC;
1005                 break;
1006             }
1007             *rbuflen += toread;
1008             close(fd);
1009
1010             ret = AFP_OK;
1011             break;
1012         }
1013         count++;
1014     }
1015
1016     if ((ea_close(&ea)) != 0) {
1017         LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
1018         return AFPERR_MISC;
1019     }
1020
1021     return ret;
1022
1023 }
1024
1025 /*
1026  * Function: list_eas
1027  *
1028  * Purpose: copy names of EAs into attrnamebuf
1029  *
1030  * Arguments:
1031  *
1032  *    vol          (r) current volume
1033  *    attrnamebuf  (w) store names a consecutive C strings here
1034  *    buflen       (rw) length of names in attrnamebuf
1035  *    uname        (r) filename
1036  *    oflag        (r) link and create flag
1037  *
1038  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1039  *
1040  * Effects:
1041  *
1042  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1043  * Increments *buflen accordingly.
1044  */
1045 int list_eas(VFS_FUNC_ARGS_EA_LIST)
1046 {
1047     int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
1048     char *buf = attrnamebuf;
1049     struct ea ea;
1050
1051     LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1052
1053     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1054         if (errno != ENOENT) {
1055             LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1056             return AFPERR_MISC;
1057         }
1058         else
1059             return AFP_OK;
1060     }
1061
1062     while (count < ea.ea_count) {
1063         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1064         if ( ( len = convert_string(vol->v_volcharset,
1065                                     CH_UTF8_MAC, 
1066                                     (*ea.ea_entries)[count].ea_name,
1067                                     (*ea.ea_entries)[count].ea_namelen,
1068                                     buf + attrbuflen,
1069                                     255))
1070              <= 0 ) {
1071             ret = AFPERR_MISC;
1072             goto exit;
1073         }
1074         if (len == 255)
1075             /* convert_string didn't 0-terminate */
1076             attrnamebuf[attrbuflen + 255] = 0;
1077
1078         LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1079             uname, (*ea.ea_entries)[count].ea_name);
1080
1081         attrbuflen += len + 1;
1082         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1083             /* Next EA name could overflow, so bail out with error.
1084                FIXME: evantually malloc/memcpy/realloc whatever.
1085                Is it worth it ? */
1086             LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1087             ret = AFPERR_MISC;
1088             goto exit;
1089         }
1090         count++;
1091     }
1092
1093 exit:
1094     *buflen = attrbuflen;
1095
1096     if ((ea_close(&ea)) != 0) {
1097         LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1098         return AFPERR_MISC;
1099     }
1100
1101     return ret;
1102 }
1103
1104 /*
1105  * Function: set_ea
1106  *
1107  * Purpose: set a Solaris native EA
1108  *
1109  * Arguments:
1110  *
1111  *    vol          (r) current volume
1112  *    uname        (r) filename
1113  *    attruname    (r) EA name
1114  *    ibuf         (r) buffer with EA content
1115  *    attrsize     (r) length EA in ibuf
1116  *    oflag        (r) link and create flag
1117  *
1118  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1119  *
1120  * Effects:
1121  *
1122  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1123  * Increments *rbuflen accordingly.
1124  */
1125 int set_ea(VFS_FUNC_ARGS_EA_SET)
1126 {
1127     int ret = AFP_OK;
1128     struct ea ea;
1129
1130     LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1131
1132     if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1133         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1134         return AFPERR_MISC;
1135     }
1136
1137     if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1138         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1139         ret = AFPERR_MISC;
1140         goto exit;
1141     }
1142
1143     if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1144         LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1145         ret = AFPERR_MISC;
1146         goto exit;
1147     }
1148
1149 exit:
1150     if ((ea_close(&ea)) != 0) {
1151         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1152         ret = AFPERR_MISC;
1153         goto exit;
1154     }
1155
1156     return ret;
1157 }
1158
1159 /*
1160  * Function: remove_ea
1161  *
1162  * Purpose: remove a EA from a file
1163  *
1164  * Arguments:
1165  *
1166  *    vol          (r) current volume
1167  *    uname        (r) filename
1168  *    attruname    (r) EA name
1169  *    oflag        (r) link and create flag
1170  *
1171  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1172  *
1173  * Effects:
1174  *
1175  * Removes EA attruname from file uname.
1176  */
1177 int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
1178 {
1179     int ret = AFP_OK;
1180     struct ea ea;
1181
1182     LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1183
1184     if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1185         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1186         return AFPERR_MISC;
1187     }
1188
1189     if ((ea_delentry(&ea, attruname)) == -1) {
1190         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1191         ret = AFPERR_MISC;
1192         goto exit;
1193     }
1194
1195     if ((delete_ea_file(&ea, attruname)) != 0) {
1196         LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1197         ret = AFPERR_MISC;
1198         goto exit;
1199     }
1200
1201 exit:
1202     if ((ea_close(&ea)) != 0) {
1203         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1204         ret = AFPERR_MISC;
1205         goto exit;
1206     }
1207
1208     return ret;
1209 }
1210
1211 /******************************************************************************************
1212  * EA VFS funcs that deal with file/dir cp/mv/rm
1213  ******************************************************************************************/
1214
1215 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1216 {
1217     LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1218
1219     int count = 0, ret = AFP_OK;
1220     struct ea ea;
1221
1222     /* Open EA stuff */
1223     if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
1224         if (errno == ENOENT)
1225             /* no EA files, nothing to do */
1226             return AFP_OK;
1227         else {
1228             LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1229             return AFPERR_MISC;
1230         }
1231     }
1232
1233     while (count < ea.ea_count) {
1234         if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1235             ret = AFPERR_MISC;
1236             continue;
1237         }
1238         free((*ea.ea_entries)[count].ea_name);
1239         (*ea.ea_entries)[count].ea_name = NULL;
1240         count++;
1241     }
1242
1243     /* ea_close removes the EA header file for us because all names are NULL */
1244     if ((ea_close(&ea)) != 0) {
1245         LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1246         return AFPERR_MISC;
1247     }
1248
1249     return ret;
1250 }
1251
1252 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1253 {
1254     int    count = 0;
1255     int    ret = AFP_OK;
1256     size_t easize;
1257     char   srceapath[ MAXPATHLEN + 1];
1258     char   *eapath;
1259     char   *eaname;
1260     struct ea srcea;
1261     struct ea dstea;
1262     struct adouble ad;
1263
1264     LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1265             
1266
1267     /* Open EA stuff */
1268     if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1269         if (errno == ENOENT)
1270             /* no EA files, nothing to do */
1271             return AFP_OK;
1272         else {
1273             LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1274             return AFPERR_MISC;
1275         }
1276     }
1277
1278     if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1279         if (errno == ENOENT) {
1280             /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1281             ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
1282             if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1283                 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1284                 ret = AFPERR_MISC;
1285                 goto exit;
1286             }
1287             ad_close(&ad, ADFLAGS_HF);
1288             if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1289                 ret = AFPERR_MISC;
1290                 goto exit;
1291             }
1292         }
1293     }
1294
1295     /* Loop through all EAs: */
1296     while (count < srcea.ea_count) {
1297         /* Move EA */
1298         eaname = (*srcea.ea_entries)[count].ea_name;
1299         easize = (*srcea.ea_entries)[count].ea_size;
1300
1301         /* Build src and dst paths for rename() */
1302         if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1303             ret = AFPERR_MISC;
1304             goto exit;
1305         }
1306         strcpy(srceapath, eapath);
1307         if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1308             ret = AFPERR_MISC;
1309             goto exit;
1310         }
1311
1312         LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1313             src, dst, srceapath, eapath);
1314
1315         /* Add EA to dstea */
1316         if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1317             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1318                 src, dst, srceapath, eapath);
1319             ret = AFPERR_MISC;
1320             goto exit;
1321         }
1322
1323         /* Remove EA entry from srcea */
1324         if ((ea_delentry(&srcea, eaname)) == -1) {
1325             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1326                 src, dst, srceapath, eapath);
1327             ea_delentry(&dstea, eaname);
1328             ret = AFPERR_MISC;
1329             goto exit;
1330         }
1331
1332         /* Now rename the EA */
1333         if ((rename( srceapath, eapath)) < 0) {
1334             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1335                 src, dst, srceapath, eapath);
1336             ret = AFPERR_MISC;
1337             goto exit;
1338         }
1339
1340         count++;
1341     }
1342
1343
1344 exit:
1345     ea_close(&srcea);
1346     ea_close(&dstea);
1347         return ret;
1348 }
1349
1350 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1351 {
1352     int    count = 0;
1353     int    ret = AFP_OK;
1354     size_t easize;
1355     char   srceapath[ MAXPATHLEN + 1];
1356     char   *eapath;
1357     char   *eaname;
1358     struct ea srcea;
1359     struct ea dstea;
1360     struct adouble ad;
1361
1362     LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1363
1364     /* Open EA stuff */
1365     if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1366         if (errno == ENOENT)
1367             /* no EA files, nothing to do */
1368             return AFP_OK;
1369         else {
1370             LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1371             return AFPERR_MISC;
1372         }
1373     }
1374
1375     if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1376         if (errno == ENOENT) {
1377             /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1378             ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
1379             if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1380                 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1381                 ret = AFPERR_MISC;
1382                 goto exit;
1383             }
1384             ad_close(&ad, ADFLAGS_HF);
1385             if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1386                 ret = AFPERR_MISC;
1387                 goto exit;
1388             }
1389         }
1390     }
1391
1392     /* Loop through all EAs: */
1393     while (count < srcea.ea_count) {
1394         /* Copy EA */
1395         eaname = (*srcea.ea_entries)[count].ea_name;
1396         easize = (*srcea.ea_entries)[count].ea_size;
1397
1398         /* Build src and dst paths for copy_file() */
1399         if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1400             ret = AFPERR_MISC;
1401             goto exit;
1402         }
1403         strcpy(srceapath, eapath);
1404         if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1405             ret = AFPERR_MISC;
1406             goto exit;
1407         }
1408
1409         LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1410             src, dst, srceapath, eapath);
1411
1412         /* Add EA to dstea */
1413         if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1414             LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
1415                 src, dst, eaname);
1416             ret = AFPERR_MISC;
1417             goto exit;
1418         }
1419
1420         /* Now copy the EA */
1421         if ((copy_file( srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1422             LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1423                 src, dst, srceapath, eapath);
1424             ret = AFPERR_MISC;
1425             goto exit;
1426         }
1427
1428         count++;
1429     }
1430
1431 exit:
1432     ea_close(&srcea);
1433     ea_close(&dstea);
1434         return ret;
1435 }
1436
1437 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1438 {
1439     LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1440
1441     int count = 0, ret = AFP_OK;
1442     char *eaname;
1443     struct ea ea;
1444
1445     /* Open EA stuff */
1446     if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1447         if (errno == ENOENT)
1448             /* no EA files, nothing to do */
1449             return AFP_OK;
1450         else {
1451             LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1452             return AFPERR_MISC;
1453         }
1454     }
1455
1456     if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
1457         switch (errno) {
1458         case EPERM:
1459         case EACCES:
1460             ret = AFPERR_ACCESS;
1461             goto exit;
1462         default:
1463             ret = AFPERR_MISC;
1464             goto exit;
1465         }
1466     }
1467
1468     while (count < ea.ea_count) {
1469         if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1470             ret = AFPERR_MISC;
1471             goto exit;
1472         }
1473         if ((chown(eaname, uid, gid)) != 0) {
1474             switch (errno) {
1475             case EPERM:
1476             case EACCES:
1477                 ret = AFPERR_ACCESS;
1478                 goto exit;
1479             default:
1480                 ret = AFPERR_MISC;
1481                 goto exit;
1482             }
1483             continue;
1484         }
1485
1486         count++;
1487     }
1488
1489 exit:
1490     if ((ea_close(&ea)) != 0) {
1491         LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
1492         return AFPERR_MISC;
1493     }
1494
1495     return ret;
1496 }
1497
1498 int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
1499 {
1500     LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
1501
1502     int count = 0, ret = AFP_OK;
1503     const char *eaname;
1504     struct ea ea;
1505
1506     /* Open EA stuff */
1507     if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1508         if (errno == ENOENT)
1509             /* no EA files, nothing to do */
1510             return AFP_OK;
1511         else
1512             return AFPERR_MISC;
1513     }
1514
1515     /* Set mode on EA header file */
1516     if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1517         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1518         switch (errno) {
1519         case EPERM:
1520         case EACCES:
1521             ret = AFPERR_ACCESS;
1522             goto exit;
1523         default:
1524             ret = AFPERR_MISC;
1525             goto exit;
1526         }
1527     }
1528
1529     /* Set mode on EA files */
1530     while (count < ea.ea_count) {
1531         if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1532             ret = AFPERR_MISC;
1533             goto exit;
1534         }
1535         if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1536             LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
1537             switch (errno) {
1538             case EPERM:
1539             case EACCES:
1540                 ret = AFPERR_ACCESS;
1541                 goto exit;
1542             default:
1543                 ret = AFPERR_MISC;
1544                 goto exit;
1545             }
1546             continue;
1547         }
1548
1549         count++;
1550     }
1551
1552 exit:
1553     if ((ea_close(&ea)) != 0) {
1554         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
1555         return AFPERR_MISC;
1556     }
1557
1558     return ret;
1559 }
1560
1561 int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
1562 {
1563     LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
1564
1565     int ret = AFP_OK;
1566     uid_t uid;
1567     const char *eaname;
1568     const char *eaname_safe = NULL;
1569     struct ea ea;
1570
1571     /* .AppleDouble already might be inaccesible, so we must run as id 0 */
1572     uid = geteuid();
1573     if (seteuid(0)) {
1574         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
1575         ret = AFPERR_MISC;
1576         goto exit;
1577     }
1578
1579     /* Open EA stuff */
1580     if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1581         if (errno == ENOENT)
1582             /* no EA files, nothing to do */
1583             return AFP_OK;
1584         else
1585             return AFPERR_MISC;
1586     }
1587
1588     /* Set mode on EA header */
1589     if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1590         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1591         switch (errno) {
1592         case EPERM:
1593         case EACCES:
1594             ret = AFPERR_ACCESS;
1595             goto exit;
1596         default:
1597             ret = AFPERR_MISC;
1598             goto exit;
1599         }
1600     }
1601
1602     /* Set mode on EA files */
1603     int count = 0;
1604     while (count < ea.ea_count) {
1605         eaname = (*ea.ea_entries)[count].ea_name;
1606         /*
1607          * Be careful with EA names from the EA header!
1608          * Eg NFS users might have access to them, can inject paths using ../ or /.....
1609          * FIXME:
1610          * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
1611          */
1612         if ((eaname_safe = strrchr(eaname, '/'))) {
1613             LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
1614             eaname = eaname_safe;
1615         }
1616         if ((eaname = ea_path(&ea, eaname)) == NULL) {
1617             ret = AFPERR_MISC;
1618             goto exit;
1619         }
1620         if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1621             LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
1622             switch (errno) {
1623             case EPERM:
1624             case EACCES:
1625                 ret = AFPERR_ACCESS;
1626                 goto exit;
1627             default:
1628                 ret = AFPERR_MISC;
1629                 goto exit;
1630             }
1631             continue;
1632         }
1633
1634         count++;
1635     }
1636
1637 exit:
1638     if (seteuid(uid) < 0) {
1639         LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
1640         exit(EXITERR_SYS);
1641     }
1642
1643     if ((ea_close(&ea)) != 0) {
1644         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);
1645         return AFPERR_MISC;
1646     }
1647
1648     return ret;
1649 }