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;
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 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 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
154 if (ad_metadata(uname, adflags, &ad) != 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));
167 FinderInfo = ad_entry(&ad, ADEID_FINDERI);
169 if ((adflags & ADFLAGS_DIR)) {
170 /* set default view */
171 uint16 = htons(FINDERINFO_CLOSEDVIEW);
172 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2);
175 /* Check if FinderInfo equals default and empty FinderInfo*/
176 if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) {
177 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
178 strcpy(attrnamebuf, ea_finderinfo);
179 attrbuflen += strlen(ea_finderinfo) + 1;
180 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
183 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
184 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %llu", uname, ad.ad_rlen);
186 if (ad.ad_rlen > 0) {
187 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
188 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
189 attrbuflen += strlen(ea_resourcefork) + 1;
193 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag);
197 /* its a symlink and client requested O_NOFOLLOW */
198 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
211 /* Start building reply packet */
212 bitmap = htons(bitmap);
213 memcpy( rbuf, &bitmap, sizeof(bitmap));
214 rbuf += sizeof(bitmap);
215 *rbuflen += sizeof(bitmap);
217 tmpattr = htonl(attrbuflen);
218 memcpy( rbuf, &tmpattr, sizeof(tmpattr));
219 rbuf += sizeof(tmpattr);
220 *rbuflen += sizeof(tmpattr);
222 /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
223 and we didnt have an error (buf_valid) */
224 if (maxreply && buf_valid) {
225 memcpy( rbuf, attrnamebuf, attrbuflen);
226 *rbuflen += attrbuflen;
236 ad_close_metadata(&ad);
241 static char *to_stringz(char *ibuf, uint16_t len)
243 static char attrmname[256];
246 /* dont fool with us */
249 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
250 strlcpy(attrmname, ibuf, len + 1);
254 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
257 uint16_t vid, bitmap, attrnamelen;
258 uint32_t did, maxreply;
268 memcpy( &vid, ibuf, sizeof(vid));
270 if (NULL == ( vol = getvolbyvid( vid )) ) {
271 LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
272 return AFPERR_ACCESS;
275 memcpy( &did, ibuf, sizeof(did));
277 if (NULL == ( dir = dirlookup( vol, did )) ) {
278 LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
282 memcpy( &bitmap, ibuf, sizeof(bitmap));
283 bitmap = ntohs( bitmap );
284 ibuf += sizeof(bitmap);
286 if (bitmap & kXAttrNoFollow)
289 /* Skip Offset and ReqCount */
293 memcpy(&maxreply, ibuf, sizeof(maxreply));
294 maxreply = ntohl(maxreply);
295 ibuf += sizeof(maxreply);
298 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
299 LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
303 if ((unsigned long)ibuf & 1)
306 /* get length of EA name */
307 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
308 attrnamelen = ntohs(attrnamelen);
309 ibuf += sizeof(attrnamelen);
311 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
313 /* Convert EA name in utf8 to unix charset */
314 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
317 /* write bitmap now */
318 bitmap = htons(bitmap);
319 memcpy(rbuf, &bitmap, sizeof(bitmap));
320 rbuf += sizeof(bitmap);
321 *rbuflen += sizeof(bitmap);
325 if its 0 we must return the size of the requested attribute,
326 if its non 0 we must return the attribute.
329 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
331 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
336 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
339 uint16_t vid, bitmap, attrnamelen;
340 uint32_t did, attrsize;
350 memcpy( &vid, ibuf, sizeof(vid));
352 if (NULL == ( vol = getvolbyvid( vid )) ) {
353 LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
354 return AFPERR_ACCESS;
357 memcpy( &did, ibuf, sizeof(did));
359 if (NULL == ( dir = dirlookup( vol, did )) ) {
360 LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
364 memcpy( &bitmap, ibuf, sizeof(bitmap));
365 bitmap = ntohs( bitmap );
366 ibuf += sizeof(bitmap);
368 if (bitmap & kXAttrNoFollow)
371 if (bitmap & kXAttrCreate)
373 else if (bitmap & kXAttrReplace)
380 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
381 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
385 if ((unsigned long)ibuf & 1)
388 /* get length of EA name */
389 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
390 attrnamelen = ntohs(attrnamelen);
391 ibuf += sizeof(attrnamelen);
392 if (attrnamelen > 255)
396 /* Convert EA name in utf8 to unix charset */
397 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
402 memcpy(&attrsize, ibuf, sizeof(attrsize));
403 attrsize = ntohl(attrsize);
404 ibuf += sizeof(attrsize);
405 if (attrsize > MAX_EA_SIZE)
406 /* we arbitrarily make this fatal */
409 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize);
411 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
416 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
419 uint16_t vid, bitmap, attrnamelen;
429 memcpy( &vid, ibuf, sizeof(vid));
431 if (NULL == ( vol = getvolbyvid( vid )) ) {
432 LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
433 return AFPERR_ACCESS;
436 memcpy( &did, ibuf, sizeof(did));
438 if (NULL == ( dir = dirlookup( vol, did )) ) {
439 LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
443 memcpy( &bitmap, ibuf, sizeof(bitmap));
444 bitmap = ntohs( bitmap );
445 ibuf += sizeof(bitmap);
447 if (bitmap & kXAttrNoFollow)
451 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
452 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
456 if ((unsigned long)ibuf & 1)
459 /* get length of EA name */
460 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
461 attrnamelen = ntohs(attrnamelen);
462 ibuf += sizeof(attrnamelen);
463 if (attrnamelen > 255)
466 /* Convert EA name in utf8 to unix charset */
467 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
470 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
472 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag);