]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea_ad.c
fa3a010926c898ff1a1688d0e631e5cf13f64f47
[netatalk.git] / libatalk / vfs / ea_ad.c
1 /*
2   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <unistd.h>
20 #include <stdint.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <dirent.h>
28
29 #include <atalk/adouble.h>
30 #include <atalk/ea.h>
31 #include <atalk/afp.h>
32 #include <atalk/logger.h>
33 #include <atalk/volume.h>
34 #include <atalk/vfs.h>
35 #include <atalk/util.h>
36 #include <atalk/unix.h>
37
38 /*
39  * Store Extended Attributes inside .AppleDouble folders as follows:
40  *
41  * filename "fileWithEAs" with EAs "testEA1" and "testEA2"
42  *
43  * - create header with with the format struct adouble_ea_ondisk, the file is written to
44  *   ".AppleDouble/fileWithEAs::EA"
45  * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2"
46  */
47
48 /* 
49  * Build mode for EA header from file mode
50  */
51 static inline mode_t ea_header_mode(mode_t mode)
52 {
53     /* Same as ad_hf_mode(mode) */
54     mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
55     /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/
56     mode |= S_IRUSR | S_IWUSR;
57     return mode;
58 }
59
60 /* 
61  * Build mode for EA file from file mode
62  */
63 static inline mode_t ea_mode(mode_t mode)
64 {
65     /* Same as ad_hf_mode(mode) */
66     mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
67     return mode;
68 }
69
70 /*
71   Taken form afpd/desktop.c
72 */
73 static char *mtoupath(const struct vol *vol, const char *mpath)
74 {
75     static char  upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
76     const char   *m;
77     char         *u;
78     size_t       inplen;
79     size_t       outlen;
80     uint16_t     flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON;
81
82     if (!mpath)
83         return NULL;
84
85     if ( *mpath == '\0' ) {
86         return( "." );
87     }
88
89     m = mpath;
90     u = upath;
91
92     inplen = strlen(m);
93     outlen = MAXPATHLEN;
94
95     if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
96                                                 vol->v_volcharset,
97                                                 vol->v_maccharset,
98                                                 m, inplen, u, outlen, &flags)) ) {
99         return NULL;
100     }
101
102     return( upath );
103 }
104
105
106 /*
107  * Function: unpack_header
108  *
109  * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
110  *
111  * Arguments:
112  *
113  *    ea      (rw) handle to struct ea
114  *
115  * Returns: 0 on success, -1 on error
116  *
117  * Effects:
118  *
119  * Verifies magic and version.
120  */
121 static int unpack_header(struct ea * restrict ea)
122 {
123     int ret = 0;
124     unsigned int count = 0;
125     uint32_t uint32;
126     char *buf;
127
128     /* Check magic and version */
129     buf = ea->ea_data;
130     if (*(uint32_t *)buf != htonl(EA_MAGIC)) {
131         LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf);
132         ret = -1;
133         goto exit;
134     }
135     buf += 4;
136     if (*(uint16_t *)buf != htons(EA_VERSION)) {
137         LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf);
138         ret = -1;
139         goto exit;
140     }
141     buf += 2;
142
143     /* Get EA count */
144     ea->ea_count = ntohs(*(uint16_t *)buf);
145     LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
146     buf += 2;
147
148     if (ea->ea_count == 0)
149         return 0;
150
151     /* Allocate storage for the ea_entries array */
152     ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
153     if ( ! ea->ea_entries) {
154         LOG(log_error, logtype_afpd, "unpack_header: OOM");
155         ret = -1;
156         goto exit;
157     }
158
159     buf = ea->ea_data + EA_HEADER_SIZE;
160     while (count < ea->ea_count) {
161         memcpy(&uint32, buf, 4); /* EA size */
162         buf += 4;
163         (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
164         (*(ea->ea_entries))[count].ea_name = strdup(buf);
165         if (! (*(ea->ea_entries))[count].ea_name) {
166             LOG(log_error, logtype_afpd, "unpack_header: OOM");
167             ret = -1;
168             goto exit;
169         }
170         (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
171         buf += (*(ea->ea_entries))[count].ea_namelen + 1;
172
173         LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
174             (*(ea->ea_entries))[count].ea_name,
175             (*(ea->ea_entries))[count].ea_size,
176             (*(ea->ea_entries))[count].ea_namelen);
177
178         count++;
179     }
180
181 exit:
182     return ret;
183 }
184
185 /*
186  * Function: pack_header
187  *
188  * Purpose: pack everything from struct ea into buffer at ea->ea_data
189  *
190  * Arguments:
191  *
192  *    ea      (rw) handle to struct ea
193  *
194  * Returns: 0 on success, -1 on error
195  *
196  * Effects:
197  *
198  * adjust ea->ea_count in case an ea entry deletetion is detected
199  */
200 static int pack_header(struct ea * restrict ea)
201 {
202     unsigned int count = 0, eacount = 0;
203     uint16_t uint16;
204     uint32_t uint32;
205     size_t bufsize = EA_HEADER_SIZE;
206
207     char *buf = ea->ea_data + EA_HEADER_SIZE;
208
209     LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
210         ea->filename, ea->ea_count, ea->ea_size);
211
212     if (ea->ea_count == 0)
213         /* nothing to do, magic, version and count are still valid in buffer */
214         return 0;
215
216     while(count < ea->ea_count) { /* the names */
217         /* Check if its a deleted entry */
218         if ( ! ((*ea->ea_entries)[count].ea_name)) {
219             count++;
220             continue;
221         }
222
223         bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
224         count++;
225         eacount++;
226     }
227
228     bufsize += (eacount * 4); /* header + ea_size for each EA */
229     if (bufsize > ea->ea_size) {
230         /* we must realloc */
231         if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
232             LOG(log_error, logtype_afpd, "pack_header: OOM");
233             return -1;
234         }
235         ea->ea_data = buf;
236     }
237     ea->ea_size = bufsize;
238
239     /* copy count */
240     uint16 = htons(eacount);
241     memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
242
243     count = 0;
244     buf = ea->ea_data + EA_HEADER_SIZE;
245     while (count < ea->ea_count) {
246         /* Check if its a deleted entry */
247         if ( ! ((*ea->ea_entries)[count].ea_name)) {
248             count++;
249             continue;
250         }
251
252         /* First: EA size */
253         uint32 = htonl((*(ea->ea_entries))[count].ea_size);
254         memcpy(buf, &uint32, 4);
255         buf += 4;
256
257         /* Second: EA name as C-string */
258         strcpy(buf, (*(ea->ea_entries))[count].ea_name);
259         buf += (*(ea->ea_entries))[count].ea_namelen + 1;
260
261         LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
262             (*(ea->ea_entries))[count].ea_name,
263             (*(ea->ea_entries))[count].ea_size,
264             (*(ea->ea_entries))[count].ea_namelen);
265
266         count++;
267     }
268
269     ea->ea_count = eacount;
270
271     LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
272         ea->filename, ea->ea_count, ea->ea_size);
273     
274     return 0;
275 }
276
277 /*
278  * Function: ea_addentry
279  *
280  * Purpose: add one EA into ea->ea_entries[]
281  *
282  * Arguments:
283  *
284  *    ea            (rw) pointer to struct ea
285  *    attruname     (r) name of EA
286  *    attrsize      (r) size of ea
287  *    bitmap        (r) bitmap from FP func
288  *
289  * Returns: new number of EA entries, -1 on error
290  *
291  * Effects:
292  *
293  * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
294  * Otherwise realloc and put entry at the end. Increments ea->ea_count.
295  */
296 static int ea_addentry(struct ea * restrict ea,
297                        const char * restrict attruname,
298                        size_t attrsize,
299                        int bitmap)
300 {
301     int ea_existed = 0;
302     unsigned int count = 0;
303     void *tmprealloc;
304
305     /* First check if an EA of the requested name already exist */
306     if (ea->ea_count > 0) {
307         while (count < ea->ea_count) {
308             if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
309                 ea_existed = 1;
310                 LOG(log_debug, logtype_afpd, "ea_addentry('%s', bitmap:0x%x): exists", attruname, bitmap);
311                 if (bitmap & kXAttrCreate)
312                     /* its like O_CREAT|O_EXCL -> fail */
313                     return -1;
314                 (*(ea->ea_entries))[count].ea_size = attrsize;
315                 return 0;
316             }
317             count++;
318         }
319     }
320
321     if ((bitmap & kXAttrReplace) && ! ea_existed)
322         /* replace was requested, but EA didn't exist */
323         return -1;
324
325     if (ea->ea_count == 0) {
326         ea->ea_entries = malloc(sizeof(struct ea_entry));
327         if ( ! ea->ea_entries) {
328             LOG(log_error, logtype_afpd, "ea_addentry: OOM");
329             return -1;
330         }
331     } else if (! ea_existed) {
332         tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
333         if ( ! tmprealloc) {
334             LOG(log_error, logtype_afpd, "ea_addentry: OOM");
335             return -1;
336         }
337         ea->ea_entries = tmprealloc;
338     }
339
340     /* We've grown the array, now store the entry */
341     (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
342     (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
343     if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
344         LOG(log_error, logtype_afpd, "ea_addentry: OOM");
345         goto error;
346     }
347     (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
348
349     ea->ea_count++;
350     return ea->ea_count;
351
352 error:
353     if (ea->ea_count == 0 && ea->ea_entries) {
354         /* We just allocated storage but had an error somewhere -> free storage*/
355         free(ea->ea_entries);
356         ea->ea_entries = NULL;
357     }
358     ea->ea_count = 0;
359     return -1;
360 }
361
362 /*
363  * Function: create_ea_header
364  *
365  * Purpose: create EA header file, only called from ea_open
366  *
367  * Arguments:
368  *
369  *    uname       (r)  filename for which we have to create a header
370  *    ea          (rw) ea handle with already allocated storage pointed to
371  *                     by ea->ea_data
372  *
373  * Returns: fd of open header file on success, -1 on error, errno semantics:
374  *          EEXIST: open with O_CREAT | O_EXCL failed
375  *
376  * Effects:
377  *
378  * Creates EA header file and initialize ea->ea_data buffer.
379  * Possibe race condition with other afpd processes:
380  * we were called because header file didn't exist in eg. ea_open. We then
381  * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
382  * What do we do then? Someone else is in the process of creating the header too, but
383  * it might not have finished it. That means we cant just open, read and use it!
384  * We therefor currently just break with an error.
385  * On return the header file is still r/w locked.
386  */
387 static int create_ea_header(const char * restrict uname,
388                             struct ea * restrict ea)
389 {
390     int fd = -1, err = 0;
391     char *ptr;
392
393     if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
394         LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
395         return -1;
396     }
397
398     /* lock it */
399     if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
400         LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
401         err = -1;
402         goto exit;
403     }
404
405     /* Now init it */
406     ptr = ea->ea_data;
407     *(uint32_t *)ptr = htonl(EA_MAGIC);
408     ptr += EA_MAGIC_LEN;
409     *(uint16_t *)ptr = htons(EA_VERSION);
410     ptr += EA_VERSION_LEN;
411     *(uint16_t *)ptr = 0;       /* count */
412
413     ea->ea_size = EA_HEADER_SIZE;
414     ea->ea_inited = EA_INITED;
415
416 exit:
417     if (err != 0) {
418         close(fd);
419         fd = -1;
420     }
421     return fd;
422 }
423
424 /*
425  * Function: write_ea
426  *
427  * Purpose: write an EA to disk
428  *
429  * Arguments:
430  *
431  *    ea         (r) struct ea handle
432  *    attruname  (r) EA name
433  *    ibuf       (r) buffer with EA content
434  *    attrsize   (r) size of EA
435  *
436  * Returns: 0 on success, -1 on error
437  *
438  * Effects:
439  *
440  * Creates/overwrites EA file.
441  *
442  */
443 static int write_ea(const struct ea * restrict ea,
444                     const char * restrict attruname,
445                     const char * restrict ibuf,
446                     size_t attrsize)
447 {
448     int fd = -1, ret = AFP_OK;
449     struct stat st;
450     char *eaname;
451
452     if ((eaname = ea_path(ea, attruname, 1)) == NULL) {
453         LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
454         return AFPERR_MISC;
455     }
456
457     LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname);
458
459     /* Check if it exists, remove if yes*/
460     if ((stat(eaname, &st)) == 0) {
461         if ((unlink(eaname)) != 0) {
462             if (errno == EACCES)
463                 return AFPERR_ACCESS;
464             else
465                 return AFPERR_MISC;
466         }
467     }
468
469     if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
470         LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
471         return -1;
472     }
473
474     /* lock it */
475     if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
476         LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
477         ret = -1;
478         goto exit;
479     }
480
481     if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) {
482         LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
483         ret = -1;
484         goto exit;
485     }
486
487 exit:
488     if (fd != -1)
489         close(fd); /* and unlock */
490     return ret;
491 }
492
493 /*
494  * Function: ea_delentry
495  *
496  * Purpose: delete one EA from ea->ea_entries[]
497  *
498  * Arguments:
499  *
500  *    ea            (rw) pointer to struct ea
501  *    attruname     (r) EA name
502  *
503  * Returns: new number of EA entries, -1 on error
504  *
505  * Effects:
506  *
507  * Remove entry from  ea->ea_entries[]. Decrement ea->ea_count.
508  * Marks it as unused just by freeing name and setting it to NULL.
509  * ea_close and pack_buffer must honor this.
510  */
511 static int ea_delentry(struct ea * restrict ea, const char * restrict attruname)
512 {
513     int ret = 0;
514     unsigned int count = 0;
515
516     if (ea->ea_count == 0) {
517         LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
518         return -1;
519     }
520
521     while (count < ea->ea_count) {
522         /* search matching EA */
523         if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
524             free((*ea->ea_entries)[count].ea_name);
525             (*ea->ea_entries)[count].ea_name = NULL;
526
527             LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
528                 attruname, count + 1, ea->ea_count);
529
530             break;
531         }
532         count++;
533     }
534
535     return ret;
536 }
537
538 /*
539  * Function: delete_ea_file
540  *
541  * Purpose: delete EA file from disk
542  *
543  * Arguments:
544  *
545  *    ea         (r) struct ea handle
546  *    attruname  (r) EA name
547  *
548  * Returns: 0 on success, -1 on error
549  */
550 static int delete_ea_file(const struct ea * restrict ea, const char *eaname)
551 {
552     int ret = 0;
553     char *eafile;
554     struct stat st;
555
556     if ((eafile = ea_path(ea, eaname, 1)) == NULL) {
557         LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
558         return -1;
559     }
560
561     /* Check if it exists, remove if yes*/
562     if ((stat(eafile, &st)) == 0) {
563         if ((unlink(eafile)) != 0) {
564             LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
565                 eafile, strerror(errno));
566             ret = -1;
567         } else
568             LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
569     }
570
571     return ret;
572 }
573
574 /*************************************************************************************
575  * ea_path, ea_open and ea_close are only global so that dbd can call them
576  *************************************************************************************/
577
578 /*
579  * Function: ea_path
580  *
581  * Purpose: return name of ea header filename
582  *
583  * Arguments:
584  *
585  *    ea           (r) ea handle
586  *    eaname       (r) name of EA or NULL
587  *    macname      (r) if != 0 call mtoupath on eaname
588  *
589  * Returns: pointer to name in static buffer, NULL on error
590  *
591  * Effects:
592  *
593  * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
594  * Files: "file" -> "file/.AppleDouble/file::EA"
595  * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
596  * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
597  */
598 char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname)
599 {
600     char *adname;
601     static char pathbuf[MAXPATHLEN + 1];
602
603     /* get name of a adouble file from uname */
604     adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
605     /* copy it so we can work with it */
606     strlcpy(pathbuf, adname, MAXPATHLEN + 1);
607     /* append "::EA" */
608     strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
609
610     if (eaname) {
611         strlcat(pathbuf, "::", MAXPATHLEN + 1);
612         if (macname)
613             if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
614                 return NULL;
615         strlcat(pathbuf, eaname, MAXPATHLEN + 1);
616     }
617
618     return pathbuf;
619 }
620
621 /*
622  * Function: ea_open
623  *
624  * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
625  *
626  * Arguments:
627  *
628  *    vol         (r) current volume
629  *    uname       (r) filename for which we have to open a header
630  *    flags       (r) EA_CREATE: create if it doesn't exist (without it won't be created)
631  *                    EA_RDONLY: open read only
632  *                    EA_RDWR: open read/write
633  *                    Eiterh EA_RDONLY or EA_RDWR MUST be requested
634  *    ea          (w) pointer to a struct ea that we fill
635  *
636  * Returns: 0 on success
637  *         -1 on misc error with errno = EFAULT
638  *         -2 if no EA header exists with errno = ENOENT
639  *
640  * Effects:
641  *
642  * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
643  * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
644  * file is either read or write locked depending on the open flags.
645  * When you're done with struct ea you must call ea_close on it.
646  */
647 int ea_open(const struct vol * restrict vol,
648             const char * restrict uname,
649             eaflags_t eaflags,
650             struct ea * restrict ea)
651 {
652     int ret = 0;
653     char *eaname;
654     struct stat st;
655
656     /* Enforce usage rules! */
657     if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
658         LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
659         return -1;
660     }
661
662     /* Set it all to 0 */
663     memset(ea, 0, sizeof(struct ea));
664
665     ea->vol = vol;              /* ea_close needs it */
666     ea->ea_flags = eaflags;
667     ea->dirfd = -1;             /* no *at (cf openat) semantics by default */
668
669     /* Dont care for errors, eg when removing the file is already gone */
670     if (!stat(uname, &st) && S_ISDIR(st.st_mode))
671         ea->ea_flags |=  EA_DIR;
672
673     if ( ! (ea->filename = strdup(uname))) {
674         LOG(log_error, logtype_afpd, "ea_open: OOM");
675         return -1;
676     }
677
678     eaname = ea_path(ea, NULL, 0);
679     LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
680
681     /* Check if it exists, if not create it if EA_CREATE is in eaflags */
682     if ((stat(eaname, &st)) != 0) {
683         if (errno == ENOENT) {
684
685             /* It doesnt exist */
686
687             if ( ! (eaflags & EA_CREATE)) {
688                 /* creation was not requested, so return with error */
689                 ret = -2;
690                 goto exit;
691             }
692
693             /* Now create a header file */
694
695             /* malloc buffer for minimal on disk data */
696             ea->ea_data = malloc(EA_HEADER_SIZE);
697             if (! ea->ea_data) {
698                 LOG(log_error, logtype_afpd, "ea_open: OOM");
699                 ret = -1;
700                 goto exit;
701             }
702
703             /* create it */
704             ea->ea_fd = create_ea_header(eaname, ea);
705             if (ea->ea_fd == -1) {
706                 ret = -1;
707                 goto exit;
708             }
709
710             return 0;
711
712         } else {/* errno != ENOENT */
713             ret = -1;
714             goto exit;
715         }
716     }
717
718     /* header file exists, so read and parse it */
719
720     /* malloc buffer where we read disk file into */
721     if (st.st_size < EA_HEADER_SIZE) {
722         LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname);
723         ret = -1;
724         goto exit;
725     }
726     ea->ea_size = st.st_size;
727     ea->ea_data = malloc(st.st_size);
728     if (! ea->ea_data) {
729         LOG(log_error, logtype_afpd, "ea_open: OOM");
730         ret = -1;
731         goto exit;
732     }
733
734     /* Now lock, open and read header file from disk */
735     if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
736         LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno));
737         ret = -1;
738         goto exit;
739     }
740
741     /* lock it */
742     if (ea->ea_flags & EA_RDONLY) {
743         /* read lock */
744         if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
745             LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
746             ret = -1;
747             goto exit;
748         }
749     } else {  /* EA_RDWR */
750         /* write lock */
751         if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
752             LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
753             ret = -1;
754             goto exit;
755         }
756     }
757
758     /* read it */
759     if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
760         LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
761         ret = -1;
762         goto exit;
763     }
764
765     if ((unpack_header(ea)) != 0) {
766         LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
767         ret = -1;
768         goto exit;
769     }
770
771 exit:
772     switch (ret) {
773     case 0:
774         ea->ea_inited = EA_INITED;
775         break;
776     case -1:
777         errno = EFAULT; /* force some errno distinguishable from ENOENT */
778         /* fall through */
779     case -2:
780         if (ea->ea_data) {
781             free(ea->ea_data);
782             ea->ea_data = NULL;
783         }
784         if (ea->ea_fd) {
785             close(ea->ea_fd);
786             ea->ea_fd = -1;
787         }
788         break;
789     }
790
791     return ret;
792 }
793
794 /*
795  * Function: ea_openat
796  *
797  * Purpose: openat like wrapper for ea_open, takes a additional file descriptor
798  *
799  * Arguments:
800  *
801  *    vol         (r) current volume
802  *    sfd         (r) openat like file descriptor
803  *    uname       (r) filename for which we have to open a header
804  *    flags       (r) EA_CREATE: create if it doesn't exist (without it won't be created)
805  *                    EA_RDONLY: open read only
806  *                    EA_RDWR: open read/write
807  *                    Eiterh EA_RDONLY or EA_RDWR MUST be requested
808  *    ea          (w) pointer to a struct ea that we fill
809  *
810  * Returns: 0 on success
811  *         -1 on misc error with errno = EFAULT
812  *         -2 if no EA header exists with errno = ENOENT
813  *
814  * Effects:
815  *
816  * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
817  * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
818  * file is either read or write locked depending on the open flags.
819  * When you're done with struct ea you must call ea_close on it.
820  */
821 int ea_openat(const struct vol * restrict vol,
822               int dirfd,
823               const char * restrict uname,
824               eaflags_t eaflags,
825               struct ea * restrict ea)
826 {
827     int ret = 0;
828     int cwdfd = -1;
829
830     if (dirfd != -1) {
831         if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
832             ret = -1;
833             goto exit;
834         }
835     }
836
837     ret = ea_open(vol, uname, eaflags, ea);
838     ea->dirfd = dirfd;
839
840     if (dirfd != -1) {
841         if (fchdir(cwdfd) != 0) {
842             LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting");
843             exit(EXITERR_SYS);
844         }
845     }
846
847
848 exit:
849     if (cwdfd != -1)
850         close(cwdfd);
851
852     return ret;
853
854 }
855
856 /*
857  * Function: ea_close
858  *
859  * Purpose: flushes and closes an ea handle
860  *
861  * Arguments:
862  *
863  *    ea          (rw) pointer to ea handle
864  *
865  * Returns: 0 on success, -1 on error
866  *
867  * Effects:
868  *
869  * Flushes and then closes and frees all resouces held by ea handle.
870  * Pack data in ea into ea_data, then write ea_data to disk
871  */
872 int ea_close(struct ea * restrict ea)
873 {
874     int ret = 0; 
875     unsigned int count = 0;
876     char *eaname;
877     struct stat st;
878
879     LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
880
881     if (ea->ea_inited != EA_INITED) {
882         LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
883         return 0;
884     }
885
886     /* pack header and write it to disk if it was opened EA_RDWR*/
887     if (ea->ea_flags & EA_RDWR) {
888         if ((pack_header(ea)) != 0) {
889             LOG(log_error, logtype_afpd, "ea_close: pack header");
890             ret = -1;
891         } else {
892             if (ea->ea_count == 0) {
893                 /* Check if EA header exists and remove it */
894                 eaname = ea_path(ea, NULL, 0);
895                 if ((lstatat(ea->dirfd, eaname, &st)) == 0) {
896                     if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) {
897                         LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
898                             eaname, strerror(errno));
899                         ret = -1;
900                     }
901                     else
902                         LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
903                 } else {
904                     /* stat error */
905                     if (errno != ENOENT) {
906                         LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
907                             eaname, strerror(errno));
908                         ret = -1;
909                     }
910                 }
911             } else { /* ea->ea_count > 0 */
912                 if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
913                     LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
914                     ret = -1;
915                     goto exit;
916                 }
917
918                 if ((ftruncate(ea->ea_fd, 0)) == -1) {
919                     LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
920                     ret = -1;
921                     goto exit;
922                 }
923
924                 if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
925                     LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
926                     ret = -1;
927                 }
928             }
929         }
930     }
931
932 exit:
933     /* free names */
934     while(count < ea->ea_count) {
935         if ( (*ea->ea_entries)[count].ea_name ) {
936             free((*ea->ea_entries)[count].ea_name);
937             (*ea->ea_entries)[count].ea_name = NULL;
938         }
939         count++;
940     }
941     ea->ea_count = 0;
942
943     if (ea->filename) {
944         free(ea->filename);
945         ea->filename = NULL;
946     }
947
948     if (ea->ea_entries) {
949         free(ea->ea_entries);
950         ea->ea_entries = NULL;
951     }
952
953     if (ea->ea_data) {
954         free(ea->ea_data);
955         ea->ea_data = NULL;
956     }
957     if (ea->ea_fd != -1) {
958         close(ea->ea_fd);       /* also releases the fcntl lock */
959         ea->ea_fd = -1;
960     }
961
962     return 0;
963 }
964
965
966
967 /************************************************************************************
968  * VFS funcs called from afp_ea* funcs
969  ************************************************************************************/
970
971 /*
972  * Function: get_easize
973  *
974  * Purpose: get size of an EA
975  *
976  * Arguments:
977  *
978  *    vol          (r) current volume
979  *    rbuf         (w) DSI reply buffer
980  *    rbuflen      (rw) current length of data in reply buffer
981  *    uname        (r) filename
982  *    oflag        (r) link and create flag
983  *    attruname    (r) name of attribute
984  *
985  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
986  *
987  * Effects:
988  *
989  * Copies EA size into rbuf in network order. Increments *rbuflen +4.
990  */
991 int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
992 {
993     int ret = AFPERR_MISC;
994     unsigned int count = 0;
995     uint32_t uint32;
996     struct ea ea;
997
998     LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
999
1000     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1001         if (errno != ENOENT)
1002             LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
1003
1004         memset(rbuf, 0, 4);
1005         *rbuflen += 4;
1006         return ret;
1007     }
1008
1009     while (count < ea.ea_count) {
1010         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
1011             uint32 = htonl((*ea.ea_entries)[count].ea_size);
1012             memcpy(rbuf, &uint32, 4);
1013             *rbuflen += 4;
1014             ret = AFP_OK;
1015
1016             LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
1017                 attruname, (*ea.ea_entries)[count].ea_size);
1018             break;
1019         }
1020         count++;
1021     }
1022
1023     if ((ea_close(&ea)) != 0) {
1024         LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
1025         return AFPERR_MISC;
1026     }
1027
1028     return ret;
1029 }
1030
1031 /*
1032  * Function: get_eacontent
1033  *
1034  * Purpose: copy EA into rbuf
1035  *
1036  * Arguments:
1037  *
1038  *    vol          (r) current volume
1039  *    rbuf         (w) DSI reply buffer
1040  *    rbuflen      (rw) current length of data in reply buffer
1041  *    uname        (r) filename
1042  *    oflag        (r) link and create flag
1043  *    attruname    (r) name of attribute
1044  *    maxreply     (r) maximum EA size as of current specs/real-life
1045  *
1046  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1047  *
1048  * Effects:
1049  *
1050  * Copies EA into rbuf. Increments *rbuflen accordingly.
1051  */
1052 int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
1053 {
1054     int ret = AFPERR_MISC, fd = -1;
1055     unsigned int count = 0;
1056     uint32_t uint32;
1057     size_t toread;
1058     struct ea ea;
1059     char *eafile;
1060
1061     LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
1062
1063     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1064         if (errno != ENOENT)
1065             LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
1066         memset(rbuf, 0, 4);
1067         *rbuflen += 4;
1068         return ret;
1069     }
1070
1071     while (count < ea.ea_count) {
1072         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
1073             if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) {
1074                 ret = AFPERR_MISC;
1075                 break;
1076             }
1077
1078             if ((fd = open(eafile, O_RDONLY)) == -1) {
1079                 LOG(log_error, logtype_afpd, "get_eacontent('%s'): open error: %s", uname, strerror(errno));
1080                 ret = AFPERR_MISC;
1081                 break;
1082             }
1083
1084             /* Check how much the client wants, give him what we think is right */
1085             maxreply -= MAX_REPLY_EXTRA_BYTES;
1086             if (maxreply > MAX_EA_SIZE)
1087                 maxreply = MAX_EA_SIZE;
1088             toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
1089             LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
1090
1091             /* Put length of EA data in reply buffer */
1092             uint32 = htonl(toread);
1093             memcpy(rbuf, &uint32, 4);
1094             rbuf += 4;
1095             *rbuflen += 4;
1096
1097             if (read(fd, rbuf, toread) != (ssize_t)toread) {
1098                 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1099                 close(fd);
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 }