2 $Id: extattrs.c,v 1.10 2009-10-22 13:40:11 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.
18 #endif /* HAVE_CONFIG_H */
24 #include <sys/types.h>
29 #include <atalk/adouble.h>
30 #include <atalk/vfs.h>
31 #include <atalk/afp.h>
32 #include <atalk/logger.h>
38 #include "directory.h"
42 static char *ea_finderinfo = "com.apple.FinderInfo";
43 static char *ea_resourcefork = "com.apple.ResourceFork";
45 /* This should be big enough to consecutively store the names of all attributes */
46 static char attrnamebuf[ATTRNAMEBUFSIZ];
49 static void hexdump(void *m, size_t l) {
56 len = sprintf(bufp, "%02x ", *p++);
60 if ((count % 16) == 0) {
61 LOG(log_debug9, logtype_afpd, "%s", buf);
68 /***************************************
70 ****************************************/
73 Note: we're being called twice. Firstly the client only want the size of all
74 EA names, secondly it wants these names. In order to avoid scanning EAs twice
75 we cache them in a static buffer.
77 int afp_listextattr(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
79 int ret, oflag = 0, adflags = 0;
80 uint16_t vid, bitmap, uint16;
81 uint32_t did, maxreply, tmpattr;
86 struct adouble ad, *adp = NULL;
88 char *uname, *FinderInfo;
89 char emptyFinderInfo[32] = { 0 };
91 static int buf_valid = 0;
92 static size_t attrbuflen = 0;
97 /* Get MaxReplySize first */
98 memcpy( &maxreply, ibuf + 14, 4);
99 maxreply = ntohl( maxreply );
102 If its the first request with maxreply=0 or if we didn't mark our buffers valid for
103 whatever reason (just a safety check, it should be valid), then scan for attributes
105 if ((maxreply == 0) || (buf_valid == 0)) {
109 memcpy( &vid, ibuf, 2);
111 if (NULL == ( vol = getvolbyvid( vid )) ) {
112 LOG(log_error, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
113 return AFPERR_ACCESS;
116 memcpy( &did, ibuf, 4);
118 if (NULL == ( dir = dirlookup( vol, did )) ) {
119 LOG(log_error, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
123 memcpy( &bitmap, ibuf, 2);
124 bitmap = ntohs( bitmap );
127 #ifdef HAVE_SOLARIS_EAS
128 if (bitmap & kXAttrNoFollow)
131 /* Skip ReqCount, StartIndex and maxreply*/
135 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
136 LOG(log_error, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
139 uname = s_path->u_name;
142 We have to check the FinderInfo for the file, because if they aren't all 0
143 we must return the synthetic attribute "com.apple.FinderInfo".
144 Note: the client will never (never seen in traces) request that attribute
147 if ((of = of_findname(s_path))) {
150 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
155 if (S_ISDIR(st.st_mode))
156 adflags = ADFLAGS_DIR;
158 if ( ad_metadata( uname, adflags, adp) < 0 ) {
161 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
162 uname, strerror(errno));
163 return AFPERR_ACCESS;
165 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
170 FinderInfo = ad_entry(adp, ADEID_FINDERI);
173 LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
174 hexdump( FinderInfo, 32);
177 if ((adflags & ADFLAGS_DIR)) {
178 /* set default view */
179 uint16 = htons(FINDERINFO_CLOSEDVIEW);
180 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
183 /* Check if FinderInfo equals default and empty FinderInfo*/
184 if ((memcmp(FinderInfo, emptyFinderInfo, 32)) != 0) {
185 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
186 strcpy(attrnamebuf, ea_finderinfo);
187 attrbuflen += strlen(ea_finderinfo) + 1;
188 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
191 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
192 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
193 if (adp->ad_eid[ADEID_RFORK].ade_len > 0) {
194 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
195 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
196 attrbuflen += strlen(ea_resourcefork) + 1;
199 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
203 /* its a symlink and client requested O_NOFOLLOW */
204 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
217 /* Start building reply packet */
218 bitmap = htons(bitmap);
219 memcpy( rbuf, &bitmap, 2);
223 tmpattr = htonl(attrbuflen);
224 memcpy( rbuf, &tmpattr, 4);
228 /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
229 and we didnt have an error (buf_valid) */
230 if (maxreply && buf_valid) {
231 memcpy( rbuf, attrnamebuf, attrbuflen);
232 *rbuflen += attrbuflen;
242 ad_close_metadata( adp);
247 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
250 uint16_t vid, bitmap;
251 uint32_t did, maxreply, attrnamelen;
252 char attrmname[256], attruname[256];
261 memcpy( &vid, ibuf, 2);
263 if (NULL == ( vol = getvolbyvid( vid )) ) {
264 LOG(log_error, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
265 return AFPERR_ACCESS;
268 memcpy( &did, ibuf, 4);
270 if (NULL == ( dir = dirlookup( vol, did )) ) {
271 LOG(log_error, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
275 memcpy( &bitmap, ibuf, 2);
276 bitmap = ntohs( bitmap );
279 #ifdef HAVE_SOLARIS_EAS
280 if (bitmap & kXAttrNoFollow)
284 /* Skip Offset and ReqCount */
288 memcpy(&maxreply, ibuf, 4);
289 maxreply = ntohl(maxreply);
293 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
294 LOG(log_error, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
298 if ((unsigned long)ibuf & 1)
301 /* get length of EA name */
302 memcpy(&attrnamelen, ibuf, 2);
303 attrnamelen = ntohs(attrnamelen);
305 if (attrnamelen > 255)
306 /* dont fool with us */
309 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
310 strncpy(attrmname, ibuf, attrnamelen);
311 attrmname[attrnamelen] = 0;
313 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, attrmname);
315 /* Convert EA name in utf8 to unix charset */
316 if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
319 if (attrnamelen == 255)
320 /* convert_string didn't 0-terminate */
323 /* write bitmap now */
324 bitmap = htons(bitmap);
325 memcpy(rbuf, &bitmap, 2);
331 if its 0 we must return the size of the requested attribute,
332 if its non 0 we must return the attribute.
335 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
337 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
342 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
344 int oflag = O_CREAT | O_WRONLY, ret;
345 uint16_t vid, bitmap;
346 uint32_t did, attrnamelen, attrsize;
347 char attrmname[256], attruname[256];
355 memcpy( &vid, ibuf, 2);
357 if (NULL == ( vol = getvolbyvid( vid )) ) {
358 LOG(log_error, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
359 return AFPERR_ACCESS;
362 memcpy( &did, ibuf, 4);
364 if (NULL == ( dir = dirlookup( vol, did )) ) {
365 LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
369 memcpy( &bitmap, ibuf, 2);
370 bitmap = ntohs( bitmap );
373 #ifdef HAVE_SOLARIS_EAS
374 if (bitmap & kXAttrNoFollow)
375 oflag |= AT_SYMLINK_NOFOLLOW;
378 if (bitmap & kXAttrCreate)
380 else if (bitmap & kXAttrReplace)
387 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
388 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
392 if ((unsigned long)ibuf & 1)
395 /* get length of EA name */
396 memcpy(&attrnamelen, ibuf, 2);
397 attrnamelen = ntohs(attrnamelen);
399 if (attrnamelen > 255)
402 /* we must copy the name as its not 0-terminated and we cant write to ibuf */
403 strncpy(attrmname, ibuf, attrnamelen);
404 attrmname[attrnamelen] = 0;
407 /* Convert EA name in utf8 to unix charset */
408 if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
411 if (attrnamelen == 255)
412 /* convert_string didn't 0-terminate */
416 memcpy(&attrsize, ibuf, 4);
417 attrsize = ntohl(attrsize);
419 if (attrsize > MAX_EA_SIZE)
420 /* we arbitrarily make this fatal */
423 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, attrmname, attrsize);
425 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
430 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
432 int oflag = O_RDONLY, ret;
433 uint16_t vid, bitmap;
434 uint32_t did, attrnamelen;
435 char attrmname[256], attruname[256];
443 memcpy( &vid, ibuf, 2);
445 if (NULL == ( vol = getvolbyvid( vid )) ) {
446 LOG(log_error, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
447 return AFPERR_ACCESS;
450 memcpy( &did, ibuf, 4);
452 if (NULL == ( dir = dirlookup( vol, did )) ) {
453 LOG(log_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
457 memcpy( &bitmap, ibuf, 2);
458 bitmap = ntohs( bitmap );
461 #ifdef HAVE_SOLARIS_EAS
462 if (bitmap & kXAttrNoFollow)
463 oflag |= AT_SYMLINK_NOFOLLOW;
467 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
468 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
472 if ((unsigned long)ibuf & 1)
475 /* get length of EA name */
476 memcpy(&attrnamelen, ibuf, 2);
477 attrnamelen = ntohs(attrnamelen);
479 if (attrnamelen > 255)
482 /* we must copy the name as its not 0-terminated and we cant write to ibuf */
483 strncpy(attrmname, ibuf, attrnamelen);
484 attrmname[attrnamelen] = 0;
487 /* Convert EA name in utf8 to unix charset */
488 if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
491 if (attrnamelen == 255)
492 /* convert_string didn't 0-terminate */
495 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, attrmname);
497 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);