2 $Id: extattrs.c,v 1.18 2009-10-29 10:34:15 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/vfs.h>
27 #include <atalk/afp.h>
28 #include <atalk/logger.h>
34 #include "directory.h"
38 static const char *ea_finderinfo = "com.apple.FinderInfo";
39 static const char *ea_resourcefork = "com.apple.ResourceFork";
41 /* This should be big enough to consecutively store the names of all attributes */
42 static char attrnamebuf[ATTRNAMEBUFSIZ];
45 static void hexdump(void *m, size_t l) {
52 len = sprintf(bufp, "%02x ", *p++);
56 if ((count % 16) == 0) {
57 LOG(log_debug9, logtype_afpd, "%s", buf);
64 /***************************************
66 ****************************************/
69 Note: we're being called twice. Firstly the client only want the size of all
70 EA names, secondly it wants these names. In order to avoid scanning EAs twice
71 we cache them in a static buffer.
73 int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
75 int ret, oflag = 0, adflags = 0;
76 uint16_t vid, bitmap, uint16;
77 uint32_t did, maxreply, tmpattr;
82 struct adouble ad, *adp = NULL;
84 char *uname, *FinderInfo;
85 char emptyFinderInfo[32] = { 0 };
87 static int buf_valid = 0;
88 static size_t attrbuflen = 0;
93 /* Get MaxReplySize first */
94 memcpy( &maxreply, ibuf + 14, sizeof (maxreply));
95 maxreply = ntohl( maxreply );
98 If its the first request with maxreply=0 or if we didn't mark our buffers valid for
99 whatever reason (just a safety check, it should be valid), then scan for attributes
101 if ((maxreply == 0) || (buf_valid == 0)) {
105 memcpy( &vid, ibuf, sizeof(vid));
107 if (NULL == ( vol = getvolbyvid( vid )) ) {
108 LOG(log_error, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
109 return AFPERR_ACCESS;
112 memcpy( &did, ibuf, sizeof(did));
114 if (NULL == ( dir = dirlookup( vol, did )) ) {
115 LOG(log_error, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
119 memcpy( &bitmap, ibuf, sizeof(bitmap));
120 bitmap = ntohs( bitmap );
121 ibuf += sizeof(bitmap);
123 #ifdef HAVE_SOLARIS_EAS
124 if (bitmap & kXAttrNoFollow)
127 /* Skip ReqCount, StartIndex and maxreply*/
131 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
132 LOG(log_error, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
137 if (!s_path->st_valid) {
138 /* it's a dir in our cache, we didn't stat it, do it now */
139 of_statdir(vol, s_path);
141 if ( s_path->st_errno != 0 ) {
142 return( AFPERR_NOOBJ );
145 uname = s_path->u_name; /*
146 We have to check the FinderInfo for the file, because if they aren't all 0
147 we must return the synthetic attribute "com.apple.FinderInfo".
148 Note: the client will never (never seen in traces) request that attribute
151 if ((of = of_findname(s_path))) {
154 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
158 if (S_ISDIR(st->st_mode))
159 adflags = ADFLAGS_DIR;
161 if ( ad_metadata( uname, adflags, adp) < 0 ) {
164 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
165 uname, strerror(errno));
166 return AFPERR_ACCESS;
168 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
173 FinderInfo = ad_entry(adp, ADEID_FINDERI);
176 LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
177 hexdump( FinderInfo, 32);
180 if ((adflags & ADFLAGS_DIR)) {
181 /* set default view */
182 uint16 = htons(FINDERINFO_CLOSEDVIEW);
183 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
186 /* Check if FinderInfo equals default and empty FinderInfo*/
187 if ((memcmp(FinderInfo, emptyFinderInfo, 32)) != 0) {
188 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
189 strcpy(attrnamebuf, ea_finderinfo);
190 attrbuflen += strlen(ea_finderinfo) + 1;
191 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
194 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
195 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
196 if (adp->ad_eid[ADEID_RFORK].ade_len > 0) {
197 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
198 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
199 attrbuflen += strlen(ea_resourcefork) + 1;
202 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
206 /* its a symlink and client requested O_NOFOLLOW */
207 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
220 /* Start building reply packet */
221 bitmap = htons(bitmap);
222 memcpy( rbuf, &bitmap, sizeof(bitmap));
223 rbuf += sizeof(bitmap);
224 *rbuflen += sizeof(bitmap);
226 tmpattr = htonl(attrbuflen);
227 memcpy( rbuf, &tmpattr, sizeof(tmpattr));
228 rbuf += sizeof(tmpattr);
229 *rbuflen += sizeof(tmpattr);
231 /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
232 and we didnt have an error (buf_valid) */
233 if (maxreply && buf_valid) {
234 memcpy( rbuf, attrnamebuf, attrbuflen);
235 *rbuflen += attrbuflen;
245 ad_close_metadata( adp);
250 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
253 uint16_t vid, bitmap, attrnamelen;
254 uint32_t did, maxreply;
255 char attrmname[256], attruname[256];
264 memcpy( &vid, ibuf, sizeof(vid));
266 if (NULL == ( vol = getvolbyvid( vid )) ) {
267 LOG(log_error, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
268 return AFPERR_ACCESS;
271 memcpy( &did, ibuf, sizeof(did));
273 if (NULL == ( dir = dirlookup( vol, did )) ) {
274 LOG(log_error, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
278 memcpy( &bitmap, ibuf, sizeof(bitmap));
279 bitmap = ntohs( bitmap );
280 ibuf += sizeof(bitmap);
282 #ifdef HAVE_SOLARIS_EAS
283 if (bitmap & kXAttrNoFollow)
287 /* Skip Offset and ReqCount */
291 memcpy(&maxreply, ibuf, sizeof(maxreply));
292 maxreply = ntohl(maxreply);
293 ibuf += sizeof(maxreply);
296 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
297 LOG(log_error, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
301 if ((unsigned long)ibuf & 1)
304 /* get length of EA name */
305 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
306 attrnamelen = ntohs(attrnamelen);
307 ibuf += sizeof(attrnamelen);
308 if (attrnamelen > 255)
309 /* dont fool with us */
312 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
313 strncpy(attrmname, ibuf, attrnamelen);
314 attrmname[attrnamelen] = 0;
316 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, attrmname);
318 /* Convert EA name in utf8 to unix charset */
319 if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
322 if (attrnamelen == 255)
323 /* convert_string didn't 0-terminate */
326 /* write bitmap now */
327 bitmap = htons(bitmap);
328 memcpy(rbuf, &bitmap, sizeof(bitmap));
329 rbuf += sizeof(bitmap);
330 *rbuflen += sizeof(bitmap);
334 if its 0 we must return the size of the requested attribute,
335 if its non 0 we must return the attribute.
338 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
340 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
345 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
347 int oflag = O_CREAT | O_WRONLY, ret;
348 uint16_t vid, bitmap, attrnamelen;
349 uint32_t did, attrsize;
350 char attrmname[256], attruname[256];
358 memcpy( &vid, ibuf, sizeof(vid));
360 if (NULL == ( vol = getvolbyvid( vid )) ) {
361 LOG(log_error, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
362 return AFPERR_ACCESS;
365 memcpy( &did, ibuf, sizeof(did));
367 if (NULL == ( dir = dirlookup( vol, did )) ) {
368 LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
372 memcpy( &bitmap, ibuf, sizeof(bitmap));
373 bitmap = ntohs( bitmap );
374 ibuf += sizeof(bitmap);
376 #ifdef HAVE_SOLARIS_EAS
377 if (bitmap & kXAttrNoFollow)
378 oflag |= AT_SYMLINK_NOFOLLOW;
381 if (bitmap & kXAttrCreate)
383 else if (bitmap & kXAttrReplace)
390 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
391 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
395 if ((unsigned long)ibuf & 1)
398 /* get length of EA name */
399 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
400 attrnamelen = ntohs(attrnamelen);
401 ibuf += sizeof(attrnamelen);
402 if (attrnamelen > 255)
405 /* we must copy the name as its not 0-terminated and we cant write to ibuf */
406 strncpy(attrmname, ibuf, attrnamelen);
407 attrmname[attrnamelen] = 0;
410 /* Convert EA name in utf8 to unix charset */
411 if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
414 if (attrnamelen == 255)
415 /* convert_string didn't 0-terminate */
419 memcpy(&attrsize, ibuf, sizeof(attrsize));
420 attrsize = ntohl(attrsize);
421 ibuf += sizeof(attrsize);
422 if (attrsize > MAX_EA_SIZE)
423 /* we arbitrarily make this fatal */
426 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, attrmname, attrsize);
428 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
433 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
435 int oflag = O_RDONLY, ret;
436 uint16_t vid, bitmap, attrnamelen;
438 char attrmname[256], attruname[256];
446 memcpy( &vid, ibuf, sizeof(vid));
448 if (NULL == ( vol = getvolbyvid( vid )) ) {
449 LOG(log_error, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
450 return AFPERR_ACCESS;
453 memcpy( &did, ibuf, sizeof(did));
455 if (NULL == ( dir = dirlookup( vol, did )) ) {
456 LOG(log_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
460 memcpy( &bitmap, ibuf, sizeof(bitmap));
461 bitmap = ntohs( bitmap );
462 ibuf += sizeof(bitmap);
464 #ifdef HAVE_SOLARIS_EAS
465 if (bitmap & kXAttrNoFollow)
466 oflag |= AT_SYMLINK_NOFOLLOW;
470 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
471 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
475 if ((unsigned long)ibuf & 1)
478 /* get length of EA name */
479 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
480 attrnamelen = ntohs(attrnamelen);
481 ibuf += sizeof(attrnamelen);
482 if (attrnamelen > 255)
485 /* we must copy the name as its not 0-terminated and we cant write to ibuf */
486 strncpy(attrmname, ibuf, attrnamelen);
487 attrmname[attrnamelen] = 0;
490 /* Convert EA name in utf8 to unix charset */
491 if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
494 if (attrnamelen == 255)
495 /* convert_string didn't 0-terminate */
498 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, attrmname);
500 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);