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