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