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