2 $Id: extattrs.c,v 1.1 2009-02-16 13:49:20 franklahm Exp $
3 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
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.
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.
16 /* According to man fsattr.5 we must define _ATFILE_SOURCE */
17 #define _ATFILE_SOURCE
21 #endif /* HAVE_CONFIG_H */
29 #include <sys/types.h>
34 #include <atalk/adouble.h>
35 #include <atalk/afp.h>
36 #include <atalk/logger.h>
41 #include "directory.h"
45 char *ea_finderinfo = "com.apple.FinderInfo";
46 char *ea_resourcefork = "com.apple.ResourceFork";
48 static void hexdump(void *m, size_t l) {
55 len = sprintf(bufp, "%02x ", *p++);
59 if ((count % 16) == 0) {
60 LOG(log_debug9, logtype_afpd, "%s", buf);
66 static int getextattr_size(char *rbuf, int *rbuflen, char *uname, int oflag, char *attruname)
72 LOG(log_debug7, logtype_afpd, "getextattr_size(%s): attribute: \"%s\"", uname, attruname);
74 if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
76 /* its a symlink and client requested O_NOFOLLOW */
77 LOG(log_debug, logtype_afpd, "getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
79 *(uint32_t *)rbuf = 0;
84 LOG(log_error, logtype_afpd, "getextattr_size: attropen error: %s", strerror(errno));
88 if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
89 LOG(log_error, logtype_afpd, "getextattr_size: fstatat error: %s", strerror(errno));
93 attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
95 /* Start building reply packet */
97 LOG(log_debug7, logtype_afpd, "getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
99 /* length of attribute data */
100 *(uint32_t *)rbuf = htonl((uint32_t)attrsize);
110 static int getextattr_content(char *rbuf, int *rbuflen, char *uname, int oflag, char *attruname, int maxreply)
113 size_t toread, okread = 0, len;
114 uint32_t *datalength;
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);
122 *(uint32_t *)rbuf = 0;
127 LOG(log_error, logtype_afpd, "getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
131 if ( -1 == (fstat(attrdirfd, &st))) {
132 LOG(log_error, logtype_afpd, "getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
137 /* Start building reply packet */
139 maxreply -= MAX_REPLY_EXTRA_BYTES;
140 if (maxreply > MAX_EA_SIZE)
141 maxreply = MAX_EA_SIZE;
143 /* But never send more than the client requested */
144 toread = (maxreply < st.st_size) ? maxreply : st.st_size;
146 LOG(log_debug7, logtype_afpd, "getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
148 /* remember where we must store length of attribute data in rbuf */
149 datalength = (uint32_t *)rbuf;
154 len = read(attrdirfd, rbuf, toread);
156 LOG(log_error, logtype_afpd, "getextattr_content(%s): read error: %s", attruname, strerror(errno));
163 if ((len == 0) || (okread == toread))
167 *datalength = htonl((uint32_t)okread);
177 /***************************************
179 ****************************************/
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.
186 int afp_listextattr(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
188 int count, attrdirfd = 0, ret, len, oflag = 0;
189 uint16_t vid, bitmap;
190 uint32_t did, maxreply;
194 struct adouble ad, *adp = NULL;
197 char *uname, *FinderInfo;
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];
206 LOG(log_debug9, logtype_afpd, "afp_listextattr: BEGIN");
212 /* Get MaxReplySize first */
213 memcpy( &maxreply, ibuf + 14, 4);
214 maxreply = ntohl( maxreply );
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
220 if ((maxreply == 0) || (buf_valid == 0)) {
224 memcpy( &vid, 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;
231 memcpy( &did, ibuf, 4);
233 if (NULL == ( dir = dirlookup( vol, did )) ) {
234 LOG(log_error, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
238 memcpy( &bitmap, ibuf, 2);
239 bitmap = ntohs( bitmap );
242 if (bitmap & kXAttrNoFollow)
245 /* Skip ReqCount, StartIndex and maxreply*/
249 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
250 LOG(log_error, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
253 uname = s_path->u_name;
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
261 if ((of = of_findname(s_path))) {
264 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
268 if ( ad_metadata( uname, 0, adp) < 0 ) {
271 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
272 uname, strerror(errno));
273 return AFPERR_ACCESS;
275 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
280 FinderInfo = ad_entry(adp, ADEID_FINDERI);
282 LOG(log_debug9, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
283 hexdump( FinderInfo, 32);
286 /* Now scan FinderInfo if its all 0 */
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);
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;
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);
312 *(uint16_t *)rbuf = htons(bitmap);
316 *(uint32_t *)rbuf = 0;
322 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
326 if (NULL == (dirp = fdopendir(attrdirfd))) {
327 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
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))
338 len = strlen(dp->d_name);
340 if ( 0 >= ( len = convert_string(obj->options.unixcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
345 /* convert_string didn't 0-terminate */
346 attrnamebuf[attrbuflen + 255] = 0;
348 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): attribute: %s", uname, dp->d_name);
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.
362 /* Start building reply packet */
363 *(uint16_t *)rbuf = htons(bitmap);
367 *(uint32_t *)rbuf = htonl(attrbuflen);
371 if (maxreply && buf_valid) {
372 memcpy( rbuf, attrnamebuf, attrbuflen);
373 *rbuflen += attrbuflen;
383 ad_close_metadata( adp);
393 LOG(log_debug9, logtype_afpd, "afp_listextattr: END");
397 int afp_getextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
400 uint16_t vid, bitmap;
401 uint32_t did, maxreply, attrnamelen;
402 char attrmname[256], attruname[256];
409 LOG(log_debug9, logtype_afpd, "afp_getextattr: BEGIN");
415 memcpy( &vid, 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;
422 memcpy( &did, ibuf, 4);
424 if (NULL == ( dir = dirlookup( vol, did )) ) {
425 LOG(log_error, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
429 memcpy( &bitmap, ibuf, 2);
430 bitmap = ntohs( bitmap );
432 if (bitmap & kXAttrNoFollow)
433 oflag = AT_SYMLINK_NOFOLLOW;
435 /* Skip Offset and ReqCount */
439 maxreply = ntohl(*((uint32_t *)ibuf));
443 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
444 LOG(log_error, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
448 if ((unsigned long)ibuf & 1)
451 /* get length of EA name */
452 attrnamelen = ntohs(*((uint16_t *)ibuf));
454 if (attrnamelen > 255)
455 /* dont fool with us */
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;
462 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, attrmname);
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)) )
468 if (attrnamelen == 255)
469 /* convert_string didn't 0-terminate */
472 /* write bitmap now */
473 *(uint16_t *)rbuf = htons(bitmap);
479 if its 0 we must return the size of the requested attribute,
480 if its non 0 we must return the attribute.
483 ret = getextattr_size(rbuf, rbuflen, s_path->u_name, oflag, attruname);
485 ret = getextattr_content(rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
488 LOG(log_debug9, logtype_afpd, "afp_getextattr: END");
494 int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
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];
505 LOG(log_debug9, logtype_afpd, "afp_setextattr: BEGIN");
511 memcpy( &vid, 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;
518 memcpy( &did, ibuf, 4);
520 if (NULL == ( dir = dirlookup( vol, did )) ) {
521 LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
525 memcpy( &bitmap, ibuf, 2);
526 bitmap = ntohs( bitmap );
528 if (bitmap & kXAttrNoFollow)
529 oflag |= AT_SYMLINK_NOFOLLOW;
530 if (bitmap & kXAttrCreate)
532 else if (bitmap & kXAttrReplace)
539 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
540 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
544 if ((unsigned long)ibuf & 1)
547 /* get length of EA name */
548 attrnamelen = ntohs(*((uint16_t *)ibuf));
550 if (attrnamelen > 255)
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;
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)) )
562 if (attrnamelen == 255)
563 /* convert_string didn't 0-terminate */
567 attrsize = ntohl(*((uint32_t *)ibuf));
569 if (attrsize > MAX_EA_SIZE)
570 /* we arbitrarily make this fatal */
573 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, attrmname, attrsize);
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);
581 LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
586 len = write(attrdirfd, ibuf, attrsize);
588 LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
598 LOG(log_debug9, logtype_afpd, "afp_setextattr: END");
604 int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
606 int oflag = O_RDONLY, attrdirfd;
607 uint16_t vid, bitmap;
608 uint32_t did, attrnamelen;
609 char attrmname[256], attruname[256];
615 LOG(log_debug9, logtype_afpd, "afp_remextattr: BEGIN");
621 memcpy( &vid, 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;
628 memcpy( &did, ibuf, 4);
630 if (NULL == ( dir = dirlookup( vol, did )) ) {
631 LOG(log_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
635 memcpy( &bitmap, ibuf, 2);
636 bitmap = ntohs( bitmap );
638 if (bitmap & kXAttrNoFollow)
639 oflag |= AT_SYMLINK_NOFOLLOW;
642 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
643 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
647 if ((unsigned long)ibuf & 1)
650 /* get length of EA name */
651 attrnamelen = ntohs(*((uint16_t *)ibuf));
653 if (attrnamelen > 255)
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;
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)) )
665 if (attrnamelen == 255)
666 /* convert_string didn't 0-terminate */
669 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, attrmname);
671 if ( -1 == (attrdirfd = attropen(s_path->u_name, ".", oflag))) {
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);
678 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
679 return AFPERR_ACCESS;
681 LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
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;
691 LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
696 LOG(log_debug9, logtype_afpd, "afp_remextattr: END");
704 #endif /* HAVE_EXT_ATTRS */