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