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