2 $Id: extattrs.c,v 1.28 2009-11-27 12:37:24 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;
84 char *uname, *FinderInfo;
85 char emptyFinderInfo[32] = { 0 };
87 static int buf_valid = 0;
88 static size_t attrbuflen = 0;
93 /* Get Bitmap and MaxReplySize first */
94 memcpy( &bitmap, ibuf +6, sizeof(bitmap));
95 bitmap = ntohs( bitmap );
97 memcpy( &maxreply, ibuf + 14, sizeof (maxreply));
98 maxreply = ntohl( maxreply );
101 If its the first request with maxreply=0 or if we didn't mark our buffers valid for
102 whatever reason (just a safety check, it should be valid), then scan for attributes
104 if ((maxreply == 0) || (buf_valid == 0)) {
108 memcpy( &vid, ibuf, sizeof(vid));
110 if (NULL == ( vol = getvolbyvid( vid )) ) {
111 LOG(log_debug, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
112 return AFPERR_ACCESS;
115 memcpy( &did, ibuf, sizeof(did));
117 if (NULL == ( dir = dirlookup( vol, did )) ) {
118 LOG(log_debug, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
122 if (bitmap & kXAttrNoFollow)
124 /* Skip Bitmap, ReqCount, StartIndex and maxreply*/
128 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
129 LOG(log_debug, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
134 if (!s_path->st_valid) {
135 /* it's a dir in our cache, we didn't stat it, do it now */
136 of_statdir(vol, s_path);
138 if ( s_path->st_errno != 0 ) {
139 return( AFPERR_NOOBJ );
142 adp = of_ad(vol, s_path, &ad);
143 uname = s_path->u_name; /*
144 We have to check the FinderInfo for the file, because if they aren't all 0
145 we must return the synthetic attribute "com.apple.FinderInfo".
146 Note: the client will never (never seen in traces) request that attribute
150 if (S_ISDIR(st->st_mode))
151 adflags = ADFLAGS_DIR;
153 if ( ad_metadata( uname, vol_noadouble(vol) | adflags, adp) < 0 ) {
159 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
160 uname, strerror(errno));
161 return AFPERR_ACCESS;
163 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
169 FinderInfo = ad_entry(adp, ADEID_FINDERI);
172 LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
173 hexdump( FinderInfo, 32);
176 if ((adflags & ADFLAGS_DIR)) {
177 /* set default view */
178 uint16 = htons(FINDERINFO_CLOSEDVIEW);
179 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
182 /* Check if FinderInfo equals default and empty FinderInfo*/
183 if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) {
184 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
185 strcpy(attrnamebuf, ea_finderinfo);
186 attrbuflen += strlen(ea_finderinfo) + 1;
187 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
190 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
191 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %llu", uname, adp->ad_rlen);
193 if (adp->ad_rlen > 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;
200 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
204 /* its a symlink and client requested O_NOFOLLOW */
205 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
218 /* Start building reply packet */
219 bitmap = htons(bitmap);
220 memcpy( rbuf, &bitmap, sizeof(bitmap));
221 rbuf += sizeof(bitmap);
222 *rbuflen += sizeof(bitmap);
224 tmpattr = htonl(attrbuflen);
225 memcpy( rbuf, &tmpattr, sizeof(tmpattr));
226 rbuf += sizeof(tmpattr);
227 *rbuflen += sizeof(tmpattr);
229 /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
230 and we didnt have an error (buf_valid) */
231 if (maxreply && buf_valid) {
232 memcpy( rbuf, attrnamebuf, attrbuflen);
233 *rbuflen += attrbuflen;
243 ad_close_metadata( adp);
248 static char *to_stringz(char *ibuf, uint16_t len)
250 static char attrmname[256];
253 /* dont fool with us */
256 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
257 strlcpy(attrmname, ibuf, len + 1);
261 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
264 uint16_t vid, bitmap, attrnamelen;
265 uint32_t did, maxreply;
275 memcpy( &vid, ibuf, sizeof(vid));
277 if (NULL == ( vol = getvolbyvid( vid )) ) {
278 LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
279 return AFPERR_ACCESS;
282 memcpy( &did, ibuf, sizeof(did));
284 if (NULL == ( dir = dirlookup( vol, did )) ) {
285 LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
289 memcpy( &bitmap, ibuf, sizeof(bitmap));
290 bitmap = ntohs( bitmap );
291 ibuf += sizeof(bitmap);
293 if (bitmap & kXAttrNoFollow)
296 /* Skip Offset and ReqCount */
300 memcpy(&maxreply, ibuf, sizeof(maxreply));
301 maxreply = ntohl(maxreply);
302 ibuf += sizeof(maxreply);
305 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
306 LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
310 if ((unsigned long)ibuf & 1)
313 /* get length of EA name */
314 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
315 attrnamelen = ntohs(attrnamelen);
316 ibuf += sizeof(attrnamelen);
318 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
320 /* Convert EA name in utf8 to unix charset */
321 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
324 /* write bitmap now */
325 bitmap = htons(bitmap);
326 memcpy(rbuf, &bitmap, sizeof(bitmap));
327 rbuf += sizeof(bitmap);
328 *rbuflen += sizeof(bitmap);
332 if its 0 we must return the size of the requested attribute,
333 if its non 0 we must return the attribute.
336 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
338 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
343 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
346 uint16_t vid, bitmap, attrnamelen;
347 uint32_t did, attrsize;
357 memcpy( &vid, ibuf, sizeof(vid));
359 if (NULL == ( vol = getvolbyvid( vid )) ) {
360 LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
361 return AFPERR_ACCESS;
364 memcpy( &did, ibuf, sizeof(did));
366 if (NULL == ( dir = dirlookup( vol, did )) ) {
367 LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
371 memcpy( &bitmap, ibuf, sizeof(bitmap));
372 bitmap = ntohs( bitmap );
373 ibuf += sizeof(bitmap);
375 if (bitmap & kXAttrNoFollow)
378 if (bitmap & kXAttrCreate)
380 else if (bitmap & kXAttrReplace)
387 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
388 LOG(log_debug, 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, sizeof(attrnamelen));
397 attrnamelen = ntohs(attrnamelen);
398 ibuf += sizeof(attrnamelen);
399 if (attrnamelen > 255)
403 /* Convert EA name in utf8 to unix charset */
404 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
409 memcpy(&attrsize, ibuf, sizeof(attrsize));
410 attrsize = ntohl(attrsize);
411 ibuf += sizeof(attrsize);
412 if (attrsize > MAX_EA_SIZE)
413 /* we arbitrarily make this fatal */
416 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize);
418 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
423 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
426 uint16_t vid, bitmap, attrnamelen;
436 memcpy( &vid, ibuf, sizeof(vid));
438 if (NULL == ( vol = getvolbyvid( vid )) ) {
439 LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
440 return AFPERR_ACCESS;
443 memcpy( &did, ibuf, sizeof(did));
445 if (NULL == ( dir = dirlookup( vol, did )) ) {
446 LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
450 memcpy( &bitmap, ibuf, sizeof(bitmap));
451 bitmap = ntohs( bitmap );
452 ibuf += sizeof(bitmap);
454 if (bitmap & kXAttrNoFollow)
458 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
459 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
463 if ((unsigned long)ibuf & 1)
466 /* get length of EA name */
467 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
468 attrnamelen = ntohs(attrnamelen);
469 ibuf += sizeof(attrnamelen);
470 if (attrnamelen > 255)
473 /* Convert EA name in utf8 to unix charset */
474 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
477 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
479 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);