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