]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea.c
a0366cff18c7760ac758aece0e7ea0e242ea2296
[netatalk.git] / libatalk / vfs / ea.c
1 /*
2   $Id: ea.c,v 1.8 2009-10-21 17:41:45 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(const struct vol * restrict vol,
912                char * restrict rbuf,
913                int * restrict rbuflen,
914                const char * restrict uname,
915                int oflag,
916                const char * restrict attruname)
917 {
918     int ret = AFPERR_MISC, count = 0;
919     uint32_t uint32;
920     struct ea ea;
921
922     LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
923
924     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
925         LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
926         return AFPERR_MISC;
927     }
928
929     while (count < ea.ea_count) {
930         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
931             uint32 = htonl((*ea.ea_entries)[count].ea_size);
932             memcpy(rbuf, &uint32, 4);
933             *rbuflen += 4;
934             ret = AFP_OK;
935
936             LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
937                 attruname, (*ea.ea_entries)[count].ea_size);
938             break;
939         }
940         count++;
941     }
942
943     if ((ea_close(&ea)) != 0) {
944         LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
945         return AFPERR_MISC;
946     }
947
948     return ret;
949 }
950
951 /*
952  * Function: get_eacontent
953  *
954  * Purpose: copy EA into rbuf
955  *
956  * Arguments:
957  *
958  *    vol          (r) current volume
959  *    rbuf         (w) DSI reply buffer
960  *    rbuflen      (rw) current length of data in reply buffer
961  *    uname        (r) filename
962  *    oflag        (r) link and create flag
963  *    attruname    (r) name of attribute
964  *    maxreply     (r) maximum EA size as of current specs/real-life
965  *
966  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
967  *
968  * Effects:
969  *
970  * Copies EA into rbuf. Increments *rbuflen accordingly.
971  */
972 int get_eacontent(const struct vol * restrict vol,
973                   char * restrict rbuf,
974                   int * restrict rbuflen,
975                   const char * restrict uname,
976                   int oflag,
977                   const char * restrict attruname,
978                   int maxreply)
979 {
980     int ret = AFPERR_MISC, count = 0, fd = -1;
981     uint32_t uint32;
982     size_t toread;
983     struct ea ea;
984     char *eafile;
985
986     LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
987
988     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
989         LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
990         return AFPERR_MISC;
991     }
992
993     while (count < ea.ea_count) {
994         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
995             if ( (eafile = ea_path(&ea, attruname)) == NULL) {
996                 ret = AFPERR_MISC;
997                 break;
998             }
999
1000             if ((fd = open(eafile, O_RDONLY)) == -1) {
1001                 ret = AFPERR_MISC;
1002                 break;
1003             }
1004
1005             /* Check how much the client wants, give him what we think is right */
1006             maxreply -= MAX_REPLY_EXTRA_BYTES;
1007             if (maxreply > MAX_EA_SIZE)
1008                 maxreply = MAX_EA_SIZE;
1009             toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
1010             LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
1011
1012             /* Put length of EA data in reply buffer */
1013             uint32 = htonl(toread);
1014             memcpy(rbuf, &uint32, 4);
1015             rbuf += 4;
1016             *rbuflen += 4;
1017
1018             if ((read(fd, rbuf, toread)) != toread) {
1019                 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
1020                 ret = AFPERR_MISC;
1021                 break;
1022             }
1023             *rbuflen += toread;
1024             close(fd);
1025
1026             ret = AFP_OK;
1027             break;
1028         }
1029         count++;
1030     }
1031
1032     if ((ea_close(&ea)) != 0) {
1033         LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
1034         return AFPERR_MISC;
1035     }
1036
1037     return ret;
1038
1039 }
1040
1041 /*
1042  * Function: list_eas
1043  *
1044  * Purpose: copy names of EAs into attrnamebuf
1045  *
1046  * Arguments:
1047  *
1048  *    vol          (r) current volume
1049  *    attrnamebuf  (w) store names a consecutive C strings here
1050  *    buflen       (rw) length of names in attrnamebuf
1051  *    uname        (r) filename
1052  *    oflag        (r) link and create flag
1053  *
1054  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1055  *
1056  * Effects:
1057  *
1058  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1059  * Increments *buflen accordingly.
1060  */
1061 int list_eas(const struct vol * restrict vol,
1062              char * restrict attrnamebuf,
1063              int * restrict buflen,
1064              const char * restrict uname,
1065              int oflag)
1066 {
1067     int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
1068     char *buf = attrnamebuf;
1069     struct ea ea;
1070
1071     LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
1072
1073     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
1074         if (errno != ENOENT) {
1075             LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
1076             return AFPERR_MISC;
1077         }
1078         else
1079             return AFP_OK;
1080     }
1081
1082     while (count < ea.ea_count) {
1083         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1084         if ( ( len = convert_string(vol->v_volcharset,
1085                                     CH_UTF8_MAC, 
1086                                     (*ea.ea_entries)[count].ea_name,
1087                                     (*ea.ea_entries)[count].ea_namelen,
1088                                     buf + attrbuflen,
1089                                     255))
1090              <= 0 ) {
1091             ret = AFPERR_MISC;
1092             goto exit;
1093         }
1094         if (len == 255)
1095             /* convert_string didn't 0-terminate */
1096             attrnamebuf[attrbuflen + 255] = 0;
1097
1098         LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
1099             uname, (*ea.ea_entries)[count].ea_name);
1100
1101         attrbuflen += len + 1;
1102         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1103             /* Next EA name could overflow, so bail out with error.
1104                FIXME: evantually malloc/memcpy/realloc whatever.
1105                Is it worth it ? */
1106             LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname);
1107             ret = AFPERR_MISC;
1108             goto exit;
1109         }
1110         count++;
1111     }
1112
1113 exit:
1114     *buflen = attrbuflen;
1115
1116     if ((ea_close(&ea)) != 0) {
1117         LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
1118         return AFPERR_MISC;
1119     }
1120
1121     return ret;
1122 }
1123
1124 /*
1125  * Function: set_ea
1126  *
1127  * Purpose: set a Solaris native EA
1128  *
1129  * Arguments:
1130  *
1131  *    vol          (r) current volume
1132  *    uname        (r) filename
1133  *    attruname    (r) EA name
1134  *    ibuf         (r) buffer with EA content
1135  *    attrsize     (r) length EA in ibuf
1136  *    oflag        (r) link and create flag
1137  *
1138  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1139  *
1140  * Effects:
1141  *
1142  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1143  * Increments *rbuflen accordingly.
1144  */
1145 int set_ea(const struct vol * restrict vol,
1146            const char * restrict uname,
1147            const char * restrict attruname,
1148            const char * restrict ibuf,
1149            size_t attrsize,
1150            int oflag)
1151 {
1152     int ret = AFP_OK;
1153     struct ea ea;
1154
1155     LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
1156
1157     if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
1158         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
1159         return AFPERR_MISC;
1160     }
1161
1162     if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
1163         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
1164         ret = AFPERR_MISC;
1165         goto exit;
1166     }
1167
1168     if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
1169         LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
1170         ret = AFPERR_MISC;
1171         goto exit;
1172     }
1173
1174 exit:
1175     if ((ea_close(&ea)) != 0) {
1176         LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
1177         ret = AFPERR_MISC;
1178         goto exit;
1179     }
1180
1181     return ret;
1182 }
1183
1184 /*
1185  * Function: remove_ea
1186  *
1187  * Purpose: remove a EA from a file
1188  *
1189  * Arguments:
1190  *
1191  *    vol          (r) current volume
1192  *    uname        (r) filename
1193  *    attruname    (r) EA name
1194  *    oflag        (r) link and create flag
1195  *
1196  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1197  *
1198  * Effects:
1199  *
1200  * Removes EA attruname from file uname.
1201  */
1202 int remove_ea(const struct vol * restrict vol,
1203               const char * restrict uname,
1204               const char * restrict attruname,
1205               int oflag)
1206 {
1207     int ret = AFP_OK;
1208     struct ea ea;
1209
1210     LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
1211
1212     if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
1213         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
1214         return AFPERR_MISC;
1215     }
1216
1217     if ((ea_delentry(&ea, attruname)) == -1) {
1218         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
1219         ret = AFPERR_MISC;
1220         goto exit;
1221     }
1222
1223     if ((delete_ea_file(&ea, attruname)) != 0) {
1224         LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
1225         ret = AFPERR_MISC;
1226         goto exit;
1227     }
1228
1229 exit:
1230     if ((ea_close(&ea)) != 0) {
1231         LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
1232         ret = AFPERR_MISC;
1233         goto exit;
1234     }
1235
1236     return ret;
1237 }
1238
1239 /**********************************************************************************
1240  * Solaris EA VFS funcs
1241  **********************************************************************************/
1242
1243 /*
1244  * Function: sol_get_easize
1245  *
1246  * Purpose: get size of an EA on Solaris native EA
1247  *
1248  * Arguments:
1249  *
1250  *    vol          (r) current volume
1251  *    rbuf         (w) DSI reply buffer
1252  *    rbuflen      (rw) current length of data in reply buffer
1253  *    uname        (r) filename
1254  *    oflag        (r) link and create flag
1255  *    attruname    (r) name of attribute
1256  *
1257  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1258  *
1259  * Effects:
1260  *
1261  * Copies EA size into rbuf in network order. Increments *rbuflen +4.
1262  */
1263 #ifdef HAVE_SOLARIS_EAS
1264 int sol_get_easize(const struct vol * restrict vol,
1265                    char * restrict rbuf,
1266                    int * restrict rbuflen,
1267                    const char * restrict uname,
1268                    int oflag,
1269                    cons char * restrict attruname)
1270 {
1271     int                 ret, attrdirfd;
1272     uint32_t            attrsize;
1273     struct stat         st;
1274
1275     LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
1276
1277     if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
1278         if (errno == ELOOP) {
1279             /* its a symlink and client requested O_NOFOLLOW  */
1280             LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
1281
1282             memset(rbuf, 0, 4);
1283             *rbuflen += 4;
1284
1285             return AFP_OK;
1286         }
1287         LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
1288         return AFPERR_MISC;
1289     }
1290
1291     if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
1292         LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
1293         ret = AFPERR_MISC;
1294         goto exit;
1295     }
1296     attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
1297
1298     /* Start building reply packet */
1299
1300     LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
1301
1302     /* length of attribute data */
1303     attrsize = htonl(attrsize);
1304     memcpy(rbuf, &attrsize, 4);
1305     *rbuflen += 4;
1306
1307     ret = AFP_OK;
1308
1309 exit:
1310     close(attrdirfd);
1311     return ret;
1312 }
1313 #endif /* HAVE_SOLARIS_EAS */
1314
1315 /*
1316  * Function: sol_get_eacontent
1317  *
1318  * Purpose: copy Solaris native EA into rbuf
1319  *
1320  * Arguments:
1321  *
1322  *    vol          (r) current volume
1323  *    rbuf         (w) DSI reply buffer
1324  *    rbuflen      (rw) current length of data in reply buffer
1325  *    uname        (r) filename
1326  *    oflag        (r) link and create flag
1327  *    attruname    (r) name of attribute
1328  *    maxreply     (r) maximum EA size as of current specs/real-life
1329  *
1330  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1331  *
1332  * Effects:
1333  *
1334  * Copies EA into rbuf. Increments *rbuflen accordingly.
1335  */
1336 #ifdef HAVE_SOLARIS_EAS
1337 int sol_get_eacontent(const struct vol * restrict vol,
1338                       char * restrict rbuf,
1339                       int * restrict rbuflen,
1340                       const char * restrict uname,
1341                       int oflag,
1342                       char * restrict attruname,
1343                       int maxreply)
1344 {
1345     int                 ret, attrdirfd;
1346     size_t              toread, okread = 0, len;
1347     char                *datalength;
1348     struct stat         st;
1349
1350     if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
1351         if (errno == ELOOP) {
1352             /* its a symlink and client requested O_NOFOLLOW  */
1353             LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
1354
1355             memset(rbuf, 0, 4);
1356             *rbuflen += 4;
1357
1358             return AFP_OK;
1359         }
1360         LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
1361         return AFPERR_MISC;
1362     }
1363
1364     if ( -1 == (fstat(attrdirfd, &st))) {
1365         LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
1366         ret = AFPERR_MISC;
1367         goto exit;
1368     }
1369
1370     /* Start building reply packet */
1371
1372     maxreply -= MAX_REPLY_EXTRA_BYTES;
1373     if (maxreply > MAX_EA_SIZE)
1374         maxreply = MAX_EA_SIZE;
1375
1376     /* But never send more than the client requested */
1377     toread = (maxreply < st.st_size) ? maxreply : st.st_size;
1378
1379     LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
1380
1381     /* remember where we must store length of attribute data in rbuf */
1382     datalength = rbuf;
1383     rbuf += 4;
1384     *rbuflen += 4;
1385
1386     while (1) {
1387         len = read(attrdirfd, rbuf, toread);
1388         if (len == -1) {
1389             LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
1390             ret = AFPERR_MISC;
1391             goto exit;
1392         }
1393         okread += len;
1394         rbuf += len;
1395         *rbuflen += len;
1396         if ((len == 0) || (okread == toread))
1397             break;
1398     }
1399
1400     okread = htonl((uint32_t)okread);
1401     memcpy(datalength, &okread, 4);
1402
1403     ret = AFP_OK;
1404
1405 exit:
1406     close(attrdirfd);
1407     return ret;
1408 }
1409 #endif /* HAVE_SOLARIS_EAS */
1410
1411 /*
1412  * Function: sol_list_eas
1413  *
1414  * Purpose: copy names of Solaris native EA into attrnamebuf
1415  *
1416  * Arguments:
1417  *
1418  *    vol          (r) current volume
1419  *    attrnamebuf  (w) store names a consecutive C strings here
1420  *    buflen       (rw) length of names in attrnamebuf
1421  *    uname        (r) filename
1422  *    oflag        (r) link and create flag
1423  *
1424  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1425  *
1426  * Effects:
1427  *
1428  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1429  * Increments *rbuflen accordingly.
1430  */
1431 #ifdef HAVE_SOLARIS_EAS
1432 int sol_list_eas(const struct vol * restrict vol,
1433                  char * restrict attrnamebuf,
1434                  int * restrict buflen,
1435                  const char * restrict uname,
1436                  int oflag)
1437 {
1438     int ret, attrbuflen = *buflen, len, attrdirfd = 0;
1439     struct dirent *dp;
1440     DIR *dirp = NULL;
1441
1442     /* Now list file attribute dir */
1443     if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
1444         if (errno == ELOOP) {
1445             /* its a symlink and client requested O_NOFOLLOW */
1446             ret = AFPERR_BADTYPE;
1447             goto exit;
1448         }
1449         LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1450         ret = AFPERR_MISC;
1451         goto exit;
1452     }
1453
1454     if (NULL == (dirp = fdopendir(attrdirfd))) {
1455         LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
1456         ret = AFPERR_MISC;
1457         goto exit;
1458     }
1459
1460     while ((dp = readdir(dirp)))  {
1461         /* check if its "." or ".." */
1462         if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
1463             (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
1464             continue;
1465
1466         len = strlen(dp->d_name);
1467
1468         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
1469         if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
1470             ret = AFPERR_MISC;
1471             goto exit;
1472         }
1473         if (len == 255)
1474             /* convert_string didn't 0-terminate */
1475             attrnamebuf[attrbuflen + 255] = 0;
1476
1477         LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
1478
1479         attrbuflen += len + 1;
1480         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
1481             /* Next EA name could overflow, so bail out with error.
1482                FIXME: evantually malloc/memcpy/realloc whatever.
1483                Is it worth it ? */
1484             LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
1485             ret = AFPERR_MISC;
1486             goto exit;
1487         }
1488     }
1489
1490     ret = AFP_OK;
1491
1492 exit:
1493     if (dirp)
1494         closedir(dirp);
1495
1496     if (attrdirfd > 0)
1497         close(attrdirfd);
1498
1499     *buflen = attrbuflen;
1500     return ret;
1501 }
1502 #endif /* HAVE_SOLARIS_EAS */
1503
1504 /*
1505  * Function: sol_set_ea
1506  *
1507  * Purpose: set a Solaris native EA
1508  *
1509  * Arguments:
1510  *
1511  *    vol          (r) current volume
1512  *    uname        (r) filename
1513  *    attruname    (r) EA name
1514  *    ibuf         (r) buffer with EA content
1515  *    attrsize     (r) length EA in ibuf
1516  *    oflag        (r) link and create flag
1517  *
1518  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1519  *
1520  * Effects:
1521  *
1522  * Copies names of all EAs of uname as consecutive C strings into rbuf.
1523  * Increments *rbuflen accordingly.
1524  */
1525 #ifdef HAVE_SOLARIS_EAS
1526 int sol_set_ea(const struct vol * restrict vol,
1527                const char * restrict u_name,
1528                const char * restrict attruname,
1529                const char * restrict ibuf,
1530                size_t attrsize,
1531                int oflag)
1532 {
1533     int attrdirfd;
1534
1535     if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
1536         if (errno == ELOOP) {
1537             /* its a symlink and client requested O_NOFOLLOW  */
1538             LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1539             return AFP_OK;
1540         }
1541         LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1542         return AFPERR_MISC;
1543     }
1544
1545     if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
1546         LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
1547         return AFPERR_MISC;
1548     }
1549
1550     return AFP_OK;
1551 }
1552 #endif /* HAVE_SOLARIS_EAS */
1553
1554 /*
1555  * Function: sol_remove_ea
1556  *
1557  * Purpose: remove a Solaris native EA
1558  *
1559  * Arguments:
1560  *
1561  *    vol          (r) current volume
1562  *    uname        (r) filename
1563  *    attruname    (r) EA name
1564  *    oflag        (r) link and create flag
1565  *
1566  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
1567  *
1568  * Effects:
1569  *
1570  * Removes EA attruname from file uname.
1571  */
1572 #ifdef HAVE_SOLARIS_EAS
1573 int sol_remove_ea(const struct vol * restrict vol,
1574                   const char * restrict uname,
1575                   const char * restrict attruname,
1576                   int oflag)
1577 {
1578     int attrdirfd;
1579
1580     if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
1581         switch (errno) {
1582         case ELOOP:
1583             /* its a symlink and client requested O_NOFOLLOW  */
1584             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
1585             return AFP_OK;
1586         case EACCES:
1587             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1588             return AFPERR_ACCESS;
1589         default:
1590             LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
1591             return AFPERR_MISC;
1592         }
1593     }
1594
1595     if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
1596         if (errno == EACCES) {
1597             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1598             return AFPERR_ACCESS;
1599         }
1600         LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
1601         return AFPERR_MISC;
1602     }
1603
1604 }
1605 #endif /* HAVE_SOLARIS_EAS */
1606
1607 /******************************************************************************************
1608  * EA VFS funcs that deal with file/dir cp/mv/rm
1609  ******************************************************************************************/
1610
1611 int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
1612 {
1613     LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
1614
1615     int count = 0, ret = AFP_OK;
1616     struct ea ea;
1617
1618     /* Open EA stuff */
1619     if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
1620         if (errno == ENOENT)
1621             /* no EA files, nothing to do */
1622             return AFP_OK;
1623         else {
1624             LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
1625             return AFPERR_MISC;
1626         }
1627     }
1628
1629     while (count < ea.ea_count) {
1630         if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
1631             ret = AFPERR_MISC;
1632             continue;
1633         }
1634         free((*ea.ea_entries)[count].ea_name);
1635         (*ea.ea_entries)[count].ea_name = NULL;
1636         count++;
1637     }
1638
1639     /* ea_close removes the EA header file for us because all names are NULL */
1640     if ((ea_close(&ea)) != 0) {
1641         LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
1642         return AFPERR_MISC;
1643     }
1644
1645     return ret;
1646 }
1647
1648 int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
1649 {
1650     int    count = 0;
1651     int    ret = AFP_OK;
1652     size_t easize;
1653     char   srceapath[ MAXPATHLEN + 1];
1654     char   *eapath;
1655     char   *eaname;
1656     struct ea srcea;
1657     struct ea dstea;
1658     struct adouble ad;
1659
1660     LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
1661             
1662
1663     /* Open EA stuff */
1664     if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1665         if (errno == ENOENT)
1666             /* no EA files, nothing to do */
1667             return AFP_OK;
1668         else {
1669             LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1670             return AFPERR_MISC;
1671         }
1672     }
1673
1674     if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1675         if (errno == ENOENT) {
1676             /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1677             ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
1678             if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1679                 LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1680                 ret = AFPERR_MISC;
1681                 goto exit;
1682             }
1683             ad_close(&ad, ADFLAGS_HF);
1684             if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1685                 ret = AFPERR_MISC;
1686                 goto exit;
1687             }
1688         }
1689     }
1690
1691     /* Loop through all EAs: */
1692     while (count < srcea.ea_count) {
1693         /* Move EA */
1694         eaname = (*srcea.ea_entries)[count].ea_name;
1695         easize = (*srcea.ea_entries)[count].ea_size;
1696
1697         /* Build src and dst paths for rename() */
1698         if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1699             ret = AFPERR_MISC;
1700             goto exit;
1701         }
1702         strcpy(srceapath, eapath);
1703         if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1704             ret = AFPERR_MISC;
1705             goto exit;
1706         }
1707
1708         LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1709             src, dst, srceapath, eapath);
1710
1711         /* Add EA to dstea */
1712         if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1713             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1714                 src, dst, srceapath, eapath);
1715             ret = AFPERR_MISC;
1716             goto exit;
1717         }
1718
1719         /* Remove EA entry from srcea */
1720         if ((ea_delentry(&srcea, eaname)) == -1) {
1721             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1722                 src, dst, srceapath, eapath);
1723             ea_delentry(&dstea, eaname);
1724             ret = AFPERR_MISC;
1725             goto exit;
1726         }
1727
1728         /* Now rename the EA */
1729         if ((rename( srceapath, eapath)) < 0) {
1730             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
1731                 src, dst, srceapath, eapath);
1732             ret = AFPERR_MISC;
1733             goto exit;
1734         }
1735
1736         count++;
1737     }
1738
1739
1740 exit:
1741     ea_close(&srcea);
1742     ea_close(&dstea);
1743         return ret;
1744 }
1745
1746 int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
1747 {
1748     int    count = 0;
1749     int    ret = AFP_OK;
1750     size_t easize;
1751     char   srceapath[ MAXPATHLEN + 1];
1752     char   *eapath;
1753     char   *eaname;
1754     struct ea srcea;
1755     struct ea dstea;
1756     struct adouble ad;
1757
1758     LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
1759
1760     /* Open EA stuff */
1761     if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
1762         if (errno == ENOENT)
1763             /* no EA files, nothing to do */
1764             return AFP_OK;
1765         else {
1766             LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
1767             return AFPERR_MISC;
1768         }
1769     }
1770
1771     if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1772         if (errno == ENOENT) {
1773             /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
1774             ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
1775             if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
1776                 LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
1777                 ret = AFPERR_MISC;
1778                 goto exit;
1779             }
1780             ad_close(&ad, ADFLAGS_HF);
1781             if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
1782                 ret = AFPERR_MISC;
1783                 goto exit;
1784             }
1785         }
1786     }
1787
1788     /* Loop through all EAs: */
1789     while (count < srcea.ea_count) {
1790         /* Copy EA */
1791         eaname = (*srcea.ea_entries)[count].ea_name;
1792         easize = (*srcea.ea_entries)[count].ea_size;
1793
1794         /* Build src and dst paths for copy_file() */
1795         if ((eapath = ea_path(&srcea, eaname)) == NULL) {
1796             ret = AFPERR_MISC;
1797             goto exit;
1798         }
1799         strcpy(srceapath, eapath);
1800         if ((eapath = ea_path(&dstea, eaname)) == NULL) {
1801             ret = AFPERR_MISC;
1802             goto exit;
1803         }
1804
1805         LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1806             src, dst, srceapath, eapath);
1807
1808         /* Add EA to dstea */
1809         if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
1810             LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
1811                 src, dst, eaname);
1812             ret = AFPERR_MISC;
1813             goto exit;
1814         }
1815
1816         /* Now copy the EA */
1817         if ((copy_file( srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
1818             LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
1819                 src, dst, srceapath, eapath);
1820             ret = AFPERR_MISC;
1821             goto exit;
1822         }
1823
1824         count++;
1825     }
1826
1827 exit:
1828     ea_close(&srcea);
1829     ea_close(&dstea);
1830         return ret;
1831 }
1832
1833 int ea_chown(VFS_FUNC_ARGS_CHOWN)
1834 {
1835     LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
1836
1837     int count = 0, ret = AFP_OK;
1838     char *eaname;
1839     struct ea ea;
1840
1841     /* Open EA stuff */
1842     if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
1843         if (errno == ENOENT)
1844             /* no EA files, nothing to do */
1845             return AFP_OK;
1846         else {
1847             LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
1848             return AFPERR_MISC;
1849         }
1850     }
1851
1852     if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
1853         switch (errno) {
1854         case EPERM:
1855         case EACCES:
1856             ret = AFPERR_ACCESS;
1857             goto exit;
1858         default:
1859             ret = AFPERR_MISC;
1860             goto exit;
1861         }
1862     }
1863
1864     while (count < ea.ea_count) {
1865         if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1866             ret = AFPERR_MISC;
1867             goto exit;
1868         }
1869         if ((chown(eaname, uid, gid)) != 0) {
1870             switch (errno) {
1871             case EPERM:
1872             case EACCES:
1873                 ret = AFPERR_ACCESS;
1874                 goto exit;
1875             default:
1876                 ret = AFPERR_MISC;
1877                 goto exit;
1878             }
1879             continue;
1880         }
1881
1882         count++;
1883     }
1884
1885 exit:
1886     if ((ea_close(&ea)) != 0) {
1887         LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
1888         return AFPERR_MISC;
1889     }
1890
1891     return ret;
1892 }
1893
1894 int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
1895 {
1896     LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
1897
1898     int count = 0, ret = AFP_OK;
1899     const char *eaname;
1900     struct ea ea;
1901
1902     /* Open EA stuff */
1903     if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1904         if (errno == ENOENT)
1905             /* no EA files, nothing to do */
1906             return AFP_OK;
1907         else
1908             return AFPERR_MISC;
1909     }
1910
1911     /* Set mode on EA header file */
1912     if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1913         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1914         switch (errno) {
1915         case EPERM:
1916         case EACCES:
1917             ret = AFPERR_ACCESS;
1918             goto exit;
1919         default:
1920             ret = AFPERR_MISC;
1921             goto exit;
1922         }
1923     }
1924
1925     /* Set mode on EA files */
1926     while (count < ea.ea_count) {
1927         if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
1928             ret = AFPERR_MISC;
1929             goto exit;
1930         }
1931         if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
1932             LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
1933             switch (errno) {
1934             case EPERM:
1935             case EACCES:
1936                 ret = AFPERR_ACCESS;
1937                 goto exit;
1938             default:
1939                 ret = AFPERR_MISC;
1940                 goto exit;
1941             }
1942             continue;
1943         }
1944
1945         count++;
1946     }
1947
1948 exit:
1949     if ((ea_close(&ea)) != 0) {
1950         LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
1951         return AFPERR_MISC;
1952     }
1953
1954     return ret;
1955 }
1956
1957 int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
1958 {
1959     LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
1960
1961     int ret = AFP_OK;
1962     uid_t uid;
1963     const char *eaname;
1964     const char *eaname_safe = NULL;
1965     struct ea ea;
1966
1967     /* .AppleDouble already might be inaccesible, so we must run as id 0 */
1968     uid = geteuid();
1969     if (seteuid(0)) {
1970         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
1971         ret = AFPERR_MISC;
1972         goto exit;
1973     }
1974
1975     /* Open EA stuff */
1976     if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
1977         if (errno == ENOENT)
1978             /* no EA files, nothing to do */
1979             return AFP_OK;
1980         else
1981             return AFPERR_MISC;
1982     }
1983
1984     /* Set mode on EA header */
1985     if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
1986         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL), strerror(errno));
1987         switch (errno) {
1988         case EPERM:
1989         case EACCES:
1990             ret = AFPERR_ACCESS;
1991             goto exit;
1992         default:
1993             ret = AFPERR_MISC;
1994             goto exit;
1995         }
1996     }
1997
1998     /* Set mode on EA files */
1999     int count = 0;
2000     while (count < ea.ea_count) {
2001         eaname = (*ea.ea_entries)[count].ea_name;
2002         /*
2003          * Be careful with EA names from the EA header!
2004          * Eg NFS users might have access to them, can inject paths using ../ or /.....
2005          * FIXME:
2006          * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
2007          */
2008         if ((eaname_safe = strrchr(eaname, '/'))) {
2009             LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
2010             eaname = eaname_safe;
2011         }
2012         if ((eaname = ea_path(&ea, eaname)) == NULL) {
2013             ret = AFPERR_MISC;
2014             goto exit;
2015         }
2016         if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
2017             LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
2018             switch (errno) {
2019             case EPERM:
2020             case EACCES:
2021                 ret = AFPERR_ACCESS;
2022                 goto exit;
2023             default:
2024                 ret = AFPERR_MISC;
2025                 goto exit;
2026             }
2027             continue;
2028         }
2029
2030         count++;
2031     }
2032
2033 exit:
2034     if (seteuid(uid) < 0) {
2035         LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
2036         exit(EXITERR_SYS);
2037     }
2038
2039     if ((ea_close(&ea)) != 0) {
2040         LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);
2041         return AFPERR_MISC;
2042     }
2043
2044     return ret;
2045 }