2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
17 #endif /* HAVE_CONFIG_H */
24 #include <atalk/adouble.h>
25 #include <atalk/util.h>
26 #include <atalk/vfs.h>
27 #include <atalk/afp.h>
28 #include <atalk/logger.h>
30 #include <atalk/globals.h>
34 #include "directory.h"
38 static const char *ea_finderinfo = "com.apple.FinderInfo";
39 static const char *ea_resourcefork = "com.apple.ResourceFork";
41 /* This should be big enough to consecutively store the names of all attributes */
42 static char attrnamebuf[ATTRNAMEBUFSIZ];
45 static void hexdump(void *m, size_t l) {
52 len = sprintf(bufp, "%02x ", *p++);
56 if ((count % 16) == 0) {
57 LOG(log_debug9, logtype_afpd, "%s", buf);
64 /***************************************
66 ****************************************/
69 Note: we're being called twice. Firstly the client only want the size of all
70 EA names, secondly it wants these names. In order to avoid scanning EAs twice
71 we cache them in a static buffer.
73 int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
75 int ret, oflag = 0, adflags = 0;
76 uint16_t vid, bitmap, uint16;
77 uint32_t did, maxreply, tmpattr;
82 struct adouble ad, *adp = NULL;
83 char *uname, *FinderInfo;
84 char emptyFinderInfo[32] = { 0 };
86 static int buf_valid = 0;
87 static size_t attrbuflen = 0;
92 /* Get Bitmap and MaxReplySize first */
93 memcpy( &bitmap, ibuf +6, sizeof(bitmap));
94 bitmap = ntohs( bitmap );
96 memcpy( &maxreply, ibuf + 14, sizeof (maxreply));
97 maxreply = ntohl( maxreply );
100 If its the first request with maxreply=0 or if we didn't mark our buffers valid for
101 whatever reason (just a safety check, it should be valid), then scan for attributes
103 if ((maxreply == 0) || (buf_valid == 0)) {
107 memcpy( &vid, ibuf, sizeof(vid));
109 if (NULL == ( vol = getvolbyvid( vid )) ) {
110 LOG(log_debug, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
111 return AFPERR_ACCESS;
114 memcpy( &did, ibuf, sizeof(did));
116 if (NULL == ( dir = dirlookup( vol, did )) ) {
117 LOG(log_debug, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
121 if (bitmap & kXAttrNoFollow)
123 /* Skip Bitmap, ReqCount, StartIndex and maxreply*/
127 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
128 LOG(log_debug, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
133 if (!s_path->st_valid) {
134 /* it's a dir in our cache, we didn't stat it, do it now */
135 of_statdir(vol, s_path);
137 if ( s_path->st_errno != 0 ) {
138 return( AFPERR_NOOBJ );
141 uname = s_path->u_name;
143 We have to check the FinderInfo for the file, because if they aren't all 0
144 we must return the synthetic attribute "com.apple.FinderInfo".
145 Note: the client will never (never seen in traces) request that attribute
149 if (S_ISDIR(st->st_mode))
150 adflags = ADFLAGS_DIR;
153 ad_init(adp, vol->v_adouble, vol->v_ad_options);
154 if (ad_metadata(uname, 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));
167 FinderInfo = ad_entry(adp, 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, adp->ad_rlen);
186 if (adp->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);
209 } /* if ((maxreply == 0) || (buf_valid == 0)) */
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;
237 ad_close(adp, ADFLAGS_HF);
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 + 1);
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);