]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/extattrs.c
Extended Attributes Support on Solaris with ZFS
[netatalk.git] / etc / afpd / extattrs.c
1 /*
2   $Id: extattrs.c,v 1.1 2009-02-16 13:49:20 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 #define _ATFILE_SOURCE
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif /* HAVE_CONFIG_H */
22
23 #ifdef HAVE_EXT_ATTRS
24
25 #include <errno.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <dirent.h>
33
34 #include <atalk/adouble.h>
35 #include <atalk/afp.h>
36 #include <atalk/logger.h>
37
38 #include "globals.h"
39 #include "volume.h"
40 #include "desktop.h"
41 #include "directory.h"
42 #include "fork.h"
43 #include "extattrs.h"
44
45 char *ea_finderinfo = "com.apple.FinderInfo";
46 char *ea_resourcefork = "com.apple.ResourceFork";
47
48 static void hexdump(void *m, size_t l) {
49     char *p = m;
50     int count = 0, len;
51     char buf[100];
52     char *bufp = buf;
53
54     while (l--) {
55         len = sprintf(bufp, "%02x ", *p++);
56         bufp += len;
57         count++;
58
59         if ((count % 16) == 0) {
60             LOG(log_debug9, logtype_afpd, "%s", buf);
61             bufp = buf;
62         }
63     }
64 }
65
66 static int getextattr_size(char *rbuf, int *rbuflen, char *uname, int oflag, char *attruname)
67 {
68     int                 ret, attrdirfd;
69     uint32_t            attrsize;
70     struct stat         st;
71
72     LOG(log_debug7, logtype_afpd, "getextattr_size(%s): attribute: \"%s\"", uname, attruname);
73
74     if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
75         if (errno == ELOOP) {
76             /* its a symlink and client requested O_NOFOLLOW  */
77             LOG(log_debug, logtype_afpd, "getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
78
79             *(uint32_t *)rbuf = 0;
80             *rbuflen += 4;
81
82             return AFP_OK;
83         }
84         LOG(log_error, logtype_afpd, "getextattr_size: attropen error: %s", strerror(errno));
85         return AFPERR_MISC;
86     }
87
88     if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
89         LOG(log_error, logtype_afpd, "getextattr_size: fstatat error: %s", strerror(errno));
90         ret = AFPERR_MISC;
91         goto exit;
92     }
93     attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
94     
95     /* Start building reply packet */
96
97     LOG(log_debug7, logtype_afpd, "getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
98
99     /* length of attribute data */
100     *(uint32_t *)rbuf = htonl((uint32_t)attrsize);
101     *rbuflen += 4;
102
103     ret = AFP_OK;
104
105 exit:
106     close(attrdirfd);
107     return ret;
108 }
109
110 static int getextattr_content(char *rbuf, int *rbuflen, char *uname, int oflag, char *attruname, int maxreply)
111 {
112     int                 ret, attrdirfd;
113     size_t              toread, okread = 0, len;
114     uint32_t            *datalength;
115     struct stat         st;
116
117     if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
118         if (errno == ELOOP) {
119             /* its a symlink and client requested O_NOFOLLOW  */
120             LOG(log_debug, logtype_afpd, "getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
121
122             *(uint32_t *)rbuf = 0;
123             *rbuflen += 4;
124
125             return AFP_OK;
126         }
127         LOG(log_error, logtype_afpd, "getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
128         return AFPERR_MISC;
129     }
130
131     if ( -1 == (fstat(attrdirfd, &st))) {
132         LOG(log_error, logtype_afpd, "getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
133         ret = AFPERR_MISC;
134         goto exit;
135     }
136
137     /* Start building reply packet */
138
139     maxreply -= MAX_REPLY_EXTRA_BYTES;
140     if (maxreply > MAX_EA_SIZE)
141         maxreply = MAX_EA_SIZE;
142
143     /* But never send more than the client requested */
144     toread = (maxreply < st.st_size) ? maxreply : st.st_size;
145
146     LOG(log_debug7, logtype_afpd, "getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
147
148     /* remember where we must store length of attribute data in rbuf */
149     datalength = (uint32_t *)rbuf;
150     rbuf += 4;
151     *rbuflen += 4;
152
153     while (1) {
154         len = read(attrdirfd, rbuf, toread);
155         if (len == -1) {
156             LOG(log_error, logtype_afpd, "getextattr_content(%s): read error: %s", attruname, strerror(errno));
157             ret = AFPERR_MISC;
158             goto exit;
159         }
160         okread += len;
161         rbuf += len;
162         *rbuflen += len;
163         if ((len == 0) || (okread == toread))
164             break;
165     }
166
167     *datalength = htonl((uint32_t)okread);
168
169     ret = AFP_OK;
170
171 exit:
172     close(attrdirfd);
173     return ret;
174 }
175
176
177 /***************************************
178  * Interface
179  ****************************************/
180
181 /* 
182    Note: we're being called twice. Firstly the client only want the size of all
183    EA names, secondly it wants these names. In order to avoid scanning EAs twice
184    we cache them in a static buffer.
185  */
186 int afp_listextattr(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
187 {
188     int                 count, attrdirfd = 0, ret, len, oflag = 0;
189     uint16_t            vid, bitmap;
190     uint32_t            did, maxreply;
191     struct vol          *vol;
192     struct dir          *dir;
193     struct path         *s_path;
194     struct adouble      ad, *adp = NULL;
195     struct ofork        *of;
196     struct dirent       *dp;
197     char                *uname, *FinderInfo;
198     DIR                 *dirp = NULL;
199
200     static int          buf_valid = 0, attrbuflen;
201     /* This should be big enough to consecutively store the names of all attributes */
202 #define ATTRNAMEBUFSIZ 4096
203     static char         attrnamebuf[ATTRNAMEBUFSIZ];
204
205 #ifdef DEBUG
206     LOG(log_debug9, logtype_afpd, "afp_listextattr: BEGIN");
207 #endif
208
209     *rbuflen = 0;
210     ibuf += 2;
211
212     /* Get MaxReplySize first */
213     memcpy( &maxreply, ibuf + 14, 4);
214     maxreply = ntohl( maxreply );
215
216     /*
217       If its the first request with maxreply=0 or if we didn't mark our buffers valid for
218       whatever reason (just a safety check, it should be valid), then scan for attributes
219     */
220     if ((maxreply == 0) || (buf_valid == 0)) {
221
222         attrbuflen = 0;
223
224         memcpy( &vid, ibuf, 2);
225         ibuf += 2;
226         if (NULL == ( vol = getvolbyvid( vid )) ) {
227             LOG(log_error, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
228             return AFPERR_ACCESS;
229         }
230
231         memcpy( &did, ibuf, 4);
232         ibuf += 4;
233         if (NULL == ( dir = dirlookup( vol, did )) ) {
234             LOG(log_error, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
235             return afp_errno;
236         }
237
238         memcpy( &bitmap, ibuf, 2);
239         bitmap = ntohs( bitmap );
240         ibuf += 2;
241
242         if (bitmap & kXAttrNoFollow)
243             oflag = O_NOFOLLOW;
244
245         /* Skip ReqCount, StartIndex and maxreply*/
246         ibuf += 10;
247
248         /* get name */
249         if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
250             LOG(log_error, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
251             return AFPERR_NOOBJ;
252         }
253         uname = s_path->u_name;
254
255         /*
256           We have to check the FinderInfo for the file, because if they aren't all 0
257           we must return the synthetic attribute "com.apple.FinderInfo".
258           Note: the client will never (never seen in traces) request that attribute
259           via FPGetExtAttr !
260         */
261         if ((of = of_findname(s_path))) {
262             adp = of->of_ad;
263         } else {
264             ad_init(&ad, vol->v_adouble, vol->v_ad_options);
265             adp = &ad;
266         }
267
268         if ( ad_metadata( uname, 0, adp) < 0 ) {
269             switch (errno) {
270             case EACCES:
271                 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
272                     uname, strerror(errno));
273                 return AFPERR_ACCESS;
274             default:
275                 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
276                 return AFPERR_MISC;
277             }
278         }
279
280         FinderInfo = ad_entry(adp, ADEID_FINDERI);
281 #ifdef DEBUG
282         LOG(log_debug9, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
283         hexdump( FinderInfo, 32);
284 #endif
285
286         /* Now scan FinderInfo if its all 0 */
287         count = 32;
288         while (count--) {
289             if (*FinderInfo++) {
290                 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
291                 strcpy(attrnamebuf, ea_finderinfo);
292                 attrbuflen += strlen(ea_finderinfo) + 1;
293                 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
294                 break;
295             }
296         }
297
298         /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
299         LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
300         if (adp->ad_eid[ADEID_RFORK].ade_len > 0) {
301             LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
302             strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
303             attrbuflen += strlen(ea_resourcefork) + 1;      
304         }
305
306         /* Now list file attribute dir */
307         if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
308             if (errno == ELOOP) {
309                 /* its a symlink and client requested O_NOFOLLOW */
310                 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
311
312                 *(uint16_t *)rbuf = htons(bitmap);
313                 rbuf += 2;
314                 *rbuflen += 2;
315
316                 *(uint32_t *)rbuf = 0;
317                 *rbuflen += 4;
318
319                 ret = AFP_OK;
320                 goto exit;
321             }
322             LOG(log_error, logtype_afpd, "afp_listextattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
323             ret = AFPERR_MISC;
324             goto exit;
325         }
326         if (NULL == (dirp = fdopendir(attrdirfd))) {
327             LOG(log_error, logtype_afpd, "afp_listextattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
328             ret = AFPERR_MISC;
329             goto exit;
330         }
331
332         while ((dp = readdir(dirp)))  {
333             /* check if its "." or ".." */
334             if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
335                 (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
336                 continue;
337
338             len = strlen(dp->d_name);
339
340             if ( 0 >= ( len = convert_string(obj->options.unixcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
341                 ret = AFPERR_MISC;
342                 goto exit;
343             }
344             if (len == 255)
345                 /* convert_string didn't 0-terminate */
346                 attrnamebuf[attrbuflen + 255] = 0;
347                 
348             LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): attribute: %s", uname, dp->d_name);
349
350             attrbuflen += len + 1;
351             if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
352                 /* Next EA name could overflow, so bail out with error.
353                    FIXME: evantually malloc/memcpy/realloc whatever.
354                    Is it worth it ? */
355                 ret = AFPERR_MISC;
356                 goto exit;
357             }
358         }
359         buf_valid = 1;
360     }
361
362     /* Start building reply packet */
363     *(uint16_t *)rbuf = htons(bitmap);
364     rbuf += 2;
365     *rbuflen += 2;
366
367     *(uint32_t *)rbuf = htonl(attrbuflen);
368     rbuf += 4;
369     *rbuflen += 4;
370
371     if (maxreply && buf_valid) {
372         memcpy( rbuf, attrnamebuf, attrbuflen);
373         *rbuflen += attrbuflen;
374         buf_valid = 0;
375     }
376
377     ret = AFP_OK;
378
379 exit:
380     if (ret != AFP_OK)
381         buf_valid = 0;
382     if (adp)
383         ad_close_metadata( adp);
384     if (dirp) {
385         closedir(dirp);
386         dirp = NULL;
387     }
388     if (attrdirfd) {
389         close(attrdirfd);
390         attrdirfd = 0;
391     }
392
393     LOG(log_debug9, logtype_afpd, "afp_listextattr: END");
394     return ret;
395 }
396
397 int afp_getextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
398 {
399     int                 ret, oflag = 0;
400     uint16_t            vid, bitmap;
401     uint32_t            did, maxreply, attrnamelen;
402     char                attrmname[256], attruname[256];
403     struct vol          *vol;
404     struct dir          *dir;
405     struct path         *s_path;
406
407
408 #ifdef DEBUG
409     LOG(log_debug9, logtype_afpd, "afp_getextattr: BEGIN");
410 #endif
411
412     *rbuflen = 0;
413     ibuf += 2;
414
415     memcpy( &vid, ibuf, 2);
416     ibuf += 2;
417     if (NULL == ( vol = getvolbyvid( vid )) ) {
418         LOG(log_error, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
419         return AFPERR_ACCESS;
420     }
421
422     memcpy( &did, ibuf, 4);
423     ibuf += 4;
424     if (NULL == ( dir = dirlookup( vol, did )) ) {
425         LOG(log_error, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
426         return afp_errno;
427     }
428
429     memcpy( &bitmap, ibuf, 2);
430     bitmap = ntohs( bitmap );
431     ibuf += 2;
432     if (bitmap & kXAttrNoFollow)
433         oflag = AT_SYMLINK_NOFOLLOW;
434
435     /* Skip Offset and ReqCount */
436     ibuf += 16;
437
438     /* Get MaxReply */
439     maxreply = ntohl(*((uint32_t *)ibuf));
440     ibuf += 4;
441
442     /* get name */
443     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
444         LOG(log_error, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
445         return AFPERR_NOOBJ;
446     }
447
448     if ((unsigned long)ibuf & 1)
449         ibuf++;
450
451     /* get length of EA name */
452     attrnamelen = ntohs(*((uint16_t *)ibuf));
453     ibuf += 2;
454     if (attrnamelen > 255)
455         /* dont fool with us */
456         attrnamelen = 255;
457
458     /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
459     strncpy(attrmname, ibuf, attrnamelen);
460     attrmname[attrnamelen] = 0;
461
462     LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, attrmname);
463
464     /* Convert EA name in utf8 to unix charset */
465     if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
466         return AFPERR_MISC;
467
468     if (attrnamelen == 255)
469         /* convert_string didn't 0-terminate */
470         attruname[255] = 0;
471     
472     /* write bitmap now */
473     *(uint16_t *)rbuf = htons(bitmap);
474     rbuf += 2;
475     *rbuflen += 2;
476     
477     /*
478       Switch on maxreply:
479       if its 0 we must return the size of the requested attribute,
480       if its non 0 we must return the attribute.
481     */
482     if (maxreply == 0)
483         ret = getextattr_size(rbuf, rbuflen, s_path->u_name, oflag, attruname);
484     else
485         ret = getextattr_content(rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
486
487 #ifdef DEBUG
488     LOG(log_debug9, logtype_afpd, "afp_getextattr: END");
489 #endif
490
491     return ret;
492 }
493
494 int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
495 {
496     int                 len, oflag = O_CREAT | O_WRONLY, attrdirfd;
497     uint16_t            vid, bitmap;
498     uint32_t            did, attrnamelen, attrsize;
499     char                attrmname[256], attruname[256];
500     struct vol          *vol;
501     struct dir          *dir;
502     struct path         *s_path;
503
504 #ifdef DEBUG
505     LOG(log_debug9, logtype_afpd, "afp_setextattr: BEGIN");
506 #endif
507
508     *rbuflen = 0;
509     ibuf += 2;
510
511     memcpy( &vid, ibuf, 2);
512     ibuf += 2;
513     if (NULL == ( vol = getvolbyvid( vid )) ) {
514         LOG(log_error, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
515         return AFPERR_ACCESS;
516     }
517
518     memcpy( &did, ibuf, 4);
519     ibuf += 4;
520     if (NULL == ( dir = dirlookup( vol, did )) ) {
521         LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
522         return afp_errno;
523     }
524
525     memcpy( &bitmap, ibuf, 2);
526     bitmap = ntohs( bitmap );
527     ibuf += 2;
528     if (bitmap & kXAttrNoFollow)
529         oflag |= AT_SYMLINK_NOFOLLOW;
530     if (bitmap & kXAttrCreate)
531         oflag |= O_EXCL;
532     else if (bitmap & kXAttrReplace)
533         oflag |= O_TRUNC;
534
535     /* Skip Offset */
536     ibuf += 8;
537
538     /* get name */
539     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
540         LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
541         return AFPERR_NOOBJ;
542     }
543
544     if ((unsigned long)ibuf & 1)
545         ibuf++;
546
547     /* get length of EA name */
548     attrnamelen = ntohs(*((uint16_t *)ibuf));
549     ibuf += 2;
550     if (attrnamelen > 255)
551         return AFPERR_PARAM;
552
553     /* we must copy the name as its not 0-terminated and we cant write to ibuf */
554     strncpy(attrmname, ibuf, attrnamelen);
555     attrmname[attrnamelen] = 0;
556     ibuf += attrnamelen;
557
558     /* Convert EA name in utf8 to unix charset */
559     if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
560         return AFPERR_MISC;
561
562     if (attrnamelen == 255)
563         /* convert_string didn't 0-terminate */
564         attruname[255] = 0;
565
566     /* get EA size */
567     attrsize = ntohl(*((uint32_t *)ibuf));
568     ibuf += 4;
569     if (attrsize > MAX_EA_SIZE)
570         /* we arbitrarily make this fatal */
571         return AFPERR_PARAM;
572     
573     LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, attrmname, attrsize);
574
575     if ( -1 == (attrdirfd = attropen(s_path->u_name, attruname, oflag, 0666))) {
576         if (errno == ELOOP) {
577             /* its a symlink and client requested O_NOFOLLOW  */
578             LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
579             return AFP_OK;
580         }
581         LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
582         return AFPERR_MISC;
583     }
584     
585     while (1) {
586         len = write(attrdirfd, ibuf, attrsize);
587         if (len == -1) {
588             LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
589             return AFPERR_MISC;
590         }
591         attrsize -= len;
592         ibuf += len;
593         if (attrsize == 0)
594             break;
595     }
596
597 #ifdef DEBUG
598     LOG(log_debug9, logtype_afpd, "afp_setextattr: END");
599 #endif
600
601     return AFP_OK;
602 }
603
604 int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
605 {
606     int                 oflag = O_RDONLY, attrdirfd;
607     uint16_t            vid, bitmap;
608     uint32_t            did, attrnamelen;
609     char                attrmname[256], attruname[256];
610     struct vol          *vol;
611     struct dir          *dir;
612     struct path         *s_path;
613
614 #ifdef DEBUG
615     LOG(log_debug9, logtype_afpd, "afp_remextattr: BEGIN");
616 #endif
617
618     *rbuflen = 0;
619     ibuf += 2;
620
621     memcpy( &vid, ibuf, 2);
622     ibuf += 2;
623     if (NULL == ( vol = getvolbyvid( vid )) ) {
624         LOG(log_error, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
625         return AFPERR_ACCESS;
626     }
627
628     memcpy( &did, ibuf, 4);
629     ibuf += 4;
630     if (NULL == ( dir = dirlookup( vol, did )) ) {
631         LOG(log_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
632         return afp_errno;
633     }
634
635     memcpy( &bitmap, ibuf, 2);
636     bitmap = ntohs( bitmap );
637     ibuf += 2;
638     if (bitmap & kXAttrNoFollow)
639         oflag |= AT_SYMLINK_NOFOLLOW;
640
641     /* get name */
642     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
643         LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
644         return AFPERR_NOOBJ;
645     }
646
647     if ((unsigned long)ibuf & 1)
648         ibuf++;
649
650     /* get length of EA name */
651     attrnamelen = ntohs(*((uint16_t *)ibuf));
652     ibuf += 2;
653     if (attrnamelen > 255)
654         return AFPERR_PARAM;
655
656     /* we must copy the name as its not 0-terminated and we cant write to ibuf */
657     strncpy(attrmname, ibuf, attrnamelen);
658     attrmname[attrnamelen] = 0;
659     ibuf += attrnamelen;
660
661     /* Convert EA name in utf8 to unix charset */
662     if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
663         return AFPERR_MISC;
664
665     if (attrnamelen == 255)
666         /* convert_string didn't 0-terminate */
667         attruname[255] = 0;
668
669     LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, attrmname);
670
671     if ( -1 == (attrdirfd = attropen(s_path->u_name, ".", oflag))) {
672         switch (errno) {
673         case ELOOP:
674             /* its a symlink and client requested O_NOFOLLOW  */
675             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
676             return AFP_OK;
677         case EACCES:
678             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
679             return AFPERR_ACCESS;
680         default:
681             LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
682             return AFPERR_MISC;
683         }
684     }
685     
686     if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
687         if (errno == EACCES) {
688             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
689             return AFPERR_ACCESS;
690         }
691         LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
692         return AFPERR_MISC;
693     }
694
695 #ifdef DEBUG
696     LOG(log_debug9, logtype_afpd, "afp_remextattr: END");
697 #endif
698
699     return AFP_OK;
700 }
701
702
703
704 #endif /* HAVE_EXT_ATTRS */
705