2 $Id: extattrs.c,v 1.29 2010-01-05 12:06:33 franklahm 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;
145 We have to check the FinderInfo for the file, because if they aren't all 0
146 we must return the synthetic attribute "com.apple.FinderInfo".
147 Note: the client will never (never seen in traces) request that attribute
151 if (S_ISDIR(st->st_mode))
152 adflags = ADFLAGS_DIR;
154 if ( ad_metadata( uname, adflags, adp) < 0 ) {
160 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
161 uname, strerror(errno));
162 return AFPERR_ACCESS;
164 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
170 FinderInfo = ad_entry(adp, ADEID_FINDERI);
173 LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
174 hexdump( FinderInfo, 32);
177 if ((adflags & ADFLAGS_DIR)) {
178 /* set default view */
179 uint16 = htons(FINDERINFO_CLOSEDVIEW);
180 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
183 /* Check if FinderInfo equals default and empty FinderInfo*/
184 if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) {
185 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
186 strcpy(attrnamebuf, ea_finderinfo);
187 attrbuflen += strlen(ea_finderinfo) + 1;
188 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
191 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
192 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %llu", uname, adp->ad_rlen);
194 if (adp->ad_rlen > 0) {
195 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
196 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
197 attrbuflen += strlen(ea_resourcefork) + 1;
201 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
205 /* its a symlink and client requested O_NOFOLLOW */
206 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
219 /* Start building reply packet */
220 bitmap = htons(bitmap);
221 memcpy( rbuf, &bitmap, sizeof(bitmap));
222 rbuf += sizeof(bitmap);
223 *rbuflen += sizeof(bitmap);
225 tmpattr = htonl(attrbuflen);
226 memcpy( rbuf, &tmpattr, sizeof(tmpattr));
227 rbuf += sizeof(tmpattr);
228 *rbuflen += sizeof(tmpattr);
230 /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
231 and we didnt have an error (buf_valid) */
232 if (maxreply && buf_valid) {
233 memcpy( rbuf, attrnamebuf, attrbuflen);
234 *rbuflen += attrbuflen;
244 ad_close_metadata( adp);
249 static char *to_stringz(char *ibuf, uint16_t len)
251 static char attrmname[256];
254 /* dont fool with us */
257 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
258 strlcpy(attrmname, ibuf, len + 1);
262 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
265 uint16_t vid, bitmap, attrnamelen;
266 uint32_t did, maxreply;
276 memcpy( &vid, ibuf, sizeof(vid));
278 if (NULL == ( vol = getvolbyvid( vid )) ) {
279 LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
280 return AFPERR_ACCESS;
283 memcpy( &did, ibuf, sizeof(did));
285 if (NULL == ( dir = dirlookup( vol, did )) ) {
286 LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
290 memcpy( &bitmap, ibuf, sizeof(bitmap));
291 bitmap = ntohs( bitmap );
292 ibuf += sizeof(bitmap);
294 if (bitmap & kXAttrNoFollow)
297 /* Skip Offset and ReqCount */
301 memcpy(&maxreply, ibuf, sizeof(maxreply));
302 maxreply = ntohl(maxreply);
303 ibuf += sizeof(maxreply);
306 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
307 LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
311 if ((unsigned long)ibuf & 1)
314 /* get length of EA name */
315 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
316 attrnamelen = ntohs(attrnamelen);
317 ibuf += sizeof(attrnamelen);
319 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
321 /* Convert EA name in utf8 to unix charset */
322 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
325 /* write bitmap now */
326 bitmap = htons(bitmap);
327 memcpy(rbuf, &bitmap, sizeof(bitmap));
328 rbuf += sizeof(bitmap);
329 *rbuflen += sizeof(bitmap);
333 if its 0 we must return the size of the requested attribute,
334 if its non 0 we must return the attribute.
337 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
339 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
344 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
347 uint16_t vid, bitmap, attrnamelen;
348 uint32_t did, attrsize;
358 memcpy( &vid, ibuf, sizeof(vid));
360 if (NULL == ( vol = getvolbyvid( vid )) ) {
361 LOG(log_debug, 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_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
372 memcpy( &bitmap, ibuf, sizeof(bitmap));
373 bitmap = ntohs( bitmap );
374 ibuf += sizeof(bitmap);
376 if (bitmap & kXAttrNoFollow)
379 if (bitmap & kXAttrCreate)
381 else if (bitmap & kXAttrReplace)
388 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
389 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
393 if ((unsigned long)ibuf & 1)
396 /* get length of EA name */
397 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
398 attrnamelen = ntohs(attrnamelen);
399 ibuf += sizeof(attrnamelen);
400 if (attrnamelen > 255)
404 /* Convert EA name in utf8 to unix charset */
405 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
410 memcpy(&attrsize, ibuf, sizeof(attrsize));
411 attrsize = ntohl(attrsize);
412 ibuf += sizeof(attrsize);
413 if (attrsize > MAX_EA_SIZE)
414 /* we arbitrarily make this fatal */
417 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize);
419 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
424 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
427 uint16_t vid, bitmap, attrnamelen;
437 memcpy( &vid, ibuf, sizeof(vid));
439 if (NULL == ( vol = getvolbyvid( vid )) ) {
440 LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
441 return AFPERR_ACCESS;
444 memcpy( &did, ibuf, sizeof(did));
446 if (NULL == ( dir = dirlookup( vol, did )) ) {
447 LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
451 memcpy( &bitmap, ibuf, sizeof(bitmap));
452 bitmap = ntohs( bitmap );
453 ibuf += sizeof(bitmap);
455 if (bitmap & kXAttrNoFollow)
459 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
460 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
464 if ((unsigned long)ibuf & 1)
467 /* get length of EA name */
468 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
469 attrnamelen = ntohs(attrnamelen);
470 ibuf += sizeof(attrnamelen);
471 if (attrnamelen > 255)
474 /* Convert EA name in utf8 to unix charset */
475 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
478 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
480 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);