2 $Id: extattrs.c,v 1.22 2009-11-09 05:45:06 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_error, 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_error, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
122 #ifdef HAVE_SOLARIS_EAS
123 if (bitmap & kXAttrNoFollow)
126 /* Skip Bitmap, ReqCount, StartIndex and maxreply*/
130 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
131 LOG(log_error, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
136 if (!s_path->st_valid) {
137 /* it's a dir in our cache, we didn't stat it, do it now */
138 of_statdir(vol, s_path);
140 if ( s_path->st_errno != 0 ) {
141 return( AFPERR_NOOBJ );
144 adp = of_ad(vol, s_path, &ad);
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
152 if (S_ISDIR(st->st_mode))
153 adflags = ADFLAGS_DIR;
155 if ( ad_metadata( uname, adflags, adp) < 0 ) {
158 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
159 uname, strerror(errno));
160 return AFPERR_ACCESS;
162 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
167 FinderInfo = ad_entry(adp, ADEID_FINDERI);
170 LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
171 hexdump( FinderInfo, 32);
174 if ((adflags & ADFLAGS_DIR)) {
175 /* set default view */
176 uint16 = htons(FINDERINFO_CLOSEDVIEW);
177 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
180 /* Check if FinderInfo equals default and empty FinderInfo*/
181 if ((memcmp(FinderInfo, emptyFinderInfo, 32)) != 0) {
182 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
183 strcpy(attrnamebuf, ea_finderinfo);
184 attrbuflen += strlen(ea_finderinfo) + 1;
185 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
188 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
189 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
190 if (adp->ad_eid[ADEID_RFORK].ade_len > 0) {
191 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
192 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
193 attrbuflen += strlen(ea_resourcefork) + 1;
196 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
200 /* its a symlink and client requested O_NOFOLLOW */
201 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
214 /* Start building reply packet */
215 bitmap = htons(bitmap);
216 memcpy( rbuf, &bitmap, sizeof(bitmap));
217 rbuf += sizeof(bitmap);
218 *rbuflen += sizeof(bitmap);
220 tmpattr = htonl(attrbuflen);
221 memcpy( rbuf, &tmpattr, sizeof(tmpattr));
222 rbuf += sizeof(tmpattr);
223 *rbuflen += sizeof(tmpattr);
225 /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
226 and we didnt have an error (buf_valid) */
227 if (maxreply && buf_valid) {
228 memcpy( rbuf, attrnamebuf, attrbuflen);
229 *rbuflen += attrbuflen;
239 ad_close_metadata( adp);
244 static char *to_stringz(char *ibuf, uint16_t len)
246 static char attrmname[256];
249 /* dont fool with us */
252 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
253 strlcpy(attrmname, ibuf, len);
257 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
260 uint16_t vid, bitmap, attrnamelen;
261 uint32_t did, maxreply;
271 memcpy( &vid, ibuf, sizeof(vid));
273 if (NULL == ( vol = getvolbyvid( vid )) ) {
274 LOG(log_error, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
275 return AFPERR_ACCESS;
278 memcpy( &did, ibuf, sizeof(did));
280 if (NULL == ( dir = dirlookup( vol, did )) ) {
281 LOG(log_error, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
285 memcpy( &bitmap, ibuf, sizeof(bitmap));
286 bitmap = ntohs( bitmap );
287 ibuf += sizeof(bitmap);
289 #ifdef HAVE_SOLARIS_EAS
290 if (bitmap & kXAttrNoFollow)
294 /* Skip Offset and ReqCount */
298 memcpy(&maxreply, ibuf, sizeof(maxreply));
299 maxreply = ntohl(maxreply);
300 ibuf += sizeof(maxreply);
303 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
304 LOG(log_error, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
308 if ((unsigned long)ibuf & 1)
311 /* get length of EA name */
312 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
313 attrnamelen = ntohs(attrnamelen);
314 ibuf += sizeof(attrnamelen);
316 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
318 /* Convert EA name in utf8 to unix charset */
319 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, 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;
355 memcpy( &vid, ibuf, sizeof(vid));
357 if (NULL == ( vol = getvolbyvid( vid )) ) {
358 LOG(log_error, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
359 return AFPERR_ACCESS;
362 memcpy( &did, ibuf, sizeof(did));
364 if (NULL == ( dir = dirlookup( vol, did )) ) {
365 LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
369 memcpy( &bitmap, ibuf, sizeof(bitmap));
370 bitmap = ntohs( bitmap );
371 ibuf += sizeof(bitmap);
373 #ifdef HAVE_SOLARIS_EAS
374 if (bitmap & kXAttrNoFollow)
375 oflag |= AT_SYMLINK_NOFOLLOW;
378 if (bitmap & kXAttrCreate)
380 else if (bitmap & kXAttrReplace)
387 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
388 LOG(log_error, 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)
425 int oflag = O_RDONLY, ret;
426 uint16_t vid, bitmap, attrnamelen;
436 memcpy( &vid, ibuf, sizeof(vid));
438 if (NULL == ( vol = getvolbyvid( vid )) ) {
439 LOG(log_error, 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_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
450 memcpy( &bitmap, ibuf, sizeof(bitmap));
451 bitmap = ntohs( bitmap );
452 ibuf += sizeof(bitmap);
454 #ifdef HAVE_SOLARIS_EAS
455 if (bitmap & kXAttrNoFollow)
456 oflag |= AT_SYMLINK_NOFOLLOW;
460 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
461 LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
465 if ((unsigned long)ibuf & 1)
468 /* get length of EA name */
469 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
470 attrnamelen = ntohs(attrnamelen);
471 ibuf += sizeof(attrnamelen);
472 if (attrnamelen > 255)
475 /* Convert EA name in utf8 to unix charset */
476 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
479 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
481 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);