2 $Id: extattrs.c,v 1.20 2009-10-29 12:48:34 didg 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 */
25 #include <atalk/adouble.h>
26 #include <atalk/util.h>
27 #include <atalk/vfs.h>
28 #include <atalk/afp.h>
29 #include <atalk/logger.h>
35 #include "directory.h"
39 static const char *ea_finderinfo = "com.apple.FinderInfo";
40 static const char *ea_resourcefork = "com.apple.ResourceFork";
42 /* This should be big enough to consecutively store the names of all attributes */
43 static char attrnamebuf[ATTRNAMEBUFSIZ];
46 static void hexdump(void *m, size_t l) {
53 len = sprintf(bufp, "%02x ", *p++);
57 if ((count % 16) == 0) {
58 LOG(log_debug9, logtype_afpd, "%s", buf);
65 /***************************************
67 ****************************************/
70 Note: we're being called twice. Firstly the client only want the size of all
71 EA names, secondly it wants these names. In order to avoid scanning EAs twice
72 we cache them in a static buffer.
74 int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
76 int ret, oflag = 0, adflags = 0;
77 uint16_t vid, bitmap, uint16;
78 uint32_t did, maxreply, tmpattr;
83 struct adouble ad, *adp = NULL;
85 char *uname, *FinderInfo;
86 char emptyFinderInfo[32] = { 0 };
88 static int buf_valid = 0;
89 static size_t attrbuflen = 0;
94 /* Get MaxReplySize first */
95 memcpy( &maxreply, ibuf + 14, sizeof (maxreply));
96 maxreply = ntohl( maxreply );
99 If its the first request with maxreply=0 or if we didn't mark our buffers valid for
100 whatever reason (just a safety check, it should be valid), then scan for attributes
102 if ((maxreply == 0) || (buf_valid == 0)) {
106 memcpy( &vid, ibuf, sizeof(vid));
108 if (NULL == ( vol = getvolbyvid( vid )) ) {
109 LOG(log_error, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
110 return AFPERR_ACCESS;
113 memcpy( &did, ibuf, sizeof(did));
115 if (NULL == ( dir = dirlookup( vol, did )) ) {
116 LOG(log_error, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
120 memcpy( &bitmap, ibuf, sizeof(bitmap));
121 bitmap = ntohs( bitmap );
122 ibuf += sizeof(bitmap);
124 #ifdef HAVE_SOLARIS_EAS
125 if (bitmap & kXAttrNoFollow)
128 /* Skip ReqCount, StartIndex and maxreply*/
132 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
133 LOG(log_error, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
138 if (!s_path->st_valid) {
139 /* it's a dir in our cache, we didn't stat it, do it now */
140 of_statdir(vol, s_path);
142 if ( s_path->st_errno != 0 ) {
143 return( AFPERR_NOOBJ );
146 uname = s_path->u_name; /*
147 We have to check the FinderInfo for the file, because if they aren't all 0
148 we must return the synthetic attribute "com.apple.FinderInfo".
149 Note: the client will never (never seen in traces) request that attribute
152 if ((of = of_findname(s_path))) {
155 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
159 if (S_ISDIR(st->st_mode))
160 adflags = ADFLAGS_DIR;
162 if ( ad_metadata( uname, adflags, adp) < 0 ) {
165 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
166 uname, strerror(errno));
167 return AFPERR_ACCESS;
169 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
174 FinderInfo = ad_entry(adp, ADEID_FINDERI);
177 LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
178 hexdump( FinderInfo, 32);
181 if ((adflags & ADFLAGS_DIR)) {
182 /* set default view */
183 uint16 = htons(FINDERINFO_CLOSEDVIEW);
184 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
187 /* Check if FinderInfo equals default and empty FinderInfo*/
188 if ((memcmp(FinderInfo, emptyFinderInfo, 32)) != 0) {
189 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
190 strcpy(attrnamebuf, ea_finderinfo);
191 attrbuflen += strlen(ea_finderinfo) + 1;
192 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
195 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
196 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
197 if (adp->ad_eid[ADEID_RFORK].ade_len > 0) {
198 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
199 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
200 attrbuflen += strlen(ea_resourcefork) + 1;
203 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
207 /* its a symlink and client requested O_NOFOLLOW */
208 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
221 /* Start building reply packet */
222 bitmap = htons(bitmap);
223 memcpy( rbuf, &bitmap, sizeof(bitmap));
224 rbuf += sizeof(bitmap);
225 *rbuflen += sizeof(bitmap);
227 tmpattr = htonl(attrbuflen);
228 memcpy( rbuf, &tmpattr, sizeof(tmpattr));
229 rbuf += sizeof(tmpattr);
230 *rbuflen += sizeof(tmpattr);
232 /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
233 and we didnt have an error (buf_valid) */
234 if (maxreply && buf_valid) {
235 memcpy( rbuf, attrnamebuf, attrbuflen);
236 *rbuflen += attrbuflen;
246 ad_close_metadata( adp);
251 static char *to_stringz(char *ibuf, uint16_t len)
253 static char attrmname[256];
256 /* dont fool with us */
259 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
260 strlcpy(attrmname, ibuf, len);
264 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
267 uint16_t vid, bitmap, attrnamelen;
268 uint32_t did, maxreply;
278 memcpy( &vid, ibuf, sizeof(vid));
280 if (NULL == ( vol = getvolbyvid( vid )) ) {
281 LOG(log_error, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
282 return AFPERR_ACCESS;
285 memcpy( &did, ibuf, sizeof(did));
287 if (NULL == ( dir = dirlookup( vol, did )) ) {
288 LOG(log_error, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
292 memcpy( &bitmap, ibuf, sizeof(bitmap));
293 bitmap = ntohs( bitmap );
294 ibuf += sizeof(bitmap);
296 #ifdef HAVE_SOLARIS_EAS
297 if (bitmap & kXAttrNoFollow)
301 /* Skip Offset and ReqCount */
305 memcpy(&maxreply, ibuf, sizeof(maxreply));
306 maxreply = ntohl(maxreply);
307 ibuf += sizeof(maxreply);
310 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
311 LOG(log_error, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
315 if ((unsigned long)ibuf & 1)
318 /* get length of EA name */
319 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
320 attrnamelen = ntohs(attrnamelen);
321 ibuf += sizeof(attrnamelen);
323 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
325 /* Convert EA name in utf8 to unix charset */
326 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
329 /* write bitmap now */
330 bitmap = htons(bitmap);
331 memcpy(rbuf, &bitmap, sizeof(bitmap));
332 rbuf += sizeof(bitmap);
333 *rbuflen += sizeof(bitmap);
337 if its 0 we must return the size of the requested attribute,
338 if its non 0 we must return the attribute.
341 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
343 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
348 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
350 int oflag = O_CREAT | O_WRONLY, ret;
351 uint16_t vid, bitmap, attrnamelen;
352 uint32_t did, attrsize;
362 memcpy( &vid, ibuf, sizeof(vid));
364 if (NULL == ( vol = getvolbyvid( vid )) ) {
365 LOG(log_error, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
366 return AFPERR_ACCESS;
369 memcpy( &did, ibuf, sizeof(did));
371 if (NULL == ( dir = dirlookup( vol, did )) ) {
372 LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
376 memcpy( &bitmap, ibuf, sizeof(bitmap));
377 bitmap = ntohs( bitmap );
378 ibuf += sizeof(bitmap);
380 #ifdef HAVE_SOLARIS_EAS
381 if (bitmap & kXAttrNoFollow)
382 oflag |= AT_SYMLINK_NOFOLLOW;
385 if (bitmap & kXAttrCreate)
387 else if (bitmap & kXAttrReplace)
394 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
395 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
399 if ((unsigned long)ibuf & 1)
402 /* get length of EA name */
403 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
404 attrnamelen = ntohs(attrnamelen);
405 ibuf += sizeof(attrnamelen);
406 if (attrnamelen > 255)
410 /* Convert EA name in utf8 to unix charset */
411 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
416 memcpy(&attrsize, ibuf, sizeof(attrsize));
417 attrsize = ntohl(attrsize);
418 ibuf += sizeof(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, to_stringz(attrmname, attrnamelen), 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 _U_, size_t *rbuflen)
432 int oflag = O_RDONLY, ret;
433 uint16_t vid, bitmap, attrnamelen;
443 memcpy( &vid, ibuf, sizeof(vid));
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, sizeof(did));
452 if (NULL == ( dir = dirlookup( vol, did )) ) {
453 LOG(log_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
457 memcpy( &bitmap, ibuf, sizeof(bitmap));
458 bitmap = ntohs( bitmap );
459 ibuf += sizeof(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, sizeof(attrnamelen));
477 attrnamelen = ntohs(attrnamelen);
478 ibuf += sizeof(attrnamelen);
479 if (attrnamelen > 255)
482 /* Convert EA name in utf8 to unix charset */
483 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
486 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
488 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);