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