2 $Id: extattrs.c,v 1.24 2009-11-13 13:27:12 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, adflags, adp) < 0 ) {
156 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
157 uname, strerror(errno));
158 return AFPERR_ACCESS;
160 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
165 FinderInfo = ad_entry(adp, ADEID_FINDERI);
168 LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
169 hexdump( FinderInfo, 32);
172 if ((adflags & ADFLAGS_DIR)) {
173 /* set default view */
174 uint16 = htons(FINDERINFO_CLOSEDVIEW);
175 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
178 /* Check if FinderInfo equals default and empty FinderInfo*/
179 if ((memcmp(FinderInfo, emptyFinderInfo, 32)) != 0) {
180 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
181 strcpy(attrnamebuf, ea_finderinfo);
182 attrbuflen += strlen(ea_finderinfo) + 1;
183 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
186 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
187 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
188 if (adp->ad_eid[ADEID_RFORK].ade_len > 0) {
189 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
190 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
191 attrbuflen += strlen(ea_resourcefork) + 1;
194 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
198 /* its a symlink and client requested O_NOFOLLOW */
199 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
212 /* Start building reply packet */
213 bitmap = htons(bitmap);
214 memcpy( rbuf, &bitmap, sizeof(bitmap));
215 rbuf += sizeof(bitmap);
216 *rbuflen += sizeof(bitmap);
218 tmpattr = htonl(attrbuflen);
219 memcpy( rbuf, &tmpattr, sizeof(tmpattr));
220 rbuf += sizeof(tmpattr);
221 *rbuflen += sizeof(tmpattr);
223 /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
224 and we didnt have an error (buf_valid) */
225 if (maxreply && buf_valid) {
226 memcpy( rbuf, attrnamebuf, attrbuflen);
227 *rbuflen += attrbuflen;
237 ad_close_metadata( adp);
242 static char *to_stringz(char *ibuf, uint16_t len)
244 static char attrmname[256];
247 /* dont fool with us */
250 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
251 strlcpy(attrmname, ibuf, len);
255 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
258 uint16_t vid, bitmap, attrnamelen;
259 uint32_t did, maxreply;
269 memcpy( &vid, ibuf, sizeof(vid));
271 if (NULL == ( vol = getvolbyvid( vid )) ) {
272 LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
273 return AFPERR_ACCESS;
276 memcpy( &did, ibuf, sizeof(did));
278 if (NULL == ( dir = dirlookup( vol, did )) ) {
279 LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
283 memcpy( &bitmap, ibuf, sizeof(bitmap));
284 bitmap = ntohs( bitmap );
285 ibuf += sizeof(bitmap);
287 if (bitmap & kXAttrNoFollow)
290 /* Skip Offset and ReqCount */
294 memcpy(&maxreply, ibuf, sizeof(maxreply));
295 maxreply = ntohl(maxreply);
296 ibuf += sizeof(maxreply);
299 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
300 LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
304 if ((unsigned long)ibuf & 1)
307 /* get length of EA name */
308 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
309 attrnamelen = ntohs(attrnamelen);
310 ibuf += sizeof(attrnamelen);
312 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
314 /* Convert EA name in utf8 to unix charset */
315 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
318 /* write bitmap now */
319 bitmap = htons(bitmap);
320 memcpy(rbuf, &bitmap, sizeof(bitmap));
321 rbuf += sizeof(bitmap);
322 *rbuflen += sizeof(bitmap);
326 if its 0 we must return the size of the requested attribute,
327 if its non 0 we must return the attribute.
330 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
332 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
337 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
340 uint16_t vid, bitmap, attrnamelen;
341 uint32_t did, attrsize;
351 memcpy( &vid, ibuf, sizeof(vid));
353 if (NULL == ( vol = getvolbyvid( vid )) ) {
354 LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
355 return AFPERR_ACCESS;
358 memcpy( &did, ibuf, sizeof(did));
360 if (NULL == ( dir = dirlookup( vol, did )) ) {
361 LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
365 memcpy( &bitmap, ibuf, sizeof(bitmap));
366 bitmap = ntohs( bitmap );
367 ibuf += sizeof(bitmap);
369 if (bitmap & kXAttrNoFollow)
372 if (bitmap & kXAttrCreate)
374 else if (bitmap & kXAttrReplace)
381 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
382 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
386 if ((unsigned long)ibuf & 1)
389 /* get length of EA name */
390 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
391 attrnamelen = ntohs(attrnamelen);
392 ibuf += sizeof(attrnamelen);
393 if (attrnamelen > 255)
397 /* Convert EA name in utf8 to unix charset */
398 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
403 memcpy(&attrsize, ibuf, sizeof(attrsize));
404 attrsize = ntohl(attrsize);
405 ibuf += sizeof(attrsize);
406 if (attrsize > MAX_EA_SIZE)
407 /* we arbitrarily make this fatal */
410 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize);
412 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
417 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
420 uint16_t vid, bitmap, attrnamelen;
430 memcpy( &vid, ibuf, sizeof(vid));
432 if (NULL == ( vol = getvolbyvid( vid )) ) {
433 LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
434 return AFPERR_ACCESS;
437 memcpy( &did, ibuf, sizeof(did));
439 if (NULL == ( dir = dirlookup( vol, did )) ) {
440 LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
444 memcpy( &bitmap, ibuf, sizeof(bitmap));
445 bitmap = ntohs( bitmap );
446 ibuf += sizeof(bitmap);
448 if (bitmap & kXAttrNoFollow)
452 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
453 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
457 if ((unsigned long)ibuf & 1)
460 /* get length of EA name */
461 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
462 attrnamelen = ntohs(attrnamelen);
463 ibuf += sizeof(attrnamelen);
464 if (attrnamelen > 255)
467 /* Convert EA name in utf8 to unix charset */
468 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
471 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
473 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);