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