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