2 $Id: extattrs.c,v 1.19 2009-10-29 10:53:52 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, 256)) )
322 /* write bitmap now */
323 bitmap = htons(bitmap);
324 memcpy(rbuf, &bitmap, sizeof(bitmap));
325 rbuf += sizeof(bitmap);
326 *rbuflen += sizeof(bitmap);
330 if its 0 we must return the size of the requested attribute,
331 if its non 0 we must return the attribute.
334 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
336 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
341 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
343 int oflag = O_CREAT | O_WRONLY, ret;
344 uint16_t vid, bitmap, attrnamelen;
345 uint32_t did, attrsize;
346 char attrmname[256], attruname[256];
354 memcpy( &vid, ibuf, sizeof(vid));
356 if (NULL == ( vol = getvolbyvid( vid )) ) {
357 LOG(log_error, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
358 return AFPERR_ACCESS;
361 memcpy( &did, ibuf, sizeof(did));
363 if (NULL == ( dir = dirlookup( vol, did )) ) {
364 LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
368 memcpy( &bitmap, ibuf, sizeof(bitmap));
369 bitmap = ntohs( bitmap );
370 ibuf += sizeof(bitmap);
372 #ifdef HAVE_SOLARIS_EAS
373 if (bitmap & kXAttrNoFollow)
374 oflag |= AT_SYMLINK_NOFOLLOW;
377 if (bitmap & kXAttrCreate)
379 else if (bitmap & kXAttrReplace)
386 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
387 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
391 if ((unsigned long)ibuf & 1)
394 /* get length of EA name */
395 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
396 attrnamelen = ntohs(attrnamelen);
397 ibuf += sizeof(attrnamelen);
398 if (attrnamelen > 255)
401 /* we must copy the name as its not 0-terminated and we cant write to ibuf */
402 strncpy(attrmname, ibuf, attrnamelen);
403 attrmname[attrnamelen] = 0;
406 /* Convert EA name in utf8 to unix charset */
407 if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 256)) )
411 memcpy(&attrsize, ibuf, sizeof(attrsize));
412 attrsize = ntohl(attrsize);
413 ibuf += sizeof(attrsize);
414 if (attrsize > MAX_EA_SIZE)
415 /* we arbitrarily make this fatal */
418 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, attrmname, attrsize);
420 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
425 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
427 int oflag = O_RDONLY, ret;
428 uint16_t vid, bitmap, attrnamelen;
430 char attrmname[256], attruname[256];
438 memcpy( &vid, ibuf, sizeof(vid));
440 if (NULL == ( vol = getvolbyvid( vid )) ) {
441 LOG(log_error, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
442 return AFPERR_ACCESS;
445 memcpy( &did, ibuf, sizeof(did));
447 if (NULL == ( dir = dirlookup( vol, did )) ) {
448 LOG(log_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
452 memcpy( &bitmap, ibuf, sizeof(bitmap));
453 bitmap = ntohs( bitmap );
454 ibuf += sizeof(bitmap);
456 #ifdef HAVE_SOLARIS_EAS
457 if (bitmap & kXAttrNoFollow)
458 oflag |= AT_SYMLINK_NOFOLLOW;
462 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
463 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
467 if ((unsigned long)ibuf & 1)
470 /* get length of EA name */
471 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
472 attrnamelen = ntohs(attrnamelen);
473 ibuf += sizeof(attrnamelen);
474 if (attrnamelen > 255)
477 /* we must copy the name as its not 0-terminated and we cant write to ibuf */
478 strncpy(attrmname, ibuf, attrnamelen);
479 attrmname[attrnamelen] = 0;
482 /* Convert EA name in utf8 to unix charset */
483 if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 256)) )
486 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, attrmname);
488 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);